HTTP status codes
The codes that actually matter in production — and the ones applications routinely get wrong.
The five classes
| Range | Meaning |
|---|---|
1xx | Informational — keep going |
2xx | Success |
3xx | Redirection — go elsewhere |
4xx | Client error — fix the request |
5xx | Server error — we broke something |
The class is the part a client should branch on. Only the first digit is semantic; the other two are a label. That is why a client that switches on the exact code breaks when a server starts sending 422 where it used to send 400 — both are 4xx, and both mean the same thing to the caller: do not retry unchanged.
HTTP/1.1 404 Not Found
Content-Type: application/problem+json
Cache-Control: no-store
{
"type": "https://example.com/errors/not-found",
"title": "No such article",
"status": 404,
"detail": "Article 8f2c was deleted on 2026-08-01."
}💡
The status line is the first line a client sees. A JSON body describing the error is useful to a human, but the code is the contract — an API that returns
200 OK with an error object forces every caller to parse the payload to find out whether the call worked.The ones you will meet daily
| Code | Use it for |
|---|---|
200 OK | Standard success with a body |
201 Created | Resource created — send Location |
204 No Content | Success, nothing to return |
301 / 308 | Permanent redirect (308 preserves the method) |
302 / 307 | Temporary redirect (307 preserves the method) |
304 Not Modified | Cache is still valid — for conditional requests |
400 Bad Request | Malformed request; failed validation |
401 Unauthorized | Authentication required or invalid |
403 Forbidden | Authenticated but not permitted |
404 Not Found | No such resource |
409 Conflict | State conflict (duplicate, wrong version) |
422 | Well-formed but semantically invalid |
429 Too Many Requests | Rate limited — include Retry-After |
500 | Unhandled server fault |
502 / 503 / 504 | Bad gateway / unavailable / gateway timeout |
Common mistakes
- Returning 200 with an error body: clients cannot tell success from failure without parsing.
- Using 401 where 403 belongs: 401 means 'who are you?', 403 means 'I know who you are, and no'.
- Returning 404 for resources the user may not know exist — sometimes 403/404 by choice for privacy.
- Redirecting POST with 302 — many clients convert it to GET. Use 307/308 to preserve the method.
⚠️
Do not return 3xx from an API endpoint expecting JSON unless redirect following is guaranteed. Many clients will not follow, and the redirect body gets ignored.
FAQ
Should validation failures be 400 or 422?
Either is defensible; 400 is the broad 'bad request' and 422 signals syntactically valid but semantically rejected. Pick one and document it.
What should I return for a health check?
200 with a tiny body, or 503 when dependencies are unhealthy so load balancers can pull the instance out.
Related
Last refreshed 2026-09-17.