HTTP Status Codes: The Complete Developer Guide

HTTP status codes are the language servers use to tell clients what happened with a request. Getting them wrong — returning 200 for errors, 404 for authorization failures, or 500 for validation issues — breaks your API clients, monitoring, and debugging. This guide explains every category, the nuances between similar codes, and exactly when to use each one.

Use our interactive HTTP Status Codes Reference tool to quickly look up any code while you work.

1. How HTTP Status Codes Work

Every HTTP response begins with a status line containing a 3-digit status code and a reason phrase. The first digit defines the class of response:

1xx
Informational
Request received, processing in progress
2xx
Success
Request was received, understood, accepted
3xx
Redirection
Further action needed to complete the request
4xx
Client Error
Request contains bad syntax or cannot be fulfilled
5xx
Server Error
Server failed to fulfil a valid request

The client (browser, mobile app, API consumer) uses the status code to decide how to behave: retry the request, follow a redirect, show an error page, or parse the response body. Status codes are not just documentation — they drive real logic in every HTTP client on the planet.

2. 2xx Success Codes — Choosing the Right One

Most developers default to 200 for everything successful but the 2xx range has several more precise codes that communicate important semantics.

200 OK — The General Success

Use 200 for successful GET, PUT, PATCH, and POST requests that return data. The response body contains the requested resource or the result of the operation. If there is nothing to return, use 204 instead.

GET  /api/users/42       → 200 OK  (return the user object)
PUT  /api/users/42       → 200 OK  (return the updated user)
POST /api/search         → 200 OK  (return search results)

201 Created — New Resource

Return 201 when a POST (or sometimes PUT) creates a new resource. Always include a Location header pointing to the new resource's URL. The body can contain the created resource or be empty.

POST /api/users
→ 201 Created
→ Location: /api/users/43
→ Body: { "id": 43, "name": "Alice" }

202 Accepted — Async Processing

Use 202 when the request has been accepted for processing but processing is not yet complete. Common for long-running jobs, email sending, and background tasks. Return a job ID or polling URL in the body.

POST /api/reports/generate
→ 202 Accepted
→ Body: { "jobId": "abc123", "statusUrl": "/api/jobs/abc123" }

204 No Content — Success With No Body

Return 204 when the operation succeeded but there is nothing to return. The canonical case is a successful DELETE. Never return 200 with an empty body — use 204 for that.

DELETE /api/users/42  → 204 No Content  (no body)
PUT    /api/settings  → 204 No Content  (settings saved, nothing to return)
Rule of thumb: If you created something, use 201. If you return data, use 200. If the operation succeeded but there is nothing to return, use 204. Use 202 for async work.

3. 3xx Redirect Codes — Permanent vs Temporary

Redirect codes tell the client to make another request to a different URL. Choosing between them incorrectly has real consequences for SEO and API behavior.

301 Moved Permanently vs 308 Permanent Redirect

Both indicate a permanent move. The critical difference is method preservation:

  • 301 — browsers typically downgrade POST to GET on redirect. Search engines transfer link equity. Cached by browsers.
  • 308 — the HTTP method is preserved (POST stays POST). Preferred for API endpoints that change URL.

302 Found vs 307 Temporary Redirect

Same distinction: 302 permits method downgrade, 307 preserves the method. Use 307 in APIs. Use 302 for temporary web page redirects where method doesn't matter.

303 See Other — Post/Redirect/Get Pattern

After a successful POST (e.g. form submission), return 303 with a Location header pointing to a confirmation page. The browser will GET that page. This prevents the "resubmit form on back button" problem.

POST /contact-form
→ 303 See Other
→ Location: /thank-you

304 Not Modified — Efficient Caching

Returned when a client sends a conditional GET (with If-None-Match or If-Modified-Since) and the resource has not changed. No body is returned. Browsers use the cached copy. Critical for performance on static assets.

SEO note: 301 and 308 pass link equity; 302 and 307 do not. For permanent URL migrations, always use 301. Never leave a 302 in place for months — it will not pass SEO value.

4. 4xx Client Error Codes — The Developer's Most Important Category

4xx errors tell the client it did something wrong. Picking the right code communicates exactly what the client needs to fix, making your API dramatically easier to debug.

400 Bad Request — Malformed Syntax

The request cannot be parsed or is structurally invalid. Use 400 for: invalid JSON, wrong content type, missing required fields in the wrong structural sense, malformed query parameters.

POST /api/users  (Content-Type: text/plain instead of application/json)
→ 400 Bad Request
→ Body: { "error": "Content-Type must be application/json" }

401 Unauthorized — Not Authenticated

The client has not authenticated. The name is misleading — it is really “unauthenticated.” Include a WWW-Authenticate header indicating how to authenticate. The client should retry with credentials.

GET /api/profile  (no Authorization header)
→ 401 Unauthorized
→ WWW-Authenticate: Bearer realm="api"

403 Forbidden — Not Authorized

The client is authenticated but lacks permission. Re-authenticating will not help. Return 403 when a logged-in user tries to access a resource they do not own or a role they do not have.

DELETE /api/users/99  (authenticated as user 42)
→ 403 Forbidden  (you can only delete your own account)

404 Not Found — Resource Does Not Exist

The resource does not exist at this URL. Note: some APIs return 404 for authorization failures to avoid leaking the existence of resources. This is valid security practice for sensitive resources (e.g. medical records).

405 Method Not Allowed

The HTTP method is not supported on this endpoint. Always include an Allow header listing the permitted methods.

DELETE /api/products  (DELETE not allowed — only GET and POST are)
→ 405 Method Not Allowed
→ Allow: GET, POST

409 Conflict — State Conflicts

The request conflicts with the current resource state. Common cases: trying to create a resource that already exists (duplicate email), optimistic locking conflicts (resource was modified since you last read it), trying to cancel an order that is already shipped.

410 Gone — Permanently Deleted

Unlike 404, a 410 explicitly says the resource existed but has been permanently removed with no forwarding address. Search engines de-index 410 pages faster than 404s. Use 410 when you intentionally retire a URL and want search engines to remove it.

422 Unprocessable Entity — Validation Errors

The request is syntactically correct but semantically invalid. This is the right code for business rule validation failures. Return structured error details in the body.

POST /api/bookings
Body: { "checkIn": "2026-04-15", "checkOut": "2026-04-10" }
→ 422 Unprocessable Entity
→ Body: {
    "errors": [
      { "field": "checkOut", "message": "Check-out must be after check-in" }
    ]
  }

429 Too Many Requests — Rate Limiting

The client has exceeded the allowed request rate. Include Retry-After (seconds or HTTP date) and optionally X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers.

→ 429 Too Many Requests
→ Retry-After: 30
→ X-RateLimit-Limit: 100
→ X-RateLimit-Remaining: 0
→ X-RateLimit-Reset: 1743000000
The most common mistake: Using 400 for everything. A 400 tells the client “fix your request structure.” A 422 says “your data is invalid.” A 403 says “you don't have permission.” Each requires a different response from the client.

5. 5xx Server Error Codes — The Server's Fault

5xx errors indicate the server failed to process a valid request. The client did nothing wrong — these are your bugs, infrastructure failures, or capacity issues.

500 Internal Server Error — Unhandled Exceptions

The catch-all for unexpected server failures. Return 500 for unhandled exceptions. Never expose stack traces or internal details in the error body — log them server-side and return a generic message to the client.

// ❌ Dangerous — leaks implementation details
→ 500 { "error": "MySQLi error: Table 'users' doesn't exist in /app/db.php line 42" }

// ✅ Safe
→ 500 { "error": "An unexpected error occurred. Reference: err_abc123" }

502 Bad Gateway — Upstream Failure

Your server is acting as a proxy (Nginx, API gateway, load balancer) and received an invalid response from an upstream service. Common when a backend container crashes or restarts. The fix is upstream, not in the client.

503 Service Unavailable — Temporary Outage

The server is temporarily unable to handle requests. Use during planned maintenance or under extreme load. Include a Retry-After header. Return a useful maintenance page for web traffic.

→ 503 Service Unavailable
→ Retry-After: Fri, 27 Mar 2026 02:00:00 GMT

504 Gateway Timeout — Upstream Too Slow

Your proxy did not receive a timely response from an upstream server. Different from 502 — the upstream did not crash, it just timed out. Common causes: slow database queries, unoptimized external API calls, network latency spikes.

Observability tip: 5xx rates are the most important metric to alert on. A sudden spike in 500s means your application is broken. A spike in 502s/504s means an upstream dependency is failing. Treat them as separate alert rules.

6. Designing REST API Error Responses

The status code alone is not enough. A well-designed error response body helps API clients (and developers) understand exactly what went wrong and how to fix it.

Recommended Error Body Structure

{
  "error": {
    "code": "VALIDATION_FAILED",      // machine-readable code
    "message": "Request validation failed", // human-readable summary
    "details": [                      // array of specific issues
      {
        "field": "email",
        "message": "Must be a valid email address",
        "value": "not-an-email"
      }
    ],
    "requestId": "req_7f3k2p9x"       // for log correlation
  }
}

Key Principles

  • Always use the correct HTTP status code — never 200 for errors
  • Include a machine-readable error code alongside the human message — clients can switch on it without parsing strings
  • Include a request ID to correlate client-reported errors with server logs
  • Never leak internals in 5xx responses (stack traces, SQL, file paths)
  • Be consistent across all endpoints — use the same error structure everywhere
  • For 422 validation errors, include which fields failed and why

7. Quick Decision Guide

A fast reference for the most common API design decisions:

Situation Correct Code
GET request, resource found, data returned200 OK
POST request created a new resource201 Created
Async job queued, not yet processed202 Accepted
DELETE succeeded, nothing to return204 No Content
URL has permanently moved301 Moved Permanently
After POST form, redirect to confirmation303 See Other
Request body cannot be parsed400 Bad Request
No valid credentials provided401 Unauthorized
Authenticated but no permission403 Forbidden
Resource does not exist404 Not Found
Resource existed but permanently deleted410 Gone
Data passes parsing but fails validation422 Unprocessable Entity
Client is sending too many requests429 Too Many Requests
Unhandled exception in your code500 Internal Server Error
Upstream service returned invalid response502 Bad Gateway
Planned maintenance or overloaded503 Service Unavailable
Upstream service did not respond in time504 Gateway Timeout

Frequently Asked Questions

What is the difference between 200 OK and 201 Created?

200 OK is the general success response — data is returned. 201 Created means a new resource was created. 201 should include a Location header pointing to the new resource's URL.

When should an API return 400 vs 422?

400 when the request is structurally unreadable (invalid JSON, wrong content type). 422 when the request is valid JSON but the values are logically wrong (date in the past, out-of-range number, business rule violation).

Why does everyone say to never return 200 for an error?

Returning 200 {"status":"error"} breaks HTTP semantics. Monitoring tools, load balancers, CDNs, and API clients all rely on status codes to detect failures automatically. A 200 with an error body fools all of them.

What is the difference between 401 and 403?

401 Unauthorized = not authenticated (not logged in). 403 Forbidden = authenticated but no permission. Re-authenticating fixes a 401. Re-authenticating will not fix a 403.

Should a DELETE request return 200 or 204?

Use 204 No Content if deletion succeeded and there is nothing to return. Use 200 OK if you are returning a confirmation body. Never return 200 with an empty body — use 204 for that.

What HTTP status code should I use for rate limiting?

429 Too Many Requests. Include a Retry-After header. Implement exponential backoff on the client when receiving 429s.