HTTP Status Codes in REST API Design

How to choose the right HTTP status codes for REST APIs. 2xx, 4xx, 5xx selection rules, validation errors, and consistent error response bodies.

ZERO UPLOAD · ALL LOCAL
  1. Type a code number (e.g. 404) to find codes by number only, or a word (e.g. "timeout", "rate limit") to search names and detail content.
  2. Use the category pills (1xx–5xx) to browse codes by class when not searching.
  3. Click Details on any card to expand causes and resolution steps in a full-width panel below the row.
  4. Click Copy Markdown on an expanded card to copy a ready-to-paste summary for Jira tickets, GitHub issues, or Slack.

Core REST status code decisions

  • 201 Created after a POST/PUT that creates a resource, with a Location header
  • 202 Accepted signals async processing — the work isn't done yet
  • 400 for structurally broken requests, 422 for requests that parse but fail semantic validation
  • 401 when credentials are missing, 403 when credentials are valid but forbidden

A single body schema for all 4xx and 5xx responses, like RFC 7807 Problem Details, simplifies client code more than getting every individual code choice right.

1XX INFORMATIONAL — 4 codes

100 Continue
RFC 9110
101 Switching Protocols
RFC 9110
102 Processing
RFC 2518
103 Early Hints
RFC 8297

2XX SUCCESS — 10 codes

200 OK
RFC 9110
201 Created
RFC 9110
202 Accepted
RFC 9110
203 Non-Authoritative Information
RFC 9110
204 No Content
RFC 9110
205 Reset Content
RFC 9110
206 Partial Content
RFC 9110
207 Multi-Status
RFC 4918
208 Already Reported
RFC 5842
226 IM Used
RFC 3229

3XX REDIRECTION — 8 codes

300 Multiple Choices
RFC 9110
301 Moved Permanently
RFC 9110
302 Found
RFC 9110
303 See Other
RFC 9110
304 Not Modified
RFC 9110
305 Use Proxy
RFC 9110
307 Temporary Redirect
RFC 9110
308 Permanent Redirect
RFC 9110

4XX CLIENT ERROR — 35 codes

400 Bad Request
RFC 9110
401 Unauthorized
RFC 9110
402 Payment Required
RFC 9110
403 Forbidden
RFC 9110
404 Not Found
RFC 9110
405 Method Not Allowed
RFC 9110
406 Not Acceptable
RFC 9110
407 Proxy Authentication Required
RFC 9110
408 Request Timeout
RFC 9110
409 Conflict
RFC 9110
410 Gone
RFC 9110
411 Length Required
RFC 9110
412 Precondition Failed
RFC 9110
413 Content Too Large
RFC 9110
414 URI Too Long
RFC 9110
415 Unsupported Media Type
RFC 9110
416 Range Not Satisfiable
RFC 9110
417 Expectation Failed
RFC 9110
418 I'm a Teapot (Unofficial)
RFC 2324
420 Enhance Your Calm (Unofficial)
Twitter
421 Misdirected Request
RFC 9110
422 Unprocessable Content
RFC 9110
423 Locked
RFC 4918
424 Failed Dependency
RFC 4918
425 Too Early
RFC 8470
426 Upgrade Required
RFC 9110
428 Precondition Required
RFC 6585
429 Too Many Requests
RFC 6585
431 Request Header Fields Too Large
RFC 6585
444 No Response (Unofficial)
nginx
451 Unavailable For Legal Reasons
RFC 7725
494 Request Header Too Large (Unofficial)
nginx
495 SSL Certificate Error (Unofficial)
nginx
496 SSL Certificate Required (Unofficial)
nginx
499 Client Closed Request (Unofficial)
nginx

5XX SERVER ERROR — 19 codes

500 Internal Server Error
RFC 9110
501 Not Implemented
RFC 9110
502 Bad Gateway
RFC 9110
503 Service Unavailable
RFC 9110
504 Gateway Timeout
RFC 9110
505 HTTP Version Not Supported
RFC 9110
506 Variant Also Negotiates
RFC 2295
507 Insufficient Storage
RFC 4918
508 Loop Detected
RFC 5842
510 Not Extended
RFC 2774
511 Network Authentication Required
RFC 6585
520 Web Server Returns an Unknown Error (Unofficial)
Cloudflare
521 Web Server Is Down (Unofficial)
Cloudflare
522 Connection Timed Out (Unofficial)
Cloudflare
523 Origin Is Unreachable (Unofficial)
Cloudflare
524 A Timeout Occurred (Unofficial)
Cloudflare
525 SSL Handshake Failed (Unofficial)
Cloudflare
526 Invalid SSL Certificate (Unofficial)
Cloudflare
527 Railgun Listener to Origin Error (Unofficial)
Cloudflare
No codes match your search.

HTTP Status Codes in REST API Design

Choosing the right HTTP status code is part of your API contract. A well-chosen code tells the client exactly what happened, which party is responsible, and whether retrying will help. Returning 200 for every response and burying success or failure in a JSON body is a common anti-pattern that breaks HTTP-aware infrastructure: CDNs, load balancers, and API gateways all make decisions based on status codes.1 Consequently, misusing them adds hidden complexity. The codes exist to carry semantic meaning that generic fields in a JSON body cannot replicate. This guide covers the selection rules that matter most in REST API design: 2xx, 4xx, and 5xx, validation errors, and consistent error body conventions.

2xx: success codes and their differences

Inside the 2xx class, four codes dominate REST API design. 200 OK is the baseline for successful GET, PUT, and PATCH responses. 201 Created follows a POST or PUT that produces a new resource; include a Location header pointing to the new resource URI. 204 No Content is the right choice for a DELETE or a PUT that returns no body.

202 Accepted signals that the request was received but processing is asynchronous: the server has not yet completed the action. Returning 200 for an async request misleads the client into thinking the work is done. Returning the correct 2xx code communicates the action outcome precisely and lets clients branch logic correctly. When your team is deciding between 200, 201, and 204 for a specific endpoint, picking the right HTTP status code for an operation keeps the contract unambiguous for every client that integrates with it.

4xx: client error selection rules

Four decisions cover most 4xx cases and mastering them will resolve the majority of status code selection questions your team encounters when designing or reviewing REST API endpoints.2 Use 400 Bad Request for structurally broken requests: malformed JSON, wrong Content-Type, or a missing required field that prevents the request from being parsed. Use 422 Unprocessable Content for requests that parse correctly but fail semantic validation, such as an end date before a start date or a quantity below the configured minimum.

The 401 vs 403 split

The 401 vs 403 decision trips up many teams because the distinction is subtle but consequential for how clients respond to the error and whether they prompt the user to authenticate or inform them that access is denied. Return 401 when the request lacks credentials and you want the client to authenticate before retrying. Return 403 when credentials are present but the action is forbidden for that identity. Returning 404 instead of 403 is valid when you want to conceal that a resource exists, such as a private user profile, but document this convention in your API specification so clients do not mistake the 404 for a missing endpoint. Pick one convention and apply it consistently across your API surface.

Consistent error response bodies

The status code signals the class of error; the response body gives the detail. A minimal but consistent error body includes at least three fields: a machine-readable error code string (e.g., "validation_failed"), a human-readable message, and optionally an array of field-level errors for validation responses so the client can present each error next to the relevant input field.

Returning a different body structure for 400 vs 422 vs 500 forces clients to write three separate error-handling branches, which increases the size of every client codebase that integrates with your API and makes it harder to add new error types in the future. A single body schema for all 4xx and 5xx responses simplifies client code and API documentation. Many teams adopt RFC 7807 (Problem Details for HTTP APIs) as a standard schema: it defines type, title, status, detail, and instance fields that cover most error cases without inventing a custom format.3

Idempotency and status codes for PUT and DELETE

Idempotent HTTP methods produce the same resource state when called multiple times with the same input. PUT and DELETE are both idempotent by definition in RFC 9110.1 A PUT that creates a resource on first call and has no effect on subsequent calls with the same input should return 201 Created on first call and 200 OK (or 204 No Content) on subsequent calls. This distinction matters for clients that implement retry logic: a PUT that returns 201 twice signals a duplicate creation problem rather than idempotent behavior.

DELETE idempotency creates a common design question: what should a DELETE return when the resource does not exist? Two conventions exist. The first returns 404 Not Found because the client requested deletion of a resource that is not there. The second returns 204 No Content because the desired outcome (the resource not existing) is already achieved. RFC 9110 describes DELETE as idempotent but does not prescribe which code to return for a non-existent resource. Pick one convention and apply it consistently across all DELETE endpoints in your API.

Using 200 versus 204 after a successful PUT

A successful PUT can return either 200 OK with the updated representation in the body, or 204 No Content with an empty body. Return 200 when the server modifies the resource beyond what the client sent (adding timestamps, normalizing fields, or computing derived values), so the client can see the final state without a follow-up GET. Return 204 when the server applies the PUT body exactly as sent and the client does not need to see the updated representation.

Documenting status codes in OpenAPI 3.x

OpenAPI 3.x requires every route to document all possible response status codes under the responses object. Each listed code maps to a response schema that describes the body structure. Documenting all 4xx and 5xx codes your endpoint can return allows clients to generate accurate error-handling code and lets API testing tools verify that your implementation matches the specification.

The default response key covers all status codes not explicitly listed. A default entry with an error schema catches any 5xx or undocumented 4xx response without requiring you to list every possible code. Most OpenAPI tooling renders default as "Any other status code" in generated documentation. Use explicit codes for expected errors (400, 401, 403, 404, 422, 429) and default as a catch-all for unexpected failures.

The 422 response schema in OpenAPI

For endpoints that perform business rule validation, document the 422 response with a schema that matches your field-level error body. Include an errors array with field, code, and message properties in the 422 schema. When clients generate SDK code from your OpenAPI spec, the 422 schema drives the error type they use to surface field-level validation failures in their type system. A 422 response that returns a different body structure than what the OpenAPI spec describes breaks generated clients silently, without any immediate runtime error.

Validating your OpenAPI document against your actual API responses catches schema drift before it breaks client integrations. A contract test that sends known inputs to each endpoint and verifies the response body matches the documented 422 schema ensures your implementation stays aligned with the specification. This is especially valuable after framework upgrades or when you add new validation rules, because the generated SDK code will silently fail to parse the new error structure until a client reports the issue. Automated contract testing tools can compare your OpenAPI spec against live API responses and flag any mismatch, keeping your documentation trustworthy for every team that depends on it.

When to use this

Use this guide when designing a new REST API endpoint or auditing an existing one. Reference it when your team debates whether to return 400 vs 422, 401 vs 403, or 200 vs 204 for a specific operation, so the decision is grounded in HTTP semantics rather than preference.

Examples

POST /orders — successful creation

Return 201 Created with a Location header pointing to the new order resource. Do not return 200 OK for resource creation: 201 signals that a new URI was created.

DELETE /orders/123 — successful deletion

Return 204 No Content when the deletion succeeded and there is no body to return. Return 200 OK only if you include a body summarising the deleted resource.

POST /orders — semantic validation failure (end date before start date)

Return 422 Unprocessable Content with a field-level errors array. The request was valid JSON with all required fields present, so 400 Bad Request would be incorrect.

Sources
  1. 1.

    R. Fielding, Ed., M. Nottingham, Ed., and J. Reschke, Ed., "HTTP Semantics," RFC 9110, IETF, June 2022. https://www.rfc-editor.org/rfc/rfc9110.txt

  2. 2.

    M. Nottingham and R. Wilde, "Problem Details for HTTP APIs," RFC 7807, IETF, March 2016. https://www.rfc-editor.org/rfc/rfc7807.txt

  3. 3.

    Mozilla Developer Network, "HTTP response status codes," developer.mozilla.org, accessed June 2026. https://developer.mozilla.org/en-US/docs/Web/HTTP/Status

FAQ