ποΈ 29042026 1200
π #api #http
The five status-code classes and the specific codes that carry distinct meaning. Most of the codes are forgettable; the ones that aren't (401 vs 403, 422 vs 400, 409, 429, 503) come up in real API decisions.
The Five Classesβ
1xx Informational β interim, rarely surfaced to apps (e.g. 100 Continue, 101 Switching Protocols). 2xx Success β request completed as intended. 3xx Redirect β client must take additional action to complete. 4xx Client Error β request itself is faulty; do not retry without changing the request. 5xx Server Error β server failed; same request might succeed later.
Class semantics matter because clients, proxies, and SDKs auto-retry differently based on class. Returning the wrong class causes wrong retry behaviour.
2xx β The Useful Onesβ
| Code | When |
|---|---|
| 200 OK | Generic success with body. Default for GET. |
| 201 Created | New resource created. Include Location header pointing to the new resource. Default for POST creates. |
| 202 Accepted | Async work queued; not yet complete. Body should describe how to track. |
| 204 No Content | Success with no body to return. Default for PUT/DELETE that have nothing to say. |
| 206 Partial Content | Range request response (downloads, video). |
3xx β Redirectsβ
| Code | When |
|---|---|
| 301 Moved Permanently | Resource has a new permanent URL. Browsers cache aggressively. Hard to undo. |
| 302 Found | Temporary redirect. Browsers don't cache (mostly). |
| 303 See Other | After POST, redirect client to GET a result resource. |
| 304 Not Modified | Conditional GET β client's cached copy is still valid. |
| 307 Temporary Redirect | Like 302 but preserves method (302 may convert POSTβGET historically). |
| 308 Permanent Redirect | Like 301 but preserves method. |
Use 307/308 in API contexts to avoid the historical method-rewriting baggage of 301/302.
4xx β Client Errorsβ
| Code | When |
|---|---|
| 400 Bad Request | Syntactically invalid (malformed JSON, missing required field). Fix the request. |
| 401 Unauthorized | Authentication missing or invalid. Send credentials. Confusingly named β should be "Unauthenticated". |
| 403 Forbidden | Authenticated, but not allowed. Different user/scope/permission needed. |
| 404 Not Found | Resource doesn't exist (or you can't see it for privacy reasons). |
| 405 Method Not Allowed | Endpoint exists but doesn't accept this method. Must include Allow header. |
| 409 Conflict | State conflict (concurrent edit, duplicate creation). Common for optimistic concurrency violations. |
| 410 Gone | Resource permanently removed; do not retry. Distinct from 404 (don't know). |
| 412 Precondition Failed | If-Match / If-Unmodified-Since failed. |
| 415 Unsupported Media Type | Server can't process the request body's content-type. |
| 422 Unprocessable Entity | Syntactically OK but semantically invalid (validation failure). Distinct from 400. |
| 429 Too Many Requests | Rate-limited. Include Retry-After header. See rate_limiting_algorithms. |
401 vs 403 β the most-asked clarificationβ
- 401: "I don't know who you are." β log in / send a token.
- 403: "I know who you are, you can't do this." β switch user / request access.
Sending 403 when you mean 401 (or vice versa) breaks SSO redirect flows and SDK auth-refresh logic.
422 vs 400β
- 400: bad syntax. Cannot parse. JSON malformed, required field missing.
- 422: parsed fine, but the contents fail validation (negative age, invalid email format, business rule violation).
Many APIs use 400 for both. RFC 9110 added 422 to fit; using it cleanly improves error handling on the client side.
5xx β Server Errorsβ
| Code | When |
|---|---|
| 500 Internal Server Error | Unhandled exception, generic failure. The "I don't know what went wrong" response. |
| 501 Not Implemented | Method recognised but not implemented. Distinct from 405. |
| 502 Bad Gateway | Upstream returned an invalid response (typically the reverse proxy says this). |
| 503 Service Unavailable | Overloaded / under maintenance. Include Retry-After. |
| 504 Gateway Timeout | Upstream timed out. Common for slow downstream services. |
5xx is the class clients/SDKs auto-retry. Don't return 5xx for "your input was wrong" β that's 4xx, and clients won't retry it.
What Each Class Means for Retriesβ
| Class | Retry advice |
|---|---|
| 2xx | Don't retry. Done. |
| 3xx | Follow redirect (or not, per client policy). |
| 4xx | Do not retry without changing the request. Exception: 408 Request Timeout, 425 Too Early, 429 (after backoff). |
| 5xx | Retry with retry_backoff_jitter. Exception: 501 Not Implemented (request will never succeed). |
429 is the special case β yes retry, but only after Retry-After.
Common Pitfallsβ
- 400 for everything 4xx-ish β collapses meaningful distinctions. Use the specific code so clients can react correctly (auth refresh on 401, retry on 429, etc.).
- 200 with
{"error": ...}body β a few APIs do this. Breaks every monitoring tool that filters by status. Use HTTP status codes. - 500 for validation failures β clients retry, server keeps failing the same way. Use 4xx.
- Missing
Retry-Afteron 429 / 503 β clients then guess. Worse, they hammer with default delays. Always set it. - No
Locationon 201 β clients can't find the resource they just created. - Custom 600+ codes β proxies and gateways drop or rewrite. Stay within standard ranges.
- Conflating 503 and 504 β 503 means "I refuse" (overloaded); 504 means "upstream didn't answer". Different operational signals.
Allowheader missing on 405 β spec requires it. Many implementations forget. Without it, clients can't programmatically discover the right method.
Relatedβ
- http_methods_idempotency_safety β what methods should return.
- retry_backoff_jitter β what to do with 5xx and 429.
- rate_limiting_algorithms β what 429 sits in front of.
- circuit_breaker_pattern β why a chain of 5xx triggers a circuit open.
Referencesβ
- RFC 9110 Β§15: Status Codes
- IANA: HTTP Status Code Registry
- Mozilla MDN: HTTP response status codes