Somewhere in production right now, an API is returning 200 OK with {"error": "payment failed"} in the body, and a monitoring dashboard is showing green. That's the case for actually knowing your HTTP status codes: they're the contract between every client and server on the internet, and half of debugging is knowing what the number is trying to tell you — especially when it's lying.
Here's the working developer's version: the mental model, the codes you'll actually meet, the pairs everyone confuses, and what each one means when you're staring at it in a log at 2 a.m.
1xx — hold on, more coming (you'll almost never touch these). 2xx — it worked. 3xx — what you want is elsewhere. 4xx — you messed up. 5xx — we messed up. That last distinction is the load-bearing one: 4xx means don't retry the same request, 5xx means retrying might work. Whole retry libraries are built on that single bit of information — which is why returning the wrong class is worse than returning no code at all.
200 OK — worked, here's the body. 201 Created — worked, made a new resource; REST APIs should return this from a successful POST, ideally with a Location header. 204 No Content — worked, nothing to say; the right answer for a successful DELETE. If your API returns 200 for everything including failures, you've opted out of HTTP semantics and every standard tool — retries, caches, monitors — now works against you.
301 is a permanent move and passes link equity — the SEO workhorse. 302 is temporary: search engines keep the old URL indexed. Shipping a 302 where you meant a 301 is one of the oldest self-inflicted SEO wounds and it still happens constantly. 308 is 301's stricter sibling: permanent, but the method and body must not change — a POST stays a POST, where many clients quietly turn a 301'd POST into a GET. 304 Not Modified is the caching handshake: nothing to download, use what you have.
Two redirect sins to audit for: chains (A→B→C→D wastes crawl budget and latency; go straight to D) and redirecting everything deleted to the homepage — that's a "soft 404" and search engines treat it as such.
400 Bad Request — malformed input; the server couldn't even parse it. 401 Unauthorized vs 403 Forbidden — the eternal mixup, untangled: 401 means "I don't know who you are" (missing or invalid credentials — try again with auth), 403 means "I know exactly who you are, and no" (authenticated but not allowed). The names are backwards — 401 is really about authentication, not authorization — thank the 1990s for that.
404 Not Found vs 410 Gone — 404 says "not here, no idea why," 410 says "existed, deliberately removed, stop asking." Search engines drop 410s from the index faster. 405 — right URL, wrong verb (you GET an endpoint that only takes POST). 409 Conflict — the state disagrees with your request (duplicate signup, stale edit). 422 — parsed fine, semantically wrong; the validation-error workhorse. 429 Too Many Requests — rate limited; read the Retry-After header and back off exponentially, because hammering a 429 is how you graduate to an IP ban.
500 — unhandled exception; the server's own code blew up. 502 Bad Gateway vs 504 Gateway Timeout — both come from a proxy in front of your app, and the difference routes your debugging: 502 means the upstream sent garbage or nothing (app crashed, wrong port, connection refused — check whether your process is alive), 504 means the upstream is alive but too slow (query taking 45 seconds against a 30-second proxy timeout — check the slow path, not the process). 503 Service Unavailable — overloaded or in maintenance; the only 5xx you sometimes return on purpose, with a Retry-After.
When you hit a code you can't place, our HTTP status code reference has the complete list with plain-English explanations — and for the error messages that come with the codes, the AI error explainer translates stack traces into causes.
Browsers follow redirects silently and dress up error pages, so debug at the wire: curl -i shows the status line and headers of each hop (the cURL builder writes the command for you), and the API tester does it with a UI. The pattern that solves most mysteries: compare the status your code receives with the status the same request gets from a clean client. When they differ, the bug is in your request — headers, auth, or method — not the server.
Past a certain scale you stop reading logs line by line and start automating the triage: an agent that watches responses, classifies by status family, retries what's retryable, and files the 5xx spike with context attached. That's the layer QADIR OS is built for — local-first agents with HTTP tooling that run on your machine, so your API keys and traffic patterns stay yours.
Keep the full list one tab away. Bookmark the HTTP status code reference, and test endpoints live with the free API tester. Want agents that do the triage for you? Join early access — no card required.