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.
Core REST status code decisions
- 201 vs 200 201 Created after a POST/PUT that creates a resource, with a Location header
- 202 vs 200 202 Accepted signals async processing — the work isn't done yet
- 400 vs 422 400 for structurally broken requests, 422 for requests that parse but fail semantic validation
- 401 vs 403 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.
Opens the HTTP Status Code Reference with this section's reference values shown at the top of the tool.
Open in the tool →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 9457 (Problem Details for HTTP APIs, which obsoletes RFC 7807) 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.4
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.5
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.
- 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.
M. Nottingham and R. Wilde, "Problem Details for HTTP APIs," RFC 7807, IETF, March 2016. https://www.rfc-editor.org/rfc/rfc7807.txt
- 3.
Mozilla Developer Network, "HTTP response status codes," developer.mozilla.org, accessed June 2026. https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
- 4.
Mozilla Developer Network, "204 No Content," developer.mozilla.org, accessed October 2026. https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/204
- 5.
Microsoft, "Error Handling with Kiota API Clients," learn.microsoft.com, May 2024. https://learn.microsoft.com/en-us/openapi/kiota/errors
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.
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
Opens the HTTP Status Code Reference with this section's checklist shown at the top of the tool.
Open in the tool →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, now maintained as RFC 9457, which obsoletes the original. 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 is registered in the IANA media type registry and which API clients and tooling recognise as a standard error body without custom parsing logic.3
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.4 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.5 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.
- 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.
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.
IANA, "application/problem+json Media Type Registration," iana.org, accessed October 2026. https://www.iana.org/assignments/media-types/application/problem+json
- 4.
Apiary, "Dredd — HTTP API Testing Framework," github.com, accessed June 2026. https://github.com/apiaryio/dredd
- 5.
Stoplight, "Prism — OpenAPI Mocking and Proxy Validation," github.com, accessed June 2026. https://github.com/stoplightio/prism
RFC 7807 is a good default for new APIs because it provides a standard schema that API tooling and clients already understand. It is not required. If your API already has a consistent error schema that clients depend on, the migration cost of switching to RFC 7807 may outweigh the benefit. The key goal is consistency, not the specific schema.
At minimum: the field path (using dot-notation or JSON Pointer), a machine-readable error code, and a human-readable message. Optionally include the rejected value (for non-sensitive fields) and a link to documentation explaining the validation rule. Keep the structure identical across all validation errors to enable consistent client-side rendering.
Propagate a correlation ID (trace ID) from the entry point through all downstream services. Include the correlation ID in every error response body. When a downstream service fails and the API gateway or BFF returns an error to the client, the correlation ID in the error body links the client-visible error to the failing microservice log entry.
In production, no. Stack traces expose internal file paths, library versions, and application structure that attackers can use. Log the full stack trace internally on the server side and return only the correlation ID to the client. In development and staging environments, returning stack traces speeds up debugging if access is restricted to trusted developers.
Use application/json for standard error responses. If you adopt RFC 7807 Problem Details, use application/problem+json, which is a registered media type that signals to clients and tooling that the body follows the Problem Details schema. Do not return text/plain or text/html for API errors: these force clients to parse unstructured content. CapyToolkit allows you to inspect the exact Content-Type and status headers your API returns, so you can verify that error responses use the correct media type before publishing your OpenAPI spec.
Pick the Right 4xx Code for Your API Response
Picking the right 4xx code for your API response means matching the client-side problem to its precise semantic category: bad syntax, missing credentials, insufficient permissions, or resource absence. The 4xx class covers every situation where the server understood the request but could not or would not fulfill it, and unlike 5xx errors, the fault sits with the client, so retrying the same request without changing it will not help. Yet knowing which 4xx code applies to a given situation requires understanding the semantic distinctions between codes that look superficially similar: 401 vs 403, 400 vs 422, 404 vs 410. What follows walks through the decision rules for each code used most often in REST API design, so you can pick the right one the next time your team debates it.1
4xx codes covered in this guide
- 401 Unauthorized credentials missing or invalid — client should authenticate and retry
- 403 Forbidden credentials valid but the identity lacks permission
- 404 vs 410 404 for absent resources, 410 for deliberately and permanently removed ones
- 409 Conflict request conflicts with current resource state — duplicate key, optimistic lock failure
- 429 Too Many Requests defined in RFC 6585, pairs with a Retry-After header
Opens the HTTP Status Code Reference with this section's checklist shown at the top of the tool.
Open in the tool →Authentication and authorisation codes
Two pairs of codes handle auth failures, and choosing the correct one prevents your clients from wasting time re-authenticating when the real problem is a permissions issue rather than a missing credential. Return 401 Unauthorized when the request lacks credentials or when the credentials provided are invalid: the client should authenticate and retry. Return 403 Forbidden when credentials are present and valid but the authenticated identity lacks permission for this specific operation.
Some APIs return 404 instead of 403 for protected resources to avoid disclosing that the resource exists to an unauthenticated or underprivileged caller. This is a valid security pattern for private data such as user profiles or internal documents, but it creates a more confusing developer experience because the client cannot distinguish between a missing endpoint and a deliberate concealment of access restrictions. 407 Proxy Authentication Required is similar to 401 but applies to proxy authentication, not server authentication: the client must authenticate with the proxy before the request reaches your origin, which matters in corporate environments where all traffic flows through an authenticating forward proxy.
Applying the correct code prevents clients from entering authentication retry loops when the real problem is a permissions configuration error that no amount of re-authentication will resolve. The distinction matters most in multi-tenant applications where different user roles have overlapping but distinct permission boundaries: returning 401 in response to a role-based access denial forces the client to re-authenticate with fresh credentials, when the actual fix requires an administrator to adjust the role assignment rather than the client to present new credentials.2
Resource errors: 404, 405, 409, 410
Four codes cover conditions where the resource state or request method is the problem, and selecting the right one ensures your clients receive actionable information about why their request was rejected. Choosing the wrong code forces clients to guess whether they should change the URI, switch the HTTP method, or resolve a state conflict before retrying, so the distinctions carry real consequences for API usability.
404, 405, and 409
404 Not Found applies when the requested URI maps to no resource in your system, whether because the resource never existed, was deleted, or the client constructed an invalid path from outdated documentation. 405 Method Not Allowed applies when the URI is valid but the HTTP method is not permitted: a POST to a read-only endpoint. The server must include an Allow header listing the permitted methods on a 405 response so the client knows which methods it can retry with. 409 Conflict applies when the request cannot be completed because of a conflict with the current state of the resource: a PUT that attempts to update a resource that has been modified since the client last read it, or a POST that tries to create a resource with a duplicate key.
410 Gone is a permanent version of 404: the resource existed and was deliberately removed by an administrator or through a documented deletion workflow. Search engines deindex a 410 URL faster than a 404, which is why choosing 410 over 404 for removed content accelerates how quickly search results reflect the deletion.3
Rate limiting: 429
429 Too Many Requests is the designated code for rate limiting, defined in RFC 6585 rather than RFC 9110.4 When a client exceeds a request quota, the server returns 429 with an optional Retry-After header indicating how long to wait. The Retry-After header makes 429 a cooperative signal: the client knows exactly when to retry rather than guessing a backoff duration.
Well-behaved clients implement Retry-After inspection as their first response to a 429. Many client libraries and HTTP frameworks do not inspect Retry-After automatically, so developers must implement this behaviour explicitly. Failing to honour Retry-After causes clients to retry too early, which wastes bandwidth and can extend the duration of a rate-limit event by triggering escalating penalties from the server.
A common mistake is returning 503 for rate-limit exhaustion: 503 implies the service is globally unavailable, which is incorrect when only a specific client is being throttled. 429 is semantically precise about the scope of the limit. Returning 503 instead of 429 also misleads monitoring systems into treating a per-client throttle as a global outage, which can trigger unnecessary paging for on-call engineers and distort service health dashboards during routine traffic spikes.
409 Conflict versus 422 for duplicate key errors
Duplicate unique key errors are a common source of 4xx code confusion in REST API design. When a POST creates a resource with a field value that must be unique, and the value already exists, the request conflicts with the current state of the resource. This is a 409 Conflict: the request is structurally valid and semantically valid in isolation, but the current resource state prevents it from completing. Returning 422 for a duplicate key error misclassifies the problem as a semantic validation failure rather than a state conflict.
The distinction matters for client behavior. A 422 tells the client the submitted data is inherently invalid: the client should fix the input and resubmit. A 409 tells the client the submitted data is valid but conflicts with existing state: the client might resolve the conflict by updating the existing resource or by informing the user that the resource already exists. These are different client behaviors, and the status code is supposed to drive the right one.
409 for optimistic locking failures
Optimistic locking failures are another canonical 409 case. When a client reads a resource, modifies it locally, and sends a PUT with an If-Match header containing the ETag it read, the server compares the current ETag with the If-Match value. If another client modified the resource between the read and the write, the ETags do not match. Some APIs prefer 409 here because it carries the semantic meaning of "your update conflicts with a concurrent modification," which is more descriptive than the generic 412 Precondition Failed code.5
Conditional requests and 412 Precondition Failed
412 Precondition Failed is the response to a conditional request where the condition evaluates to false. Conditional requests include an If-Match, If-None-Match, If-Modified-Since, or If-Unmodified-Since header that the server evaluates before processing the request. A PUT with If-Match: "etag-value" tells the server to apply the update only if the resource's current ETag matches. If another client modified the resource first, the ETags do not match and the server returns 412 rather than applying the update.
412 is the foundation of optimistic locking in HTTP APIs. It allows multiple clients to read and modify resources concurrently without explicit locks, using the ETag as a version identifier. The client reads the resource, receives an ETag, modifies the resource locally, and sends the modification with If-Match. If the server returns 412, the client knows another modification happened and must re-read the resource before retrying.
Including the current ETag in a 412 response
A 412 response should include the current ETag in the response headers so the client can immediately compare it to the one it sent. Without the current ETag in the response, the client must make a separate GET request to retrieve it before retrying the conditional update. Setting ETag on the 412 response saves a round trip and gives the client the information it needs to decide whether to re-read and merge or to surface a conflict message to the user.
When to use this
Decide which 4xx code fits here whenever your team debates 400 vs 422 for a validation failure, 401 vs 403 for an access denial, or 404 vs 410 for a deleted resource. Check the rate-limiting section too before you default to 503 for a throttled client.
Examples
POST /api/users — JSON body is missing the required email field
Return 400 Bad Request. A missing required field is a structural problem that a JSON schema validator would catch. Reserve 422 for semantically invalid content that parses correctly.
GET /api/orders/999 — authenticated user attempts to view another user's order
Return 403 Forbidden (or 404 if you want to conceal the resource exists). The credentials are valid but the identity lacks permission. Do not return 401: re-authenticating will not help.
POST /api/products — product SKU already exists in the database
Return 409 Conflict. The request is well-formed and authenticated but conflicts with existing resource state. Include the conflicting field and value in the response body.
- 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.
Mozilla Developer Network, "HTTP response status codes," developer.mozilla.org, accessed June 2026. https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
- 3.
Mozilla Developer Network, "410 Gone," developer.mozilla.org, accessed October 2026. https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/410
- 4.
M. Nottingham and R. Tarreau, "Additional HTTP Status Codes," RFC 6585, IETF, April 2012. https://www.rfc-editor.org/rfc/rfc6585.txt
- 5.
Microsoft, "HttpStatusCode Enum (System.Net)," learn.microsoft.com, accessed October 2026. https://learn.microsoft.com/en-us/dotnet/api/system.net.httpstatuscode?view=net-10.0
Malformed JSON in the request body is the most frequent cause. Other common causes: wrong Content-Type header (sending JSON without application/json), missing required fields that fail schema validation, and invalid data types (a string where a number is expected). The 400 body should specify which part of the request failed.
Use 409 when the request cannot complete because it conflicts with the current resource state, not because the client made a structural error. Typical cases: creating a resource with a duplicate unique key, updating a resource that was modified by another request since the client last fetched it (optimistic locking failure), or transitioning a resource to an incompatible state.
No. 405 Method Not Allowed means the HTTP method is not supported for this URI, regardless of who is making the request: a POST to a read-only endpoint returns 405 for all clients. 403 Forbidden means the HTTP method is supported but this specific client lacks permission to use it on this resource.
429 is for per-client rate limiting: the specific client has exceeded its quota. 503 is for global server overload: the service cannot handle any more requests regardless of which client is asking. If you are shedding load globally, use 503 with Retry-After. If you are throttling a specific API key or IP address, use 429.
Yes, and you should. Validation failures caused by invalid client input are client errors even if the validation runs on the server. Return 400 for structural failures and 422 for semantic failures. Reserve 5xx codes for conditions the server did not anticipate and that are not caused by the content of the client request. CapyToolkit allows you to inspect and verify the exact status headers your endpoints return, which makes it easier to catch mismatched codes during development.