API

HTTP Status Codes: The Complete Developer Guide (2026)

May 2, 2026 · 9 min read · Format API responses →

Every HTTP response carries a three-digit status code that tells the client what happened. Knowing what each code means — and when to use each one in your own APIs — is fundamental to building and debugging web applications. This guide covers every status code you'll actually encounter, with notes on correct usage.

The Five Classes at a Glance

Status codes are grouped by their first digit: 1xx Informational, 2xx Success, 3xx Redirection, 4xx Client Error, 5xx Server Error. The first digit tells you the category; the full three digits tell you the specific outcome.

1xx — Informational

These are rarely seen in application code but important to understand.

100
Continue
The server has received the request headers. The client should proceed to send the body. Used with large uploads when the client sends Expect: 100-continue first.
101
Switching Protocols
Server agrees to switch protocols as requested. Crucial for WebSocket upgrades — the browser sends Upgrade: websocket and the server responds with 101.
103
Early Hints
Allows the server to send response headers early, before the full response is ready. Used to hint at resources to preload (CSS, fonts) for performance.

2xx — Success

200
OK
The standard success response. Use for GET requests that return data, PUT/PATCH that return the updated resource, and POST that returns a result (not a newly created resource).
201
Created
A new resource was created. Return this for successful POST requests that create a record. Include a Location header pointing to the new resource's URL.
202
Accepted
The request was accepted for processing but processing is not complete. Use for async operations where work is queued and will be done later.
204
No Content
Success with no response body. Use for DELETE requests and PUT/PATCH when you do not return the updated resource. The response must have no body.
206
Partial Content
The server is delivering only part of the resource due to a Range header from the client. Used for video streaming and resumable downloads.

3xx — Redirection

301
Moved Permanently
The resource has permanently moved to a new URL. Browsers and crawlers cache this indefinitely. Use when you permanently change a URL structure — Google transfers SEO equity to the new URL.
302
Found (Temporary Redirect)
Temporary redirect. Browsers follow it but do not cache. Historically misused — prefer 303 or 307 for clearer semantics.
303
See Other
After a POST, redirect the client to a GET at a different URL. The correct way to implement the Post/Redirect/Get pattern — prevents form resubmission on browser refresh.
304
Not Modified
The resource has not changed since the client's cached version. The browser uses its cache and does not re-download the resource. Saves bandwidth — work with ETags and Last-Modified headers.
307
Temporary Redirect
Like 302 but guarantees the method is preserved. A POST stays a POST after the redirect. Use when temporarily redirecting any non-GET method.
308
Permanent Redirect
Like 301 but preserves the HTTP method. Use instead of 301 when permanently redirecting POST or PUT requests.

4xx — Client Errors

These mean the client made an error. A well-designed API uses specific 4xx codes so clients know exactly what to fix.

400
Bad Request
The request is malformed — invalid JSON, missing required fields, invalid field values. Always include an error body explaining what is wrong.
401
Unauthorized
Authentication is required but was not provided or is invalid. Despite the name, this means "unauthenticated." Include a WWW-Authenticate header.
403
Forbidden
The client is authenticated but does not have permission. The user is known but not allowed. Do not return 404 to hide the resource's existence when 403 is correct.
404
Not Found
The resource does not exist. Also use to hide 403 errors when you do not want to reveal that a resource exists at all (security-sensitive APIs).
405
Method Not Allowed
The HTTP method is not supported for this endpoint. Include an Allow header listing the permitted methods.
409
Conflict
The request conflicts with the current state. Classic use: attempting to create a resource that already exists, or an edit conflict in optimistic locking.
410
Gone
The resource existed but has been permanently deleted. Use instead of 404 when you want clients and crawlers to know the resource is intentionally gone and will not return.
422
Unprocessable Entity
The request is well-formed but semantically invalid. Use for validation errors — the JSON parsed fine, but the values fail business logic validation.
429
Too Many Requests
Rate limit exceeded. Include Retry-After and X-RateLimit-* headers so clients know when to retry and how many requests remain.

5xx — Server Errors

These mean the server failed. The client's request may have been valid — the problem is on your end.

500
Internal Server Error
An unexpected error occurred. The catch-all for unhandled exceptions. Log the full error server-side; never expose stack traces to clients.
502
Bad Gateway
A reverse proxy or gateway received an invalid response from an upstream server. Common when your app server crashes and nginx returns this to clients.
503
Service Unavailable
The server is temporarily unable to handle requests — overloaded or in maintenance. Include a Retry-After header. Use during planned deployments.
504
Gateway Timeout
The gateway timed out waiting for the upstream server. Common when a database query or external API call takes too long. Check upstream timeouts and query performance.

Common Mistakes to Avoid

Returning 200 for errors is the most damaging anti-pattern in API design. Some APIs return {"status": "error"} with HTTP 200 — this breaks every HTTP client, monitoring tool, and framework that relies on status codes. Always use the correct 4xx or 5xx code.

Using 404 when you mean 403 because you want to hide that a resource exists is sometimes correct — but do not do it reflexively. It makes debugging harder for legitimate clients.

Returning 500 for validation errors is also common and wrong. If the user sent bad data, that is a 400 or 422, not a 500. Reserve 5xx for genuine server failures.

Quick reference: 2xx = it worked. 3xx = look elsewhere. 4xx = you (the client) did something wrong. 5xx = we (the server) did something wrong.

Debugging API Responses

When debugging API responses, format the JSON body with ToolPry's JSON Formatter to quickly spot the error details. Use the URL Encoder if you need to inspect or construct query parameters. For timestamp-related 401 errors (token expiry), the Timestamp Converter can decode Unix timestamps in JWT expiry claims instantly.

Status Codes in REST API Design

Choosing the right status code is one of the most important decisions in REST API design. The status code is a contract with your clients — it tells them how to handle the response without parsing the body. A well-designed API uses specific status codes so clients can write simple, reliable error handling. A poorly designed API forces clients to parse every response body to determine whether the request succeeded.

The most important principle: never return 200 for an error. Some APIs return {"status": "error", "message": "Not found"} with HTTP 200. This breaks every HTTP client library, monitoring tool, load balancer, and caching layer that relies on status codes. Use 4xx and 5xx codes for errors, always.

Return meaningful error bodies alongside your status codes. A 400 response should explain which field is invalid and why. A 409 should identify what conflicted. A 429 should include Retry-After. The status code tells the client what category of problem occurred; the body explains the specific details.

Status Codes and Caching

HTTP caching is built on status codes. Understanding this helps you design APIs that take advantage of browser and CDN caching automatically.

200 OK responses are cached according to the Cache-Control header. Cache-Control: max-age=3600 caches the response for an hour. Cache-Control: no-store prevents caching entirely.

301 Moved Permanently is cached indefinitely by browsers. This is correct for permanent URL changes but dangerous if you use 301 for temporary redirects — browsers may cache the redirect long after you want to remove it. Clear the cache by issuing a 301 to itself with no-cache, then change it to the actual destination.

304 Not Modified is the caching success response. When a client sends If-None-Match or If-Modified-Since, and your content has not changed, return 304 with no body. The client uses its cached copy. This eliminates bandwidth entirely for unchanged resources.

404 Not Found should not be cached aggressively — resources may appear in the future. Avoid setting long max-age on 404 responses.

5xx responses should never be cached. A server error is transient — clients should retry without needing to wait for a cached error to expire.

Monitoring and Alerting by Status Code

Status code distributions tell you more about your application's health than almost any other single metric. Monitoring tools like Datadog, New Relic, and Prometheus all support status code breakdowns. Set alerts for conditions like: 5xx rate exceeds 0.1% of requests, 4xx rate increases by more than 10× baseline, 499 (client disconnected) rate increases (often indicates slow responses), or specific status codes like 429 increasing (rate limit pressure).

When debugging a production incident, the status code distribution over time is the first thing to check. A sudden spike in 503s means your servers are overwhelmed or a dependency is down. A spike in 401s might mean a certificate expired or a secret was rotated. A spike in 400s often means a client deployment changed its request format.

Frequently Asked Questions

What is the difference between 401 and 403?

This confuses many developers because the names seem backwards. 401 Unauthorized actually means unauthenticated — the client has not proved its identity. The name is a historical misnomer. 403 Forbidden means unauthorized — the client's identity is known, but it does not have permission for this action. The practical rule: if the client has not logged in (no token, invalid token, expired token), return 401. If the client is logged in but cannot access this resource (wrong role, wrong ownership), return 403.

Should I return 404 or 403 for a resource the user cannot access?

It depends on whether you want to reveal the resource exists. A 403 confirms the resource exists but is forbidden. A 404 reveals nothing. For most APIs, 403 is correct and more useful to developers — they know the endpoint exists and need to fix their permissions. For security-sensitive APIs (admin endpoints, private user data), returning 404 to non-privileged users is correct — it prevents enumeration attacks where attackers probe for the existence of resources.

When should I use 202 Accepted?

Return 202 when the request has been accepted for processing but the processing is not yet complete. The classic use case is asynchronous job queuing: you accept an export request, queue a background job, and return 202 with a location URL the client can poll for status. Include a Location header pointing to the job status endpoint. This pattern is preferable to holding the HTTP connection open for long-running operations, which is fragile across proxies, load balancers, and client timeout limits.

What does 422 Unprocessable Entity mean vs 400 Bad Request?

The distinction is syntactic vs semantic validity. 400 Bad Request means the request is structurally malformed — invalid JSON, missing required headers, a URL that does not make sense. 422 Unprocessable Entity means the request is syntactically valid but semantically wrong — valid JSON, but field values that fail business logic validation (a date in the past when a future date is required, a negative quantity, an email address already registered). Many APIs use 400 for both and that is acceptable; 422 provides finer-grained information when clients need to distinguish parse errors from validation errors.

How should I handle rate limiting with 429?

Return 429 with a Retry-After header (seconds until the client can retry) and X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers on every response so clients can track their usage proactively. Include these headers on successful responses too, not only on 429 — clients should back off before hitting the limit, not after. Implement exponential backoff with jitter in your API clients: on receiving a 429, wait the Retry-After duration, then retry with increasing delays on subsequent 429 responses. Format API error responses to make them easier to read when debugging rate limit issues.