HTTP status.
Every HTTP status code currently registered with IANA, sourced from RFC 9110 (the unified HTTP semantics RFC, 2022) and its predecessors. Search by number, name, or description; filter by category. The reference you grep when you forget what 422 actually means.
Why the first digit matters.
HTTP status codes are three-digit integers from 100 to 599, partitioned into five categories by the first digit. The categorisation is not arbitrary — it dates to RFC 1945 (HTTP/1.0, 1996) and tells the client how to react before parsing the response body. A client receiving a 5xx code can retry; a client receiving a 4xx code shouldn't (the request itself is wrong, retrying without modification will fail again). A 3xx tells the client to follow a Location header; a 2xx tells it to consume the body as-is.
1xx informational codes are intermediate — the server has received the request headers and is signalling state without ending the exchange. 100 Continue tells a client that started uploading a large body to keep going. 101 Switching Protocols is what a server returns when a WebSocket Upgrade is accepted. 103 Early Hints (RFC 8297, 2017) is the newest and most underused: it lets the server send Link preload headers before the final response is ready, giving the browser a head start on fetching CSS and JavaScript while the dynamic page is still being assembled. CDNs (Cloudflare, Fastly) and frameworks (Next.js, SvelteKit) increasingly support 103 in 2024.
2xx success means the request was received, understood, and accepted. 200 OK is the workhorse. 201 Created should follow a successful POST that creates a resource (with a Location header pointing to the new URL). 204 No Content should follow a successful PUT or DELETE where the server has nothing to return. 206 Partial Content is the response to a Range request — what video players use to seek into a file without re-downloading from the start.
3xx redirection tells the client the resource lives somewhere else. The four-way distinction between 301/302/307/308 is the single most-confused area of HTTP. 301 and 308 are permanent (browsers and search engines update bookmarks); 302 and 307 are temporary. 301 and 302 historically allowed clients to switch the request method (POST → GET), which broke API behaviour; 307 and 308 explicitly forbid that change. For new APIs, prefer 308 for permanent and 307 for temporary; for human-facing redirects after a POST, use 303 See Other to explicitly turn the redirect into a GET.
4xx client errors indicate the request itself was wrong. 400 Bad Request is the catch-all; specific codes (401, 403, 404, 405, 409, 422, 429) cover named cases. The boundary between 400 and 422 is fuzzy — RFC 9110 says 400 is for malformed syntax and 422 is for "well-formed but semantically invalid", but most APIs use 400 broadly and 422 only when they want to signal validation errors specifically.
5xx server errors indicate the server's failure to handle a valid request. 500 is the catch-all. 502 Bad Gateway and 504 Gateway Timeout indicate failures in proxies or load balancers between the client and origin. 503 Service Unavailable signals temporary unavailability (planned maintenance, overload). 5xx responses are the right time to retry with exponential backoff; 4xx responses are not, because retrying without modifying the request will produce the same error.
From a tower of RFCs to one.
For most of HTTP's history, the spec was scattered across many documents. RFC 2616 (1999) was the original HTTP/1.1 — a single large document. RFC 7230 through 7235 (2014) split it into six focused RFCs covering message syntax, semantics, conditional requests, range requests, caching, and authentication. RFC 9110 (June 2022) re-unified the semantic layer: status codes, methods, headers, and the data model all live in one document again. The split-out RFCs 9111 (caching) and 9112 (HTTP/1.1 message syntax) handle the lower-level details.
RFC 9110 is the document to cite for any modern HTTP semantic question. It deprecates a handful of codes (303 has slightly cleaner semantics; some 4xx codes were renamed for clarity — 413 went from "Payload Too Large" to "Content Too Large"; 416 "Range Not Satisfiable" was tightened). It also formalises practices that were already universal — the requirement that 3xx responses include a Location header, the recommendation that 429 responses include a Retry-After header.
A handful of status codes live outside the core RFCs. 418 "I'm a teapot" comes from RFC 2324, an April Fools' joke from 1998 that some servers implement. 451 "Unavailable for Legal Reasons" (RFC 7725, 2016) honours Ray Bradbury's Fahrenheit 451 and is the right code for content removed under court order or geo-blocked for regulatory reasons. The IANA HTTP Status Code Registry is the authoritative list of every registered code; codes not in the registry should not be used in production.
The five most-mishandled codes in real APIs.
200 for everything. The single most common API anti-pattern is to return 200 OK regardless of outcome, with the actual error in the response body. {"status": "error", "message": "user not found"} with HTTP 200 makes monitoring useless — you can't alert on 4xx/5xx rates because there aren't any. Use the right status code; put structured error details in the body via RFC 7807 Problem Details. The body and the status work together.
401 vs 403 confusion. 401 means "you're not authenticated" — the request lacks credentials, and adding them might succeed. 403 means "you're authenticated but not authorised" — credentials won't help. Many APIs return 401 for both cases, which conflates "log in" with "you can't do this". The fix: check the Authorization header first; if missing or invalid, return 401; if valid but lacks permission, return 403.
404 for permission denials. Some APIs return 404 instead of 403 to avoid confirming that a resource exists. This is "security through obscurity" with real costs — your client cannot distinguish a missing resource from a permission issue, which makes debugging painful. The HTTP spec says you can do this, but most modern guidance is to be explicit: 403 means "this exists but you can't see it", 404 means "this doesn't exist". Pick one consistently.
500 instead of 4xx. A failed validation that should return 400 or 422 sometimes returns 500 because the server-side validation throws an unhandled exception that the framework converts to 500. The client retries (5xx is retryable), gets the same error, and writes a bug report. The fix: catch validation errors at the API boundary and translate them into the correct 4xx code with a structured error body.
302 instead of 303. The Post-Redirect-Get pattern (handle a form POST, then redirect to a GET that displays the result) needs 303 See Other, which explicitly tells the client to use GET on the new URL. 302 historically allowed the client to keep the POST method, which is wrong for PRG. Most modern frameworks default to the right code, but legacy code often gets this wrong, leading to subtle double-submission bugs.
Which codes should you retry?
The first-digit rule is the right starting point: retry 5xx, don't retry 4xx. The exceptions matter. 408 Request Timeout is a 4xx but should be retried — the server is telling you it gave up waiting. 429 Too Many Requests is a 4xx and should be retried, but only after honouring the Retry-After header. 425 Too Early is the rare 4xx that explicitly invites a retry. Among 5xx codes, 501 Not Implemented should not be retried — the server doesn't support what you're asking, and that won't change without a code deploy.
For idempotent methods (GET, HEAD, PUT, DELETE), retrying any 5xx is safe. For non-idempotent methods (POST), retries can cause duplicate effects. Stripe's idempotency-key pattern — the client generates a UUID per request, the server caches the response under that key, retries with the same key get the cached response — is the standard solution. Most modern APIs (Stripe, GitHub, Square) implement idempotency keys for unsafe methods.
Backoff strategy matters more than the binary retry-or-not decision. Exponential backoff with jitter — wait 2^n × random_factor between retries — prevents thundering herds when many clients receive errors simultaneously. AWS SDK, Google Cloud SDK, and most production HTTP clients implement exponential backoff with jitter as the default. Cap the maximum delay (typically 30s) and the maximum retry count (typically 5–10) to bound worst-case latency. The Retry Strategy simulator visualises the interactions.