HTTP status codes are a contract older than most of the companies using them. Most APIs survive on a handful of codes — but picking the right one at each decision point changes how clients, caches, and monitoring systems behave. Here is the working subset, with the decisions attached.
The success codes: 200, 201, 204
- 200 OK — the default success. Returns a body with the result.
- 201 Created — a POST that created a resource. Return it with a
Locationheader pointing at the new resource; clients that follow links get correct behavior for free. - 204 No Content — success with nothing to say: DELETE confirmations, PUT updates where the client already knows the state. Saves an empty-body round trip. Two cautions: 204 must not carry a body, and
await res.json()on a 204 throws — client code should branch on status.
Redirects: the method-preservation matrix
The redirect codes differ on one axis that breaks APIs when ignored: whether the request method survives.
| Code | Meaning | Method preserved? | Cached? |
|---|---|---|---|
| 301 | Moved permanently | Often rewritten POST→GET | Yes, aggressively |
| 302 | Found (temporary) | Often rewritten POST→GET | No |
| 307 | Temporary redirect | Strictly preserved | No |
| 308 | Permanent redirect | Strictly preserved | Yes |
For API versioning (/v1/… → /v2/…) use 308 — a rewritten POST is a corrupted API call. For page moves, 301 is fine and its caching is a feature. When migrating, check the redirect chain with a URL parser — accidental double-redirects multiply latency.
The 4xx family: say what you mean
- 400 Bad Request — malformed syntax. If the client can fix nothing by retrying unchanged, 400 is right.
- 401 vs 403 — 401 = “who are you?” (unauthenticated; include
WWW-Authenticate), 403 = “I know who you are; no” (unauthorized). Confusing them turns your error logs into noise. - 404 vs 410 — 404 for missing and maybe-temporarily-missing; 410 Gone tells caches and crawlers “permanently removed, stop asking” — useful for de-indexing expired content faster than 404.
- 429 Too Many Requests — the rate-limit code, always with
Retry-After. Clients that respect it are doing you a favor; make respecting it easy.
The 5xx family and what monitoring should do with them
500 is the generic crash, 502/504 mean an upstream/gateway failed (classic proxy-speak for “the app behind me is down or slow”), and 503 means deliberate unavailability (maintenance or overload — include Retry-After). The monitoring division of labor: 5xx spikes page the on-call; 4xx spikes get investigated, not paged — they usually mean a broken client release, an abusive crawler, or an expired token storm.
Status codes are also a caching contract
Codes are how caches decide what to keep: 200 with fresh headers is cacheable, 301 is cached aggressively, 404/410 are cacheable by many CDNs (a cached 404 can outlive the fix — purge after deployment), and 5xx must never be cached. When “we fixed it but some users still see the old response”, the status code and its cacheability are the first suspects. The full HTTP status reference includes the cacheability and method rules for every code, so the contract is one lookup away.