How HTTP Works

Comparison

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.

Bottom line: Return 201 with a Location header when a request created a new resource, 200 when you are sending a representation back, and 204 when the action worked and there is nothing to send.

By How HTTP WorksReview process
200 OK
vs
204 No Content

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 OK201 Created204 No Content
MeaningSucceeded; the response describes the resultSucceeded; a new resource now existsSucceeded; no content follows
BodyExpected (meaning depends on the method)Optional, usually the new resourceForbidden: the response ends after the headers
Key headerContent-Type, Content-LengthLocation (the new resource URL; the spec says SHOULD)None of Content-Length or Transfer-Encoding
Typical methodsGET, POST (action), PUT/PATCH (with echo)POST create, PUT to a new URLDELETE, PUT/PATCH (no echo), OPTIONS preflight
Cacheable by defaultYes (defined as cacheable)NoYes (defined as cacheable)
Browser behaviorReplaces the page or resolves the fetchSame as 200 for fetchStays on the current page after a form submit or navigation

Which One Should I Use

Creating something: 201

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

{"name": "Docs site"}
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 and point at a status resource.

Reading or running an action: 200

GET /api/projects/prj_7f3k2 HTTP/1.1
Host: api.example.com
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

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

{"name": "Documentation"}
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 for the update semantics themselves.

Deleting: 204

DELETE /api/projects/prj_7f3k2 HTTP/1.1
Host: api.example.com
HTTP/1.1 204 No Content

A second DELETE on the same URL usually returns 404 or 410. That is fine: DELETE is idempotent in effect (the resource stays gone), not in status code.

Edge cases

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 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

Browse /search