Pick the Right 4xx Client Error Code for Your API
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
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.
Rate limiting: 429
429 Too Many Requests is the designated code for rate limiting, defined in RFC 6585 rather than RFC 9110.3 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.
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.
M. Nottingham and R. Tarreau, "Additional HTTP Status Codes," RFC 6585, IETF, April 2012. https://www.rfc-editor.org/rfc/rfc6585.txt