Skip to main content

Cloudsoft Zone Build, deploy and scale with practical guides to PHP, Node.js, React, DevOps and AI automation. Explore the guides

Cloudsoft Zone
Resources

HTTP status codes cheat sheet for API developers (RFC 9110)

Which HTTP status codes a JSON API needs, when to use each, the headers that go with them and common mistakes, checked against RFC 9110 and the IANA registry.

Cloudsoft Zone

7 min read

Close-up of two stacked network switches with blue and grey Ethernet cables plugged into their numbered ports
Photograph: ShakataGaNai / CC BY-SA 3.0, via Wikimedia Commons
In this article

Status codes are the first thing every API client reads, and the first thing it gets wrong when they are inconsistent. A 200 with an error message inside, a 500 for a validation failure or a 403 where a 401 was meant all push work onto every developer who integrates with you.

This cheat sheet covers the status codes a typical REST or JSON API actually needs, with the rules from RFC 9110 (the current HTTP Semantics standard) and the IANA registry, both checked on 9 October 2026. Each entry says when to use the code, what headers go with it, and the common mistakes. Keep it open while you design endpoints or review a pull request.

A pedestrian traffic light in Vienna showing a green signal with two figures, with trees and buildings behind it
A pedestrian light in Vienna. Status codes are signals too: clients act on the colour before reading anything else.

The five classes in one table

ClassMeaningWho should act
1xxInformational: the request is in progressUsually handled by the HTTP library
2xxSuccessClient continues normally
3xxRedirection: look elsewhere or use a cached copyClient follows Location or uses its cache
4xxClient error: the request must change before retryingThe caller fixes the request
5xxServer error: the request may be fine; the server failedThe operator; the caller may retry later

The class matters more than the exact number. Clients, proxies and monitoring tools treat any unknown code as the x00 of its class, so a 4xx must always mean "the caller did something wrong" and a 5xx "we did".

2xx: success codes for APIs

CodeNameUse it when
200OKA read succeeded, or an update returns the updated resource
201CreatedA POST (or PUT) created a resource. RFC 9110 says the new resource is identified by the Location header, so send it.
202AcceptedWork was queued, not finished: imports, exports, video processing. Return a link to a status resource.
204No ContentSuccess with nothing to return, such as a DELETE or an update where the client already has the data. The response has no body.
206Partial ContentYou serve byte ranges, for example resumable downloads

3xx: redirects and caching

CodeNameMethod on redirectUse it for
301Moved PermanentlyMay change POST to GETPermanent page moves (SEO redirects)
302FoundMay change POST to GETTemporary redirects of GET requests
303See OtherAlways GETAfter a form POST, send the browser to a result page
304Not ModifiedNot a redirectConditional GET with ETag/If-None-Match: the cached copy is still valid
307Temporary RedirectMust not changeTemporary redirects of API calls
308Permanent RedirectMust not changePermanent moves of API endpoints

RFC 9110 keeps a historical note on 301 and 302: a user agent may change the request method from POST to GET when following them, and the standard points to 307 and 308 if that is undesired. For APIs, that settles it: use 307 or 308, because a client that silently turns your POST into a GET loses the request body.

A Raspberry Pi 4 in a black heatsink case sitting on a small TP-Link managed network switch, with yellow and grey Ethernet cables plugged in
A Raspberry Pi on a managed network switch. Even the smallest home-lab server answers every request with a status code, and a 502 or 504 usually points at whatever sits in front of it.

4xx: client errors you will actually use

Authentication and permissions

  • 401 Unauthorized means "not authenticated": no credentials, or invalid ones. RFC 9110 says a server generating a 401 MUST send a WWW-Authenticate header with at least one challenge, for example WWW-Authenticate: Bearer. MDN puts it plainly: semantically, 401 means "unauthenticated".
  • 403 Forbidden means the server knows who you are, but you may not do this. The client should not retry with the same credentials.
  • Hiding existence: RFC 9110 allows a server to answer 404 instead of 403 to hide that a forbidden resource exists. This is a good default for other tenants' records in multi-tenant APIs.

Requests that are wrong

CodeNameUse it when
400Bad RequestThe request is malformed: invalid JSON, wrong types, missing required parameters
404Not FoundNo such resource (or one you are deliberately hiding)
405Method Not AllowedThe resource exists but not for this method. RFC 9110: the server MUST send an Allow header listing supported methods.
406Not AcceptableYou cannot produce any format listed in the client's Accept header
409ConflictThe request conflicts with current state: a stale version, a duplicate unique key, an invalid state transition
410GoneThe resource existed and was removed on purpose, such as a retired API version
412Precondition FailedAn If-Match or If-Unmodified-Since condition failed (optimistic locking via headers)
413Content Too LargeAn upload or body exceeds your limit
415Unsupported Media TypeThe body is in a format you do not accept, such as XML sent to a JSON-only endpoint
422Unprocessable ContentThe body is well-formed but semantically invalid: validation errors
429Too Many RequestsRate limiting. RFC 6585 says the response may include Retry-After.

400 or 422 for validation errors?

Both are defensible. RFC 9110 defines 422 for content whose type and syntax are correct but whose instructions cannot be processed, which describes most validation failures. Laravel follows that convention: its documentation says a failed validation on a request expecting JSON returns a 422 response with the errors. Many other APIs use 400 for everything the caller got wrong. Pick one, document it, and apply it consistently. Whichever you choose, return field-level detail in the body; RFC 9457 Problem Details is a standard format for that.

Renamed codes

RFC 9110 renamed two familiar codes: 413 is now Content Too Large (formerly Payload Too Large) and 422 is Unprocessable Content (formerly Unprocessable Entity). The numbers did not change, and libraries lag behind: in Node.js 24.19.0, http.STATUS_CODES[422] still returns "Unprocessable Entity" and [413] returns "Payload Too Large". Clients should never depend on the reason phrase; HTTP/2 and HTTP/3 do not even send one.

5xx: server errors

CodeNameUse it when
500Internal Server ErrorAn unexpected failure in your code. Log it with a request ID; never return stack traces.
501Not ImplementedThe server does not support the method at all (rare in application code)
502Bad GatewayA proxy or gateway received an invalid response from upstream
503Service UnavailableTemporary overload or maintenance. RFC 9110: the server may send Retry-After.
504Gateway TimeoutA proxy did not get a response from upstream in time

Your application code usually returns only 500 and 503. 502 and 504 normally come from nginx, a load balancer or a CDN in front of you, which makes them a useful hint during an incident: the problem is between the proxy and your app.

Codes to avoid or treat with care

  • 418 is listed in the IANA registry as "(Unused)". It began as an April Fools' joke; do not use it in production APIs.
  • 402 Payment Required is reserved for future use in RFC 9110. If you use it for billing problems, document exactly what it means in your API.
  • 200 with an error body breaks retries, monitoring and caching. If the request failed, say so in the status code.
  • Temporary registrations: the IANA registry currently lists 104 (Upload Resumption Supported) as a temporary registration expiring on 13 November 2026, tied to an IETF draft. Check the registry before relying on anything new.

Check status codes from the terminal

Shell
# Status code only
curl -s -o /dev/null -w "%{http_code}\n" https://example.com/api/orders/123

# Status line and headers (look for Location, Allow, Retry-After, WWW-Authenticate)
curl -si -X DELETE https://example.com/api/orders/123 | head -n 20

In Node.js, import { STATUS_CODES } from 'node:http' gives you a lookup table of names, which is handy for logs but, as noted above, not always the newest wording. In PHP, http_response_code(422) sets the status in plain PHP; frameworks such as Laravel and Symfony provide response helpers.

What to do next

  1. List every endpoint and the status codes it can return; put them in your OpenAPI description.
  2. Choose 400 or 422 for validation and use it everywhere.
  3. Add the required headers: Location on 201, Allow on 405, WWW-Authenticate on 401, and Retry-After on 429 and 503 where you can.
  4. Replace 301 and 302 with 307 and 308 for anything other than browser page moves.
  5. Write one test per error path, so a refactor cannot quietly turn a 404 into a 500.

Consistent status codes are cheap documentation. When every endpoint follows the same rules, your API's clients need fewer special cases, and your logs tell you at a glance whether the caller or the server is at fault.

Frequently asked questions

  • What is the difference between 401 and 403?

    401 means the request is not authenticated and must include a WWW-Authenticate challenge. 403 means the caller is known but not allowed to do this.

  • Should validation errors return 400 or 422?

    Either works if you are consistent. RFC 9110 defines 422 for well-formed but semantically invalid content, and Laravel returns 422 for failed JSON validation.

  • When should I use 307 or 308 instead of 302 or 301?

    Whenever the request method must be preserved, which is almost always the case for APIs. Clients may turn a POST into a GET when following 301 or 302.

  • Is 422 still called Unprocessable Entity?

    RFC 9110 renamed it Unprocessable Content, and 413 became Content Too Large. Some libraries, including Node.js 24's http.STATUS_CODES, still use the old names.

  • Which header should accompany a 429 response?

    Retry-After, telling the client how long to wait. RFC 6585 says the response may include it, and well-behaved clients use it to back off.

  • Can I return 404 instead of 403?

    Yes. RFC 9110 allows a server to respond 404 to hide the existence of a forbidden resource, which is useful for records belonging to other users or tenants.

Advertise on CloudSoftZone

Your next customer could start right here.

Introduce your tool, product or business to readers exploring technology.

Advertise with us

Explore placements and request a private quote

Sources

  1. RFC 9110: HTTP Semantics — IETF, 2022-06-01
  2. Hypertext Transfer Protocol (HTTP) Status Code Registry — IANA
  3. RFC 6585: Additional HTTP Status Codes — IETF, 2012-04-01
  4. HTTP response status codes — MDN
  5. Laravel 13.x: Validation — Laravel

Comments

No comments yet. Be the first to join the conversation.

Leave a comment

Every comment is read by a moderator before it appears. Your email address is never published; it is used only to contact you about this comment.

Never published. Used only to contact you about this comment.

Loading the security check…

Comments are moderated before publication.