An HTTP status code is the server's one-line answer to every request. That single code determines what the client does next — retry, redirect to login, surface an error, or use a cached response. This guide pulls the codes from RFC 9110 that actually show up in production, and focuses on which code to pick when rather than dictionary definitions.
The five classes — what 1xx through 5xx mean
| Range | Class | Meaning |
|---|---|---|
| 1xx | Informational | Request received, still processing (rare in practice) |
| 2xx | Success | The request succeeded |
| 3xx | Redirection | The client should look somewhere else |
| 4xx | Client Error | The client must change the request before retrying |
| 5xx | Server Error | Server's fault; the client can't fix the request alone |
For a quick lookup by number or keyword, the fastest path is HTTP Status Codes.
The most common 2xx codes
- 200 OK — generic success. GET, PUT, POST with a response body.
- 201 Created — a new resource was created. Return for POST creations; include a
Locationheader with the new resource URL. - 202 Accepted — request acknowledged, processing async. Return a job ID and a polling URL.
- 204 No Content — success, no response body. Good fit for DELETE or idempotent PUT.
3xx — the confusing redirects
- 301 Moved Permanently — permanent move. Browsers and search engines refresh their caches. Use for slug renames. Watch out: some old clients change POST to GET on retry. For APIs, prefer 308.
- 302 Found — temporary move. Same method-change quirk.
- 303 See Other — after a POST/PUT, redirect a browser to a GET result page. Explicitly switches the method to GET.
- 304 Not Modified — tells the client to use its cached copy. Triggered by
If-None-Match/If-Modified-Since. - 307 Temporary Redirect — temporary move; method preserved.
- 308 Permanent Redirect — permanent move; method preserved. The right choice when moving API endpoints.
4xx — client errors
400 vs 422 — shape vs meaning
- 400 Bad Request — the request itself is malformed. JSON parsing fails, a required header is missing, a query parameter is wrong shape. The server can't even understand the payload.
- 422 Unprocessable Content — the payload parsed fine but violates business rules. The email is well-formed but already registered; the price is negative. WebDAV origin, but standard in REST APIs (Rails, Symfony, Stripe).
Rule of thumb: did the server manage to parse the body? No → 400. Yes, but it broke a validation rule → 422.
401 vs 403 — who you are vs what you can do
- 401 Unauthorized — authentication failure. No token, expired token, forged token. Semantically: "I don't know who you are." The client should send the user to login. Return a
WWW-Authenticateheader. - 403 Forbidden — authorization failure. The server knows who you are; you just can't access this resource. Semantically: "I know you, but no." The client should show a permission page or contact-admin link.
Examples: unauthenticated request to /admin = 401. Authenticated regular user hitting /admin = 403.
Other common 4xx codes
- 404 Not Found — resource doesn't exist. Could be the path is wrong or the ID is missing. Many APIs use 404 instead of 403 to hide the existence of a resource (Stripe, GitHub).
- 405 Method Not Allowed — URL is correct but the method isn't. The response must include an
Allowheader listing the supported methods. - 409 Conflict — concurrency collision. ETag mismatch; a record with that name already exists. The client should refetch and retry.
- 410 Gone — permanently deleted. Unlike 404, this explicitly says "it used to be here and isn't anymore", so search engines drop the URL faster.
- 413 Payload Too Large — body exceeds the server's limit. Common for upload caps.
- 415 Unsupported Media Type — the
Content-Typeisn't supported. Sending XML to a JSON-only endpoint. - 429 Too Many Requests — rate limit exceeded. Include
Retry-Afteras seconds or HTTP-date.
5xx — server errors
- 500 Internal Server Error — uncategorized server fault. The catchall when an unexpected exception bubbles up. Never leak stack traces in the body.
- 501 Not Implemented — the server doesn't support this method at all. Different from 405: "this server is never going to handle that method."
- 502 Bad Gateway — a proxy got an invalid response from upstream. The classic Cloudflare/Nginx screen.
- 503 Service Unavailable — temporarily down (maintenance, overload). Include
Retry-After. - 504 Gateway Timeout — upstream didn't respond in time. Client should back off and retry.
Retryable vs not
Clients need to know which codes are safe to retry automatically and which are not.
- Safe to retry: 429 (wait
Retry-After), 502/503/504 (exponential backoff with jitter). - Do not retry: 400, 401, 403, 404, 422 — the same request will fail the same way. Fix the code or the input.
- Auto-retry only for idempotent methods — GET, PUT, DELETE are safe. POST needs an idempotency key to be retryable without duplicating work.
API design tips
- Be consistent — pick one code per failure category and stick to it across the API. Agree as a team that "business validation failure = 422".
- Add an error code in the body — the HTTP status is a coarse class; a JSON
error_codefield carries the specific reason (e.g.email_already_taken). Clients branch on that. - Be careful with 3xx — many API clients don't follow them. Reserve for OAuth flows; avoid for normal REST responses.
- Don't return 200 for errors — putting
success: falsein a 200 response breaks every HTTP infrastructure layer that looks at status codes (proxies, caches, CDNs). Use 4xx/5xx.
Debugging tools
When a response arrives and you need the meaning fast, HTTP Status Codes is the quickest path — search by number or keyword. If the URL itself is suspect, URL Parser breaks it into path/query/fragment. To reproduce the call with curl, cURL Builder assembles method/header/body for you.
Summary
- 2xx success, 3xx redirect, 4xx client, 5xx server.
- 400 = parse failure, 422 = business violation. 401 = unknown identity, 403 = no permission.
- Permanent API moves use 308 (preserves method). 301/302 are for browsers.
- Auto-retry only 429/502/503/504. Other 4xx need a code or input fix.
- HTTP status + body
error_codeis the clearest contract.