Synchronous APIs vs Asynchronous APIs
The distinction between synchronous and asynchronous API design is one of the most consequential architectural decisions. Synchronous APIs work well for fast operations where the client can wait. Asynchronous APIs are required for operations that might time out, need to be queued, or where the result is not immediately available.
Synchronous APIs respond immediately – the client blocks until the result arrives. Asynchronous APIs accept a request, return immediately with a job ID or 202 Accepted, then deliver the result later via polling or webhook. Synchronous is simpler to implement and consume. Asynchronous is necessary when operations take longer than a few seconds or when decoupling producers from consumers.
| Feature | Synchronous APIs | Asynchronous APIs |
|---|---|---|
| Response timing | Immediate – client waits for result | Returns 202 immediately, result delivered later |
| Client complexity | Simple – one request, one response | Higher – must poll or handle webhook callbacks |
| Server complexity | Lower – simple request handler | Higher – job queue, state storage, retry logic |
| Timeout risk | High – long operations hit proxy/client timeouts | None – long work happens after the 202 is returned |
| Scalability | Server thread/connection held during processing | Workers process asynchronously – better resource utilization |
| Error handling | Error in response body – client handles immediately | Error delivered via polling or webhook – must be designed for |
| Idempotency | Simple – retry the same request | Complex – duplicate submissions need deduplication keys |
| Observability | Simple – one trace spans entire operation | Distributed – trace must follow job across worker boundary |
| HTTP status for submit | 200 OK with result | 202 Accepted with job ID and polling URL |
| Best for | Read operations, fast writes (<2s), CRUD | Long tasks, file processing, email sending, ML inference |
When to use Synchronous APIs
Use synchronous APIs when: operations complete in under 2–3 seconds, the client needs the result before it can continue, the operation is a read (GET) or a simple write, or simplicity matters more than scalability. Most REST CRUD operations are correctly synchronous.
When to use Asynchronous APIs
Use asynchronous APIs when: operations may take more than a few seconds (file export, report generation, video processing, bulk imports), the operation involves external services with unpredictable latency, or the system needs to handle traffic spikes without holding connections open. Always use async for email sending, PDF generation, and ML inference.
Common Mistakes
- Making long operations synchronous – a PDF export or report that takes 30 seconds will hit Cloudflare's 100-second proxy timeout, nginx's proxy_read_timeout, and client-side fetch timeouts. Make it async from day one.
- Not providing a status endpoint with the 202 – a 202 Accepted response must include a way to check job status. The Location header or a JSON body with polling URL is required. Without it, clients cannot determine when the job is done.
- Not handling async job failures – async jobs can fail silently if the worker crashes. Every async job system needs: a dead-letter queue, a failed state in the status endpoint, and alerting on stuck jobs.
- Using async for fast operations unnecessarily – if an operation reliably completes in under 500ms, making it async adds complexity with no benefit. Keep CRUD operations synchronous.
FAQ
What is the correct HTTP status code for an accepted async job?
202 Accepted – defined in RFC 9110 as 'the request has been accepted for processing, but the processing has not been completed.' The response should include either a Location header pointing to the job status endpoint, or a body with the job ID and polling URL. The status endpoint should return 200 with a status field (pending, processing, complete, failed).
How do I implement polling correctly?
Exponential backoff with jitter: start at 1 second, double each time, add random jitter, cap at 30–60 seconds. Stop polling after a maximum duration (e.g., 10 minutes). The status endpoint should be cheap to call (cached, no heavy computation). Return ETag or Last-Modified to enable 304 Not Modified responses for unchanged job state.