The Five Classes of HTTP Status Codes
HTTP status codes are three-digit numbers grouped into five classes based on their first digit. Understanding this taxonomy is the first step to using them correctly — both when building APIs and when handling responses in client code.
- 1xx — Informational: Request received, continuing process. Rarely seen in application code.
- 2xx — Success: The request was successfully received, understood, and processed.
- 3xx — Redirection: Further action is needed to complete the request.
- 4xx — Client Error: The request contains bad syntax, invalid parameters, or cannot be fulfilled for a reason the client can fix.
- 5xx — Server Error: The server failed to fulfil a valid request due to a problem on the server side.
The most important design principle: use status codes semantically. Never return 200 OK with an error body. A well-designed API communicates intent through the status code before the client reads the response body.
2xx — Success Codes
200 OK — The standard success response for GET, PUT, and PATCH requests. The response body contains the requested or updated resource.
201 Created — A new resource was successfully created. Always return this after a successful POST that creates a resource. Include a Location header pointing to the new resource's URL.
202 Accepted — The request has been accepted for processing, but processing has not completed. Use for async operations: the server queued the job and will process it later.
204 No Content — Success, but there is no response body. Use for DELETE operations and for PUT/PATCH when you choose not to return the updated resource. Returning 200 with an empty body is incorrect — use 204.
206 Partial Content — The server is sending only part of the resource. Used for range requests and resumable file downloads.
3xx — Redirection Codes
301 Moved Permanently — The resource has moved to a new URL permanently. Browsers and crawlers will update their bookmarks. Use when migrating URLs during a redesign. Return the new URL in the Location header.
302 Found — Temporary redirect. The resource is temporarily at a different URL; come back and check the original URL next time. Commonly (mis)used for post-login redirects.
304 Not Modified — The client's cached version is still current. Used with ETag and Last-Modified headers to enable conditional requests. When the server returns 304, no body is sent — the client uses its cache.
307 Temporary Redirect — Like 302, but explicitly guarantees the method will not change (a POST stays a POST after the redirect). Use 307 over 302 for non-GET redirects.
308 Permanent Redirect — Like 301, but method-preserving. The permanent equivalent of 307.
4xx — Client Error Codes
400 Bad Request — The request is malformed. Missing required fields, invalid JSON, wrong data types. Return with a body explaining what was wrong.
401 Unauthorized — Authentication is required and has not been provided, or the provided credentials are invalid. Despite the name, this is about authentication, not authorization. Always include a WWW-Authenticate header.
403 Forbidden — The client is authenticated but does not have permission to access this resource. Do not return 404 to hide the existence of the resource unless you explicitly need to (see 404 notes).
404 Not Found — The resource does not exist. Also used intentionally when you do not want to reveal whether a resource exists to unauthorized clients (e.g., private user profiles).
405 Method Not Allowed — The HTTP method is not allowed for this endpoint. Return with an Allow header listing valid methods: Allow: GET, POST.
409 Conflict — The request conflicts with the current state of the resource. Use when a user tries to create a resource that already exists with the same unique identifier.
410 Gone — Like 404, but explicitly states the resource existed and has been permanently deleted. Useful for telling crawlers not to re-index a URL.
422 Unprocessable Entity — The request is well-formed but fails validation. The standard choice for form validation errors. Return detailed field-level errors in the body.
429 Too Many Requests — Rate limit exceeded. Always include a Retry-After header indicating when the client can retry. Optionally include X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers.
5xx — Server Error Codes
500 Internal Server Error — The server encountered an unexpected condition. This is the catch-all for unhandled exceptions. Never return detailed error messages or stack traces to clients in production — log them server-side instead.
501 Not Implemented — The server does not support the functionality required. Use for endpoints that are planned but not yet built.
502 Bad Gateway — The server, acting as a gateway, received an invalid response from an upstream service. Common when your Node.js app is down but Nginx is still running.
503 Service Unavailable — The server is temporarily unable to handle requests due to maintenance or overload. Include a Retry-After header. This is the correct response to return during planned maintenance windows.
504 Gateway Timeout — An upstream service did not respond in time. Common in microservice architectures when a downstream dependency is slow.
Best Practices for API Error Responses
Use a consistent error response format across all endpoints:
{
"error": {
"status": 422,
"code": "VALIDATION_FAILED",
"message": "The request body failed validation.",
"details": [
{ "field": "email", "message": "Must be a valid email address" },
{ "field": "age", "message": "Must be at least 18" }
],
"requestId": "req_abc123" // for correlating with server logs
}
}
The requestId field is especially valuable — it lets support teams find the exact log entry for a failed request without needing the user to reproduce it.