Status code categories at a glance
- 1xx Informational — request received, processing continues. Rare in practice.
- 2xx Success — request succeeded.
- 3xx Redirection — further action needed to complete.
- 4xx Client errors — client's fault (bad syntax, bad auth, bad resource).
- 5xx Server errors — server's fault (broken, overloaded, misconfigured).
Common status-code debates
401 vs 403
401 Unauthorized — "I don't know who you are." The request has no credentials, or the credentials are invalid/expired. Reply to an unauthenticated client.
403 Forbidden — "I know who you are and you can't have this." Credentials are valid but the user lacks permission for the resource.
The 401 name is a misnomer — it really means unauthenticated, not unauthorized. Too late to rename now.
422 vs 400
400 Bad Request — the request itself is malformed (invalid JSON, missing required headers, bad syntax).
422 Unprocessable Entity — the request is syntactically valid but semantically wrong (email address is well-formed but not a real email, user ID exists but isn't active). Most modern APIs prefer 422 for business-rule validation failures.
301 vs 302 vs 307 vs 308
- 301 Moved Permanently — permanent, but some clients change the request method from POST to GET (historical bug that became de facto behavior).
- 302 Found — temporary, same method-change issue.
- 307 Temporary Redirect — like 302 but preserves the HTTP method. Safe for POST.
- 308 Permanent Redirect — like 301 but preserves the method. Prefer 308 for modern APIs.
204 No Content
204 returns success with no body. Use it for PUT/DELETE success, or for any endpoint where
the client doesn't need a response payload. Pairs with Content-Length: 0. Saves bytes on
high-volume endpoints.
429 Too Many Requests
Rate-limit hit. Always include Retry-After (seconds or HTTP-date) so clients know when to
retry. Good APIs also include X-RateLimit-Remaining and X-RateLimit-Reset.
Tips for choosing the right status
- Use 201 after creating a resource, and include a
Locationheader pointing to the new resource. - Use 202 for long-running async tasks — return a polling URL.
- Use 503 for planned downtime with
Retry-After. - Never use 200 for errors. Returning a JSON body like
{"success": false, "error": "..."}with a 200 status is a common bug — clients check status codes, not bodies. - Don't over-engineer. 200, 201, 204, 400, 401, 403, 404, 409, 422, 429, 500 cover 95% of real APIs.
The 11 most useful HTTP status codes for API design
You don't need to memorize all 60+ registered status codes. This set covers 99% of real-world API needs:
| Code | Name | Use it when… |
|---|---|---|
| 200 | OK | Standard success — GET, PUT, PATCH returned data. |
| 201 | Created | Resource created. Add a Location header pointing to the new URL. |
| 204 | No Content | Success, nothing to return — DELETE, or PUT when you don't return the updated resource. |
| 301 | Moved Permanently | URL has permanently moved. SEO link equity transfers. Clients may change POST to GET — prefer 308 for APIs. |
| 308 | Permanent Redirect | Like 301 but method is preserved. The modern choice for permanent API redirects. |
| 400 | Bad Request | Request is malformed — missing required field, wrong content-type, unparseable body. |
| 401 | Unauthorized | No credentials provided, or credentials are invalid/expired. Prompt re-login. |
| 403 | Forbidden | Credentials are valid but the user lacks permission for this resource or action. |
| 404 | Not Found | Resource does not exist. Also use to hide existence of private resources from unauthorized users. |
| 422 | Unprocessable Entity | Request is syntactically valid but semantically wrong — business rule validation failures. |
| 429 | Too Many Requests | Rate limit exceeded. Include Retry-After header with wait time in seconds. |
| 500 | Internal Server Error | Unhandled exception or server-side bug. Log the error internally; return a generic message to the client. |
HTTP status codes and caching
Status codes tell intermediary caches (CDN, browser, proxy) what they can store. Getting this wrong leads to stale data or cache misses on things that should be cached:
- 200 OK — cacheable if
Cache-ControlorExpiresis present. Without explicit cache headers, browsers may heuristically cache GET responses. Always set explicit headers on API responses. - 301 Moved Permanently — cached indefinitely by default. Browsers will not re-request the old URL after seeing a 301. This is intentional — use 307 if you might need to change the redirect later.
- 302 Found / 307 Temporary Redirect — not cached by default. The client re-checks the redirect on every request, which is usually what you want for temporary moves.
- 304 Not Modified — tells the client to use its cached version. The server returns no body. Works with
ETag/If-None-MatchorLast-Modified/If-Modified-Sinceconditional request headers. - 404 Not Found — do not cache aggressively. A 404 today might be a valid URL tomorrow. Set a short
Cache-Control: max-age=60at most. - 410 Gone — like 404 but permanent. Safe to cache indefinitely. Use 410 when a resource was intentionally removed and will never return — CDNs can cache this for a long time.
Status codes in REST vs GraphQL
A major philosophical difference between REST and GraphQL is how errors are communicated:
REST uses status codes semantically — a failed request returns a 4xx or 5xx status, and HTTP clients, monitoring tools, and load balancers understand that the request failed without parsing the body.
GraphQL always returns 200 OK — even when a query fails, validation errors occur, or resolvers throw exceptions. Errors appear in the errors array in the response body. This means a 200 response might contain a full stack trace.
This design decision is controversial. Arguments for it: partial results are valid (some fields succeed, others fail), and HTTP status codes don't map cleanly to field-level errors. Arguments against it: monitoring tools that check for non-200 responses miss all GraphQL errors, APM alerts never fire, and intermediary caches may incorrectly cache a "200" error response.
Common workarounds: some teams return 400 for request-level errors (malformed query, missing auth) while keeping 200 for resolver-level errors. Others use the x-http-code header or a custom gateway layer to expose error status. The GraphQL over HTTP spec (published 2023) recommends returning 4xx for certain error classes — check if your server library has implemented it.
FAQ
Should I return 200 with an error body?
No. Returning {"success": false, "error": "Not found"} with a 200 status is an anti-pattern that breaks HTTP. Every HTTP client library, monitoring tool, APM system, CDN, and load balancer uses the status code to determine success or failure. Returning a 200 with an error body means: your CDN might cache the error response; your monitoring never alerts; client libraries that check response.ok report success when there was none; and any developer reading logs sees a wall of 200s and cannot tell which requests failed.
What is 418 I'm a Teapot?
418 comes from RFC 2324, the "Hyper Text Coffee Pot Control Protocol" — a 1998 April Fools' Day RFC written as a joke. The spec defines a protocol for controlling networked coffee pots, and 418 means "the server is a teapot and refuses to brew coffee." It was intended as humor and never meant for real use. Despite that, Chrome, Firefox, Node.js, and most major HTTP frameworks implement it. Google ran a server that responded 418 for years. It was formally acknowledged in RFC 7168 (2014). Today it's used as a fun response for "this endpoint intentionally does nothing" or easter eggs.
Related tools
- JSON Formatter — Format, validate, and beautify JSON online. 100% client-side — your data never leaves your browser.
- URL Encoder / Decoder — Percent-encode and decode URLs per RFC 3986.
- JWT Decoder — Decode and inspect JSON Web Tokens. Local-only — tokens never leave your browser.
- Regex Tester — Test regular expressions with live match highlighting and explanation.
Related articles
- 4 min readHTTP 301 vs 302 Redirect — Permanent vs Temporary Redirects ExplainedUnderstand when to use 301 (permanent) vs 302 (temporary) redirects, how they affect SEO page rank transfer, browser caching, and which status code to use for specific...
- 5 min readHTTP Cache Headers — Cache-Control, ETag, and Last-Modified ExplainedMaster HTTP caching headers: Cache-Control directives (max-age, no-cache, immutable), ETag for conditional requests, and Last-Modified. Learn how to cache static assets forever...
- 6 min readHTTP 404 Not Found — What It Means and How to Fix ItHTTP 404 means the server understood the request but can't find the resource. It's different from 410 Gone, 301 Moved, and 500 Server Error. Here's when each status applies and...
- 5 min readHTTP 500 Internal Server Error — What It Means and How to Debug ItHTTP 500 means the server encountered an unexpected error it couldn't handle. Unlike 4xx errors, 500 is always the server's fault. Here's how to diagnose and fix HTTP 500 errors.
- 4 min readHTTP Redirect Status Codes — 301, 302, 307, 308 ExplainedHTTP 3xx redirect codes tell clients to look elsewhere for a resource. Here's when to use 301 vs 302 vs 307 vs 308, how redirects affect SEO, and how to implement redirects in...
- 5 min readHTTP 4xx Client Error Codes — 400, 401, 403, 404, 409, 422, 429 ExplainedHTTP 4xx status codes indicate client errors. Here's when to use 400 Bad Request vs 422 Unprocessable Entity, the difference between 401 and 403, what 409 Conflict means, and...
Pillar
Part of Dev Productivity — regex, cron, timestamps, HTTP, color, word counter, aspect ratio, case.
Written by Mian Ali Khalid. Last updated 2026-05-13.