# 202 Accepted

> The request was accepted for processing but not completed yet. Learn when to use 202 for asynchronous operations.

Source: https://howhttpworks.com/status-codes/202
Last reviewed: 2026-10-05

> **TL;DR:** 202 Accepted means the server accepted your request, but processing is not finished. Follow the supplied job-status URL; if progress stalls, check the job ID in the queue and worker logs before submitting the request again.

## What is 202 Accepted?

A **202 Accepted** response acknowledges unfinished work: an export, import or notification has been accepted for processing. The original HTTP response ends here. The eventual result belongs in a separate status resource or notification; the server cannot later replace that 202 with a 200 on the same exchange. [RFC 9110 section 15.3.3](https://www.rfc-editor.org/rfc/rfc9110.html#section-15.3.3) recommends describing the current state and pointing to a status monitor.

A job can subsequently fail validation or fail during execution. Show “queued” or “processing” in the client until the job's own state reaches completion. A successful HTTP acceptance is the start of this workflow, not the completion screen.

The exchanges below are illustrative; job IDs, progress counts and timestamps describe an application contract rather than fields required by HTTP.

## What you see in your client

202 is in the success range. If your application calls it an “error 202,” inspect its success test and the endpoint's asynchronous contract.

- **Axios:** the default 200–299 predicate resolves with `response.status === 202`. A custom `validateStatus: status => status === 200` produces `AxiosError: Request failed with status code 202`. Check the [default predicate](https://github.com/axios/axios/blob/v1.x/lib/defaults/index.js) and [message construction](https://github.com/axios/axios/blob/v1.x/lib/core/settle.js).
- **fetch:** resolves with `response.ok === true`; it leaves job completion to your code. The [Fetch success range](https://fetch.spec.whatwg.org/#ok-status) includes 202.
- **Python requests:** `raise_for_status()` returns normally and `response.ok` is `True`. Its [source](https://requests.readthedocs.io/en/latest/_modules/requests/models/#Response.raise_for_status) reserves `HTTPError` for 4xx/5xx.
- **curl `-f`:** accepts 202. Use `curl -i` to read the job URL; [`-f`](https://curl.se/docs/manpage.html#-f) fails on HTTP codes of 400 or greater.
- **.NET:** [`EnsureSuccessStatusCode()`](https://github.com/dotnet/runtime/blob/main/src/libraries/System.Net.Http/src/System/Net/Http/HttpResponseMessage.cs) accepts 202 and returns the response.
- **Spring:** [`WebClient.retrieve()`](https://docs.spring.io/spring-framework/reference/web/webflux-webclient/client-retrieve.html) and [`RestTemplate`'s default handler](https://github.com/spring-projects/spring-framework/blob/main/spring-web/src/main/java/org/springframework/web/client/DefaultResponseErrorHandler.java) treat 202 as a normal response. Handle the job state separately from HTTP exceptions.

A `Location` header on 202 gives your application a URL to inspect. It is not a redirect status: the client needs to start a new request to that URL. Save the job identifier before closing the screen so a reload can resume polling the same operation.

## When Does This Happen?

You'll see a 202 Accepted response in these common situations:

**1. File Processing**

```text
POST /videos/upload with large video file
Server starts encoding in the background
```

**2. Batch Operations**

```text
POST /users/bulk-import with CSV file
Server queues processing of thousands of records
```

**3. Email Sending**

```text
POST /emails/send with newsletter to 10,000 subscribers
Server queues emails for gradual delivery
```

**4. Report Generation**

```text
POST /reports/sales with complex parameters
Server starts generating large PDF report
```

**5. Data Analysis**

```text
POST /analytics/process with large dataset
Server begins machine learning analysis
```

## Example Responses

**Video Upload Processing:**

```http
HTTP/1.1 202 Accepted
Content-Type: application/json
Location: /jobs/video-123

{
  "jobId": "video-123",
  "status": "processing",
  "message": "Video upload accepted for processing",
  "estimatedCompletion": "2026-01-18T12:15:00Z",
  "statusUrl": "/jobs/video-123/status"
}
```

**Bulk Data Import:**

```http
HTTP/1.1 202 Accepted
Content-Type: application/json

{
  "batchId": "import-456",
  "status": "queued",
  "totalRecords": 5000,
  "message": "Import job queued successfully",
  "checkStatusAt": "/imports/456/status"
}
```

**Email Campaign:**

```http
HTTP/1.1 202 Accepted
Content-Type: application/json

{
  "campaignId": "newsletter-789",
  "status": "sending",
  "recipients": 10000,
  "sent": 0,
  "progressUrl": "/campaigns/789/progress"
}
```

## Real-World Example

For a photo-resizing endpoint, accepting the upload and producing the resized images are separate steps:

**Request:**

```http
POST /photos HTTP/1.1
Host: api.example.com
Content-Type: application/octet-stream
Content-Length: 5

photo
```

**Response:**

```http
HTTP/1.1 202 Accepted
Content-Type: application/json
Location: /jobs/photo-resize-abc123

{
  "jobId": "photo-resize-abc123",
  "status": "processing",
  "originalSize": "4032x3024",
  "targetSizes": ["1920x1440", "800x600", "200x150"],
  "estimatedCompletion": "2026-01-18T12:05:00Z",
  "statusEndpoint": "/jobs/photo-resize-abc123"
}
```

## 202 vs Other Success Codes

| Code    | Meaning                    | When to Use                                    |
| ------- | -------------------------- | ---------------------------------------------- |
| **202** | Accepted, processing later | Long-running operations, async processing      |
| **200** | Success, completed now     | Immediate operations with results              |
| **201** | Created successfully       | Resource creation that completes immediately   |
| **204** | Success, no content        | Operations that complete with no response data |

## Status Checking Pattern

After receiving 202, clients typically check status:

**Initial Request:**

```text
POST /reports/generate
→ 202 Accepted with jobId
```

**Status Check:**

```text
GET /jobs/report-123
→ 200 OK with current status
```

**Completion Check:**

```text
GET /jobs/report-123
→ 303 See Other, Location: /reports/final-report.pdf
```

## Common Mistakes

**Using 202 for completed operations**

```text
POST /users/login  ← Fast operation
HTTP/1.1 202 Accepted  ← Should be 200 OK immediately
```

**Omitting status tracking**

```text
HTTP/1.1 202 Accepted
{"message": "Processing"}  ← No way to check progress!
```

**Keeping slow work on the request path**

A server can wait for completion and return 200, but that holds the connection open. When the API queues the work and replies before completion, use 202 and give the client a monitoring URL.

```text
POST /videos/process  ← Takes 10 minutes
HTTP/1.1 200 OK  ← Client waits 10 minutes for response
```

**Return a job URL**

```text
POST /videos/process
HTTP/1.1 202 Accepted
Location: /jobs/video-123

{
  "jobId": "video-123",
  "statusUrl": "/jobs/video-123"
}
```

## Common causes by stack

**Express:** `res.status(202).location('/jobs/123').json({ jobId: '123' })` emits 202 explicitly. Check the [response call](https://expressjs.com/en/5x/api/response/#res.status) and the enqueue result together: publish the job successfully before acknowledging it, and use a completed-operation code when the work already finished.

**FastAPI:** `@app.post('/exports', status_code=202)` selects acceptance; `background_tasks.add_task(...)` schedules work after the response. [BackgroundTasks](https://fastapi.tiangolo.com/tutorial/background-tasks/) runs application background work; adding a task alone does not select 202. Use the [path-operation status setting](https://fastapi.tiangolo.com/tutorial/response-status-code/) and record a status that the client can query.

**Spring MVC / Spring Boot:** `ResponseEntity.accepted().location(jobUri).body(job)` creates the response through the [accepted builder](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/ResponseEntity.html#accepted()). Trace the job identifier into your executor or queue logs when the request succeeds but the status never advances.

**ASP.NET Core:** `Accepted("/jobs/123", job)` creates a 202 with a monitoring location and response value. The [controller source](https://github.com/dotnet/aspnetcore/blob/main/src/Mvc/Mvc.Core/src/ControllerBase.cs) documents that URI as the location for monitoring requested content. Pair it with a GET action that reports the stored job state.

**AWS Lambda / API Gateway:** Lambda's [`InvocationType: Event`](https://docs.aws.amazon.com/lambda/latest/api/API_Invoke.html) returns 202 for asynchronous invocation. API Gateway's [non-proxy asynchronous Lambda integration](https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-integration-async.html) sets `X-Amz-Invocation-Type` to `'Event'`; inspect the method's response mapping to see what your caller receives. Check the function's invocation logs rather than expecting its return payload in the acceptance response.

## Handling a job that stays pending

Poll the status URL supplied by the API, with its documented delay, and stop when the body reports a terminal state such as `completed` or `failed`. Those names are application fields, so implement the actual values your API publishes. A `200 OK` from the status endpoint means the status lookup succeeded; its body can report that the background operation failed.

Keep a record linking the incoming request, job ID and queue message ID. If polling stays at `queued`, inspect whether a worker consumed that message. If it stays at `processing`, inspect that worker's last recorded step. Re-submit only after checking the original job: another POST can enqueue another export or email campaign. A client-side timeout on polling should preserve the job URL for a later check.

## Getting 202 Accepted right

**Provide Job Tracking:**

```http
HTTP/1.1 202 Accepted
Content-Type: application/json
Location: /jobs/unique-job-id

{
  "jobId": "unique-job-id",
  "statusUrl": "/jobs/unique-job-id",
  "estimatedDuration": "5 minutes"
}
```

**Include Progress Information:**

```text
GET /jobs/unique-job-id
HTTP/1.1 200 OK

{
  "status": "processing",
  "progress": 65,
  "message": "Processing item 650 of 1000"
}
```

**Handle Completion:**

```text
GET /jobs/unique-job-id
HTTP/1.1 303 See Other
Location: /results/final-output
```

## Try It Yourself

Use the [request builder](https://howhttpworks.com/tools/playground) to inspect response headers and bodies. For an asynchronous API you operate, send its documented POST request, record the returned job ID, then send a GET to its monitoring URL. Compare the HTTP status with the job state in the body; they answer different questions.

## Try it with curl

Start a job, then poll the URL the server gives you in `Location`.

```bash
curl -i -X POST https://api.example.com/exports \
  -H 'Content-Type: application/json' \
  -d '{"format":"pdf"}'

curl -i https://api.example.com/exports/8f3a
```

The response headers can include a polling interval:

```http
HTTP/2 202
location: /exports/8f3a
retry-after: 5
```

Read `retry-after: 5` as an instruction in this API to wait five seconds before polling. Document that convention for your clients; HTTP 202 itself sets no polling interval.

## Related Status Codes

- [200 OK](https://howhttpworks.com/status-codes/200) - Immediate successful completion
- [201 Created](https://howhttpworks.com/status-codes/201) - Resource created immediately
- [303 See Other](https://howhttpworks.com/status-codes/303) - Job completed, result available elsewhere
- [102 Processing](https://howhttpworks.com/status-codes/102) - Still working on the request
