X Xerobit

HTTP 4xx Client Error Codes — 400, 401, 403, 404, 409, 422, 429 Explained

HTTP 4xx status codes indicate client errors. Here's when to use 400 Bad Request vs 422 Unprocessable Entity, the difference between 401 and 403, what 409 Conflict means, and...

Mian Ali Khalid · · 5 min read
Use the tool
HTTP Status Codes
Full HTTP status code reference with explanations and when to use each.
Open HTTP Status Codes →

HTTP 4xx codes tell clients the request failed because of something on their end. Choosing the right 4xx code helps API consumers understand what to fix without guessing.

Use the HTTP Status Codes Reference to quickly look up any status code.

400 Bad Request

The request is syntactically malformed — the server can’t process it:

400 — Use when:
- Request body is invalid JSON
- Required field is missing
- Field type is wrong (string instead of number)
- Content-Type header is missing or wrong
POST /api/users
Content-Type: application/json

{"email": "alice" malformed JSON, missing closing brace

HTTP/1.1 400 Bad Request
{"error": {"code": "BAD_REQUEST", "message": "Invalid JSON body"}}

401 Unauthorized

The request requires authentication and none was provided (or the credentials are invalid):

401 — Use when:
- No Authorization header
- Bearer token is expired
- API key is invalid
- Basic auth credentials are wrong
GET /api/profile

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="api"
{"error": {"code": "UNAUTHENTICATED", "message": "Authentication required"}}

The WWW-Authenticate header tells the client what auth scheme to use.

403 Forbidden

The client is authenticated but lacks permission for the resource:

403 — Use when:
- User is logged in but can't access another user's data
- API token has insufficient scope
- IP is blocked
- Resource requires a different role (admin only)
GET /api/users/999/profile   ← user 123 trying to access user 999

HTTP/1.1 403 Forbidden
{"error": {"code": "FORBIDDEN", "message": "You can only access your own profile"}}

401 vs 403: 401 = “I don’t know who you are.” 403 = “I know who you are, but you can’t do this.”

404 Not Found

The resource doesn’t exist:

404 — Use when:
- /api/users/999 and user 999 doesn't exist
- Route path doesn't exist in the API

Note: Some APIs return 404 even for “resource exists but you can’t see it” to avoid leaking information. This is intentional and valid — choose based on your security requirements.

405 Method Not Allowed

The HTTP verb isn’t supported for this endpoint:

DELETE /api/articles/123   ← endpoint is read-only

HTTP/1.1 405 Method Not Allowed
Allow: GET, POST
{"error": {"code": "METHOD_NOT_ALLOWED", "message": "DELETE is not allowed on this endpoint"}}

Include the Allow header listing valid methods.

409 Conflict

The request conflicts with the current state of the resource:

409 — Use when:
- Creating a user with an email that already exists
- Trying to check out a branch that's already checked out
- Optimistic concurrency: update fails because resource changed since last read
- Trying to publish something already published
POST /api/users
{"email": "alice@example.com"}   ← email already registered

HTTP/1.1 409 Conflict
{"error": {"code": "DUPLICATE_EMAIL", "message": "An account with this email already exists"}}

410 Gone

The resource existed but has been permanently deleted:

410 — Use when:
- User deleted their account and URL should be tombstoned
- An API endpoint has been permanently removed
- A blog post was deleted and should be deindexed by search engines

Unlike 404, which is ambiguous, 410 explicitly signals “this was here, it’s gone, don’t come back.”

422 Unprocessable Entity

The request is well-formed but semantically invalid:

422 — Use when:
- Request body parses fine but field values fail validation
- Email format is invalid
- Date is in the past when a future date is required
- Referenced foreign key doesn't exist
POST /api/users
{"email": "not-an-email", "age": -5}

HTTP/1.1 422 Unprocessable Entity
{
  "error": {
    "code": "VALIDATION_ERROR",
    "details": [
      {"field": "email", "message": "Must be a valid email"},
      {"field": "age", "message": "Must be a positive number"}
    ]
  }
}

400 vs 422: 400 = can’t parse the request at all. 422 = parsed fine, values are wrong.

429 Too Many Requests

The client has exceeded the rate limit:

HTTP/1.1 429 Too Many Requests
Retry-After: 60
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1715000000

{"error": {"code": "RATE_LIMIT_EXCEEDED", "message": "Too many requests. Try again in 60 seconds."}}

Always include Retry-After so clients know when to retry. Use Retry-After: <seconds> or an HTTP-date.

Quick reference

CodeNameWhen to use
400Bad RequestMalformed syntax, wrong content-type
401UnauthorizedNot authenticated
403ForbiddenAuthenticated but not allowed
404Not FoundResource doesn’t exist
405Method Not AllowedWrong HTTP verb
409ConflictState conflict (duplicate, optimistic lock)
410GonePermanently deleted resource
422Unprocessable EntityValidation failure
429Too Many RequestsRate limit exceeded

Related posts

Related tool

HTTP Status Codes

Full HTTP status code reference with explanations and when to use each.

Written by Mian Ali Khalid. Part of the Dev Productivity pillar.