Designing Consistent API Error Responses

How to design consistent API error response bodies. Machine-readable codes, RFC 7807 Problem Details, field-level validation errors, and correlation IDs.

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.

RFC 7807 Problem Details fields

  • type a URI that identifies the error type
  • title a short human-readable summary
  • status the HTTP status code
  • detail a human-readable explanation
  • instance a URI identifying the specific occurrence of the problem

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.

Designing Consistent API Error Responses

A consistent error response body is as important as the status code. The HTTP status code tells the client which class of error occurred. The response body tells the client exactly what went wrong, which field caused the failure, and what it should do next. An API where every error condition returns a different body structure forces clients to write multiple parsing branches and creates a higher documentation burden. Consequently, designing a single error schema that covers all 4xx and 5xx conditions reduces client integration complexity substantially. This guide covers the elements of a well-designed error body: machine-readable codes, human-readable messages, field-level validation errors, RFC 7807 Problem Details, and correlation IDs for distributed systems.

Machine-readable vs human-readable error codes

Every API error body should include both a machine-readable code and a human-readable message. The machine-readable code is a string constant that identifies the error type precisely: "validation_error", "resource_not_found", or "rate_limit_exceeded". Clients use this code to branch their error handling logic without parsing English text, which means you can add new error codes without breaking existing client integrations as long as the schema shape stays stable.

Never use the HTTP status code as the only error identifier in the body: returning {"error": 422} forces clients to parse an integer and then look up what 422 means for this specific endpoint. Include a docs URL field pointing to the API documentation for this error type to surface contextual help directly in the error response, so that developers who encounter an unfamiliar code can resolve it without leaving their debugging flow.

Avoid using HTTP status phrases like "Unprocessable Entity" as the human-readable message: they are technical and unhelpful to application developers who may not have the IANA registry memorised. Instead, write messages that describe what the client should do next, such as "The email address is already registered" rather than "Unprocessable Entity", which turns the error into actionable guidance rather than a vocabulary quiz.

Versioning error schemas and correlation IDs

Error schemas evolve over time, and changing the body structure of error responses is a breaking change for existing clients. Version your error schema from the start: include a version field in the error body or use a versioned Content-Type such as application/problem+json; version=2. Without versioning, a seemingly harmless addition like a new optional field can break your clients that perform strict schema validation, which is why production APIs that serve external consumers almost always lock their error schema to a specific version before the first public release.

Correlation IDs

When a client reports an error they cannot reproduce, a correlation ID in the error response is essential for server-side debugging. Generate a unique request ID on every incoming request, include it in the X-Request-ID response header, and include it in the error body as a request_id or trace_id field. The dual placement ensures that the correlation ID survives even if a client library strips custom response headers, and it gives you two independent ways to surface the ID in your own logs and error reports. This redundancy matters in production: response headers are more visible to monitoring tools, while the body field is more visible to client-side exception trackers, so including both ensures the correlation ID reaches whichever system your team uses first during an incident.

Structured logging that records the same correlation ID alongside the full error detail links your client's error report directly to your server log entry. This is especially important in microservice architectures where a single error visible to your client may span multiple downstream services that you need to trace during an incident.

Field-level validation errors

For 422 Unprocessable Content responses, a flat error message is insufficient when multiple fields fail validation simultaneously. When your client receives "Validation failed" without detail, it must fix one field, resubmit, and discover additional failures one at a time. Returning an errors array where each item identifies the failing field path, a machine-readable error code, and a human-readable message lets your client fix all failures in a single round trip.

Use dot-notation or JSON Pointer (RFC 6901) to express nested field paths: "user.address.postalCode" or "/user/address/postalCode" is unambiguous for both humans and your programmatic clients.1 Include the rejected value in the error object when it is safe to do so: seeing "rejected: '2024-01-31'" alongside "error: DATE_TOO_EARLY" is more actionable than the message alone.

Do not include rejected values for password fields, authentication tokens, other sensitive inputs, or any field marked as sensitive in your data model. Returning a rejected password in the response body creates a credential leak if the error response is ever logged, cached, or displayed on a screen that other people can see.

RFC 7807 Problem Details: structure and adoption

RFC 7807 defines a standard HTTP error response schema called Problem Details. The schema uses five fields: type (a URI that identifies the error type), title (a short human-readable summary), status (the HTTP status code), detail (a human-readable explanation), and instance (a URI that identifies the specific occurrence of the problem).2 The Content-Type for Problem Details responses is application/problem+json, which API clients and tooling recognise as a standard error body without custom parsing logic.2

RFC 7807 Problem Details for API errors gives you a standard schema with type, title, status, detail, and instance fields so clients already parsing Problem Details integrate without learning a custom format. Your clients that already support Problem Details from other APIs can integrate with your API's error handling without learning a new format. The type URI does not need to point to a real endpoint, but it should be a stable identifier that uniquely names the error type. A type value of https://errors.example.com/validation-failed paired with an errors extension array follows the RFC's extension mechanism.

Extending Problem Details with custom fields

RFC 7807 allows custom fields alongside the standard five. Adding an errors array with per-field validation details, a requestId field for correlation IDs, or a retryAfter field for rate limit responses extends Problem Details without breaking conformant clients. Conformant clients are required to ignore unknown fields they do not recognise, so your extensions are transparent to your clients that have not implemented them yet. Document your custom extensions in your API specification so your clients know which additional fields to expect on specific error types.

Testing error response contracts with OpenAPI validation tools

Error response contracts are the most frequently untested part of an API. Functional tests typically exercise the happy path and check that 200 responses include the expected data. The 400, 422, 429, and 500 error paths receive less attention, which allows breaking changes to error body structure to ship undetected. Automated contract testing closes this gap by verifying that every documented status code returns a body matching its OpenAPI schema.

Dredd runs your OpenAPI specification against your running server and verifies that your responses match the documented schemas. Dredd generates test cases from the examples in your OpenAPI spec: if you document a 422 example, Dredd sends a request that triggers a 422 and compares the response body to the documented schema.3 Running Dredd in your CI pipeline catches schema mismatches before they reach production and break your client integrations.

Prism for mocking and contract validation

Prism (from Stoplight) can run in proxy mode, sitting between your test clients and your real server, validating both your requests and your responses against your OpenAPI spec in real time. Prism reports schema violations for any response body that does not match the documented schema for its status code.4 Running Prism as part of your integration test suite validates your API's error responses systematically without requiring individual test assertions for each error case.

When to use this

Use this guide when designing or auditing an API's error response contract. Reference it when your team decides on an error body schema, when adding a new error condition, or when standardising inconsistent error responses across multiple endpoints.

Examples

POST /api/users — multiple validation failures

Return 422 with an errors array listing each failing field, its machine-readable code, and a human-readable message. Include a request_id in the body for server-side debugging correlation.

GET /api/orders/999 — resource not found

Return 404 with a body containing error code "resource_not_found", a message, and a request_id. Avoid returning the internal database ID or table name in the message.

POST /api/payments — rate limit exceeded

Return 429 with error code "rate_limit_exceeded", the Retry-After header, and the rate limit window and remaining quota in the response body. This lets the client display an accurate retry countdown to the end user.

Sources
  1. 1.

    P. Bryan, M. Nottingham, and K. Zyp, "JavaScript Object Notation (JSON) Pointer," RFC 6901, IETF, April 2013. https://www.rfc-editor.org/rfc/rfc6901

  2. 2.

    Mark Nottingham, Erik Wilde, and Sanjay Dalal, "Problem Details for HTTP APIs," RFC 9457, IETF, July 2023. https://www.rfc-editor.org/info/rfc9457

  3. 3.

    Apiary, "Dredd — HTTP API Testing Framework," github.com, accessed June 2026. https://github.com/apiaryio/dredd

  4. 4.

    Stoplight, "Prism — OpenAPI Mocking and Proxy Validation," github.com, accessed June 2026. https://github.com/stoplightio/prism

FAQ