# 200 vs 201 vs 204: Which Success Code to Return

> Choose between 200 OK, 201 Created and 204 No Content for REST APIs: what each means, the Location and body rules, raw responses for POST, PUT, PATCH and DELETE.

Source: https://howhttpworks.com/compare/200-vs-201-vs-204
Last reviewed: 2026-10-04

> **TL;DR:** Return **201 Created** with a `Location` header when the request created a new resource. Return **200 OK** when the response carries a representation. Return **204 No Content** when the action succeeded and there is nothing to send back, which is the usual answer for DELETE and for updates where the client already knows the result.

## Side By Side

| | 200 OK | 201 Created | 204 No Content |
|---|---|---|---|
| Meaning | Succeeded; the response describes the result | Succeeded; a new resource now exists | Succeeded; no content follows |
| Body | Expected (meaning depends on the method) | Optional, usually the new resource | Forbidden: the response ends after the headers |
| Key header | `Content-Type`, `Content-Length` | `Location` (the new resource URL; the spec says SHOULD) | None of `Content-Length` or `Transfer-Encoding` |
| Typical methods | GET, POST (action), PUT/PATCH (with echo) | POST create, PUT to a new URL | DELETE, PUT/PATCH (no echo), OPTIONS preflight |
| Cacheable by default | Yes (defined as cacheable) | No | Yes (defined as cacheable) |
| Browser behavior | Replaces the page or resolves the fetch | Same as 200 for fetch | Stays on the current page after a form submit or navigation |

## Which One Should I Use

### Creating something: 201

```http
POST /api/projects HTTP/1.1
Host: api.example.com
Content-Type: application/json

{"name": "Docs site"}
```

```http
HTTP/1.1 201 Created
Location: https://api.example.com/api/projects/prj_7f3k2
Content-Type: application/json

{"id": "prj_7f3k2", "name": "Docs site", "createdAt": "2026-10-04T09:30:00Z"}
```

The `Location` header lets a client follow up without parsing your JSON schema. If the work is queued instead of finished, use [202 Accepted](https://howhttpworks.com/status-codes/202) and point at a status resource.

### Reading or running an action: 200

```http
GET /api/projects/prj_7f3k2 HTTP/1.1
Host: api.example.com
```

```http
HTTP/1.1 200 OK
Content-Type: application/json

{"id": "prj_7f3k2", "name": "Docs site"}
```

200 is also right for a POST that did not create anything: login, search with a body, a "convert this" call. For POST, RFC 9110 says the 200 content typically describes or contains the result of the action.

### Updating: 200 with the new state, or 204

```http
PATCH /api/projects/prj_7f3k2 HTTP/1.1
Host: api.example.com
Content-Type: application/merge-patch+json

{"name": "Documentation"}
```

```http
HTTP/1.1 200 OK
Content-Type: application/json

{"id": "prj_7f3k2", "name": "Documentation", "updatedAt": "2026-10-04T09:31:12Z"}
```

Prefer 200 with a body when the server computes anything the client cannot predict (`updatedAt`, normalized strings, a new `ETag`). Prefer 204 when the client sent the whole state and nothing changed server-side. See [PUT vs PATCH](https://howhttpworks.com/compare/put-vs-patch) for the update semantics themselves.

### Deleting: 204

```http
DELETE /api/projects/prj_7f3k2 HTTP/1.1
Host: api.example.com
```

```http
HTTP/1.1 204 No Content
```

A second DELETE on the same URL usually returns [404](https://howhttpworks.com/status-codes/404) or 410. That is fine: DELETE is idempotent in effect (the resource stays gone), not in status code.

### Edge cases

- **PUT to a URL that did not exist yet and you created it:** 201, with the body or `Location` as above. If it replaced something, 200 or 204.
- **Form POST from a browser that should not navigate away** (autosave, a tracking endpoint): 204 keeps the user on the current page.
- **CORS preflight:** `OPTIONS` answered with 204 and the `Access-Control-Allow-*` headers is the usual pattern, and browsers accept any 2xx.
- **Partial lists:** 206 for byte ranges, not 200, if the request had a `Range` header and you honor it.

## Common Mistakes

**A 200 for everything, with an error flag in the body.** Retries, monitoring and API gateways read the status line. See [400 vs 422](https://howhttpworks.com/compare/400-vs-422) for what to return instead.

**A body on a 204.** Frameworks that serialize `null` or `{}` into a 204 produce a response some proxies drop and some clients choke on. Return 200 with the JSON if you have something to say.

**A 201 without `Location`.** Clients then have to know your URL scheme to find what they created. It also breaks generic tooling that follows `Location`.

**A 200 on create because "it worked."** Clients that branch on `201` to show "created" toasts or to update a local cache never fire.

**Returning 204 to a `fetch()` call and then calling `.json()`.** The parse throws on empty content. Guard on status.

**Assuming 204 is uncacheable.** It is defined as cacheable by default, so a GET that returns 204 can be stored unless you send `Cache-Control: no-store`.

## FAQ

### Should a successful POST return 200 or 201?

Return 201 Created when the POST created a resource that now has its own URL, and include a Location header pointing at it (RFC 9110 section 15.3.2). Return 200 when the POST was an action or query that did not create an addressable resource, such as a search with a large body or a login.

### What should DELETE return: 200, 202 or 204?

RFC 9110 section 9.3.5 allows all three. 204 is the common choice when the resource is gone and there is nothing to report. 200 fits when you return a status message or the deleted representation. 202 is correct when the deletion has been accepted but will happen later.

### Can a 204 response have a body?

No. A 204 response ends at the header section and cannot contain content (RFC 9110 section 15.3.5), and it must not include Content-Length or Transfer-Encoding. Sending a JSON body with a 204 is a bug that clients and proxies handle inconsistently.

### Should PUT or PATCH return 200 or 204?

Either is valid. Return 200 with the updated representation if clients benefit from seeing server-computed fields such as updatedAt or a normalized value. Return 204 when the client already knows the result. If PUT created a resource that did not exist before, return 201 instead.

### Do I need a body with 201 Created?

The Location header is what the spec asks for; a body is optional. Most JSON APIs also return the created representation so the client gets the generated id and defaults without a second GET. Both are valid.

### Why does my fetch() call throw on a 204 when I call response.json()?

A 204 has no content, so JSON parsing an empty string fails with "Unexpected end of JSON input". Check response.status === 204 (or the Content-Length) before parsing, or only call json() when the status is 200 or 201.

## References

- [MDN Web Docs: 200 OK](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/200)
- [MDN Web Docs: 201 Created](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/201)
- [MDN Web Docs: 204 No Content](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/204)
- [RFC 9110: 200 OK (section 15.3.1)](https://www.rfc-editor.org/rfc/rfc9110#section-15.3.1)
- [RFC 9110: 201 Created (section 15.3.2)](https://www.rfc-editor.org/rfc/rfc9110#section-15.3.2)
- [RFC 9110: 204 No Content (section 15.3.5)](https://www.rfc-editor.org/rfc/rfc9110#section-15.3.5)
