HTTP 202 Accepted indicates that the request has been accepted for processing, but the processing has not been completed and may not have even started. It is intentionally non-committal — the server promises only that it received the request, not that it will succeed. This status is essential for asynchronous API patterns where operations take seconds to minutes (video transcoding, report generation, batch imports) and blocking the HTTP connection would be impractical.
Response includes the status code, standard headers (including Content-Type), and a small diagnostic JSON body describing the request and returned status.
Simulator URL (copy in the app after load — not a normal link):
https://httpstatus.com/api/status/202
Example request:
curl -i "https://httpstatus.com/api/status/202"The request has been accepted for processing, but processing is not complete. Often used for asynchronous operations or batch processing.
On this code, Inspector focuses on semantics, headers, and correctness warnings that commonly affect clients and caches.
202 Accepted (RFC 7231 Section 6.3.3) exists specifically because HTTP is synchronous but many operations are not. The response body SHOULD include a current status of the request and either a pointer to a status monitor (URL) or an estimated completion time. Common patterns: (1) Return 202 with a Location header pointing to a status endpoint (GET /jobs/123). (2) Return 202 with a body containing a job ID and poll URL. (3) Use 202 with Retry-After header to suggest when to check back. The client then polls the status endpoint, which returns 200 with progress updates until the job completes. Some APIs use webhooks as a callback alternative to polling. 202 responses are NOT cacheable.
// Async job pattern with Express
app.post('/api/reports', async (req, res) => {
const jobId = crypto.randomUUID();
await jobQueue.add({ id: jobId, params: req.body });
res.status(202)
.header('Retry-After', '10')
.json({
jobId,
statusUrl: `/api/jobs/${jobId}`,
message: 'Report generation started'
});
});
app.get('/api/jobs/:id', async (req, res) => {
const job = await jobQueue.getJob(req.params.id);
if (!job) return res.status(404).json({ error: 'Job not found' });
if (job.status === 'complete') {
return res.json({ status: 'complete', resultUrl: job.resultUrl });
}
res.header('Retry-After', '5').json({ status: job.status, progress: job.progress });
});from fastapi import FastAPI, BackgroundTasks
import uuid
jobs = {}
@app.post('/api/reports', status_code=202)
async def create_report(params: ReportParams,
bg: BackgroundTasks):
job_id = str(uuid.uuid4())
jobs[job_id] = {'status': 'queued', 'progress': 0}
bg.add_task(generate_report, job_id, params)
return {
'job_id': job_id,
'status_url': f'/api/jobs/{job_id}'
}
@app.get('/api/jobs/{job_id}')
async def get_job(job_id: str):
return jobs.get(job_id, {'status': 'not_found'})@PostMapping("/api/reports")
public ResponseEntity<JobStatus> createReport(
@RequestBody ReportParams params) {
String jobId = UUID.randomUUID().toString();
asyncService.generateReport(jobId, params);
return ResponseEntity.accepted()
.header("Retry-After", "10")
.header("Location", "/api/jobs/" + jobId)
.body(new JobStatus(jobId, "queued"));
}func createReportHandler(w http.ResponseWriter, r *http.Request) {
jobID := uuid.New().String()
go processReport(jobID, r.Body) // async
w.Header().Set("Location", "/api/jobs/"+jobID)
w.Header().Set("Retry-After", "10")
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusAccepted)
json.NewEncoder(w).Encode(map[string]string{
"jobId": jobID,
"statusUrl": "/api/jobs/" + jobID,
})
}