Xerobit

HTTP Status Codes

Every HTTP status code from the IANA registry. Searchable, filterable by category, with plain-English meaning, including the tricky ones nobody gets right (422 vs 400, 401 vs 403, 301 vs 308).

100
Continue
Server received the request headers and client should proceed to send the body.
101
Switching Protocols
Server is switching protocols as requested via Upgrade header.
102
Processing
WebDAV: server has received and is processing the request.
103
Early Hints
Server is sending hints (typically Link preload) before the final response.
200
OK
Standard success response.
201
Created
Request succeeded and a new resource was created. Include Location header.
202
Accepted
Request accepted for processing but not yet completed.
203
Non-Authoritative Information
Response is from a transforming proxy, not the origin.
204
No Content
Success with no body to return. Common for PUT and DELETE.
205
Reset Content
Success; client should reset the document view (e.g. form).
206
Partial Content
Range request succeeded. Used for video streaming and resumable downloads.
207
Multi-Status
WebDAV: multiple independent responses in the body.
208
Already Reported
WebDAV: members of a binding already enumerated.
226
IM Used
Response is a delta encoding result.
300
Multiple Choices
Multiple options available; rarely used.
301
Moved Permanently
Permanent redirect. SEO-safe; search engines transfer rank.
302
Found
Temporary redirect. Original URL retains its rank.
303
See Other
Redirect that forces a GET on the new URL. Post-redirect-get pattern.
304
Not Modified
Cached version still valid. No body returned.
307
Temporary Redirect
Like 302 but preserves HTTP method.
308
Permanent Redirect
Like 301 but preserves HTTP method. Prefer over 301 for modern APIs.
400
Bad Request
Malformed request. Generic client error.
401
Unauthorized
Authentication required (missing/invalid credentials). Should be "Unauthenticated".
402
Payment Required
Reserved. Used by some APIs for quota exhaustion.
403
Forbidden
Authenticated but not authorized. Server understood, refused.
404
Not Found
Resource does not exist.
405
Method Not Allowed
Method not supported on this resource. Response should list allowed methods.
406
Not Acceptable
Server cannot produce response matching Accept header.
407
Proxy Authentication Required
Like 401 but for the proxy.
408
Request Timeout
Server timed out waiting for the request.
409
Conflict
Request conflicts with current state (e.g. duplicate resource).
410
Gone
Resource permanently removed. Stronger signal than 404.
411
Length Required
Content-Length header missing.
412
Precondition Failed
If-Match or similar precondition failed.
413
Payload Too Large
Request body exceeds server limit.
414
URI Too Long
URL exceeds server limit.
415
Unsupported Media Type
Content-Type not supported.
416
Range Not Satisfiable
Requested range outside the resource size.
417
Expectation Failed
Expect header requirement cannot be met.
418
I'm a Teapot
April Fools joke from RFC 2324. Real HTCPCP response.
421
Misdirected Request
Server not configured to produce response for combined URL + Host.
422
Unprocessable Entity
Semantic validation failure. Use for "request is syntactically fine but logically invalid".
423
Locked
WebDAV: resource locked.
424
Failed Dependency
WebDAV: prior request failed.
425
Too Early
Server unwilling to process an early-data request to avoid replay.
426
Upgrade Required
Client must upgrade to a different protocol.
428
Precondition Required
Server requires request to be conditional (If-Match etc.).
429
Too Many Requests
Rate limit exceeded. Include Retry-After header.
431
Request Header Fields Too Large
Headers exceed server limit.
451
Unavailable For Legal Reasons
Content blocked for legal reasons. References Fahrenheit 451.
500
Internal Server Error
Generic server failure. Check logs.
501
Not Implemented
Server does not recognize or support the method.
502
Bad Gateway
Upstream server returned invalid response.
503
Service Unavailable
Temporarily unavailable. Include Retry-After.
504
Gateway Timeout
Upstream server timed out.
505
HTTP Version Not Supported
Server does not support the HTTP version.
506
Variant Also Negotiates
Internal configuration error in content negotiation.
507
Insufficient Storage
WebDAV: out of space.
508
Loop Detected
WebDAV: infinite loop in request.
510
Not Extended
Further extensions required to fulfill the request.
511
Network Authentication Required
Client must authenticate with the network (captive portal).

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 Location header 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:

CodeNameUse it when…
200OKStandard success: GET, PUT, PATCH returned data.
201CreatedResource created. Add a Location header pointing to the new URL.
204No ContentSuccess, nothing to return: DELETE, or PUT when you don't return the updated resource.
301Moved PermanentlyURL has permanently moved. SEO link equity transfers. Clients may change POST to GET: prefer 308 for APIs.
308Permanent RedirectLike 301 but method is preserved. The modern choice for permanent API redirects.
400Bad RequestRequest is malformed: missing required field, wrong content-type, unparseable body.
401UnauthorizedNo credentials provided, or credentials are invalid/expired. Prompt re-login.
403ForbiddenCredentials are valid but the user lacks permission for this resource or action.
404Not FoundResource does not exist. Also use to hide existence of private resources from unauthorized users.
422Unprocessable EntityRequest is syntactically valid but semantically wrong: business rule validation failures.
429Too Many RequestsRate limit exceeded. Include Retry-After header with wait time in seconds.
500Internal Server ErrorUnhandled 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-Control or Expires is 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-Match or Last-Modified / If-Modified-Since conditional 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=60 at 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.

Pillar