
HTTP Status Codes: The Complete Developer Reference (2026)
π· Thomas Jensen / PexelsHTTP Status Codes: The Complete Developer Reference (2026)
A practical guide to all HTTP status codes β what they mean, when they occur, and how to handle them in your APIs and web apps.
HTTP Status Codes Are Not Just Trivia
Every HTTP response carries a three-digit status code. Most developers have them memorized up to a point β 200 means good, 404 means missing, 500 means something broke. But the full picture is more interesting and more useful than that.
Understanding HTTP status codes well means writing better APIs, debugging faster, and making smarter decisions about redirects, caching, and error handling. This guide is a practical reference, not an RFC dump. We'll cover what matters, why it matters, and how to apply it.
If you want an interactive reference while you're working, check out the free HTTP Status Codes tool β look up any code instantly with descriptions and usage notes.
The Five Categories: A Mental Model
HTTP status codes are grouped into five classes. The first digit tells you which category you're in:
| Range | Category | The vibe |
|---|---|---|
| 1xx | Informational | "Hold on, still working..." |
| 2xx | Success | "Here you go." |
| 3xx | Redirection | "It's over there." |
| 4xx | Client Error | "You messed up." |
| 5xx | Server Error | "We messed up." |
This taxonomy is worth internalizing. When you see an unfamiliar status code, the first digit alone tells you whether to look at your request or the server.
1xx: Informational
These are rarely seen in application code but worth knowing:
100 Continue β The server received the request headers and the client should proceed with the body. Used to avoid sending large bodies when the server might reject the request anyway (e.g., based on auth headers).
101 Switching Protocols β The server is switching from HTTP to another protocol, typically WebSocket. You'll see this in the browser's network tab when a WebSocket connection is established.
103 Early Hints β A newer one, useful for performance. The server sends resource hints (like preload links) before the final response, letting the browser start fetching assets earlier.
2xx: Success Codes
These are what you want. But they're not all the same.
200 OK
The default success. The request worked, here's the response body. Use it for GET requests, and generally for POST/PUT when you're returning the updated resource.
curl -I https://api.example.com/users/42
# HTTP/2 200
# content-type: application/json
201 Created
Used after a resource is successfully created β typically in response to a POST. Best practice is to include a Location header pointing to the new resource.
curl -X POST https://api.example.com/users \
-H "Content-Type: application/json" \
-d '{"name": "Alice", "email": "alice@example.com"}'
# HTTP/2 201
# Location: /users/43
A lot of APIs return 200 for everything. That works, but it's lazy. Returning 201 with a Location header gives clients a cleaner contract β they know a new thing exists and where to find it.
204 No Content
The request succeeded, but there's nothing to return. Perfect for DELETE operations, or PUT/PATCH when you don't need to send back the updated resource.
curl -X DELETE https://api.example.com/users/42
# HTTP/2 204
# (empty body)
Never return a body with 204 β clients don't expect one and some will ignore it. If you need to return data after deletion (like a count of remaining items), use 200 instead.
202 Accepted
The request was accepted for processing, but processing isn't complete yet. This is the right code for async operations β you've queued the job, but it might take a while.
{
"status": "accepted",
"jobId": "job_abc123",
"checkUrl": "/jobs/job_abc123"
}
206 Partial Content
Used when serving a range of a resource (video streaming, download resumption). The client requests a range via the Range header, and the server returns the slice with 206.
3xx: Redirects
Redirects are where developers make the most costly mistakes β not because they're hard to understand, but because the SEO and caching implications are easy to miss.
301 Moved Permanently
The resource has permanently moved to a new URL. The browser will cache this redirect indefinitely. Search engines transfer link equity (PageRank) to the new URL.
Use 301 when:
- You're migrating a domain
- Changing URL structure permanently
- Consolidating duplicate pages
Be careful: Once a 301 is cached, it's very hard to undo. Browsers may cache it for months. If you're not 100% sure the redirect is permanent, use 302.
302 Found
Temporary redirect. The browser follows it but doesn't cache it permanently. SEO value stays with the original URL.
Use 302 for:
- A/B testing (temporarily showing a variant)
- Maintenance pages
- Login redirects (redirect to login, then back)
307 Temporary Redirect
Functionally similar to 302, but with a stricter guarantee: the method and body must not change. A POST redirected via 307 must be re-sent as POST to the new URL. Useful in REST APIs where method preservation matters.
308 Permanent Redirect
Like 301, but method-preserving. The permanent version of 307. Less common but useful for APIs that need to maintain request semantics across URL changes.
304 Not Modified
The client sent a conditional GET (with If-None-Match or If-Modified-Since), and the cached version is still valid. The server sends no body β just the status. This is how HTTP caching works efficiently.
4xx: Client Errors
The client sent something wrong. Here's where most API debugging happens.
400 Bad Request
A catch-all for malformed requests β missing required fields, invalid JSON, type mismatches. Be as specific as possible in the response body.
{
"error": "validation_failed",
"details": [
{ "field": "email", "message": "must be a valid email address" },
{ "field": "age", "message": "must be a positive integer" }
]
}
401 Unauthorized
The request requires authentication and the client hasn't provided it (or provided invalid credentials). The response should include a WWW-Authenticate header indicating how to authenticate.
Despite the name "Unauthorized," this code is really about authentication, not authorization.
403 Forbidden
The client is authenticated but doesn't have permission to access the resource. The server knows who you are; it just won't let you in.
This distinction matters:
- Getting someone else's private data β 403
- Accessing the API without a token β 401
- Accessing an endpoint your subscription doesn't cover β 403
404 Not Found
The resource doesn't exist. One nuance: some APIs return 404 when the user doesn't have permission to see a resource, to avoid revealing its existence. This is legitimate for security-sensitive resources (like user accounts that shouldn't be enumerated).
405 Method Not Allowed
The endpoint exists, but not for the method you used. A GET on an endpoint that only accepts POST, for example. Always include the Allow header listing valid methods.
409 Conflict
The request conflicts with the current state of the resource. Classic use cases: trying to create a user with an email that already exists, or updating a resource with a stale version (optimistic locking failure).
410 Gone
Like 404, but definitively β the resource existed once and is permanently deleted. Search engines will deindex 410 pages faster than 404s. Useful for content that you've intentionally removed.
422 Unprocessable Entity
The request is syntactically valid (parseable JSON, correct content type) but semantically invalid β it violates business rules. Many modern APIs prefer 422 over 400 for validation errors because it's more precise.
The difference between 400 and 422 is subtle but real:
- 400: "I can't even parse what you sent me"
- 422: "I parsed it fine, but this doesn't make sense"
429 Too Many Requests
You've been rate-limited. The response should include Retry-After (seconds to wait) and ideally X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers.
curl https://api.example.com/data
# HTTP/2 429
# retry-after: 30
# x-ratelimit-limit: 100
# x-ratelimit-remaining: 0
# x-ratelimit-reset: 1711180800
418 I'm a Teapot
Defined in RFC 2324 as an April Fools' joke (the Hyper Text Coffee Pot Control Protocol). The server refuses to brew coffee because it is a teapot. Somehow it made it into real browser implementations and became a beloved easter egg. Google returns it for certain requests. It's the internet's most beloved useless standard.
5xx: Server Errors
Something went wrong on the server side. The client didn't do anything wrong.
500 Internal Server Error
The generic "something broke" code. Usually means an unhandled exception. Log everything, never expose raw stack traces in responses, and alert on these in production.
502 Bad Gateway
The server was acting as a gateway or proxy and received an invalid response from an upstream server. Common with nginx + Node.js setups when the Node process crashes or times out.
503 Service Unavailable
The server is temporarily unable to handle the request β overloaded, or down for maintenance. Should include a Retry-After header. Your load balancer typically returns this when all backend instances are unhealthy.
504 Gateway Timeout
The gateway didn't get a timely response from the upstream server. Classic symptom of a slow database query, an external API that's timing out, or a Lambda function that's hitting its execution limit.
507 Insufficient Storage
The server can't store the representation needed to complete the request. Mostly relevant for WebDAV (file storage APIs) or upload endpoints with quota limits.
HTTP Status Codes in REST API Design
Here's a practical cheat sheet for REST API design decisions:
GET /users β 200 (list), 404 (collection doesn't exist β rarely used for lists)
GET /users/ β 200 (found), 404 (not found), 403 (forbidden)
POST /users β 201 (created), 400/422 (validation error), 409 (conflict)
PUT /users/ β 200 (updated with body), 204 (updated no body), 404, 409
PATCH /users/ β 200 or 204, 400/422, 404
DELETE /users/ β 204 (deleted), 404 (not found), 409 (can't delete β has dependencies)
Be consistent
The worst APIs are inconsistent ones. Pick a convention for validation errors (400 or 422), pick whether to return the resource after updates (200 or 204), and stick to it everywhere. Document it clearly.
Don't return 200 for everything
Some APIs return HTTP 200 with a body like {"success": false, "error": "not found"}. This breaks client libraries, makes monitoring harder, and ignores a perfectly good standard. Use the right status codes.
Provide useful error bodies
A 400 response with no body is useless. At minimum, return an error code and message:
{
"error": "INVALID_EMAIL",
"message": "The email address provided is not valid.",
"field": "email"
}
Consistent error shapes let client developers write generic error handlers instead of parsing error strings.
Use 404 vs 403 carefully
For sensitive resources, it's sometimes better to return 404 even when the resource exists but the user can't access it. This prevents information leakage β an attacker can't determine whether a private account exists by getting a 403 instead of a 404.
Debugging with Status Codes
When you're debugging an API issue, start with the status code:
Got a 4xx? The problem is in the request. Check headers, body, authentication, content type.
Got a 5xx? The problem is on the server. Check logs, upstream dependencies, resource limits.
Got a 200 but wrong data? Now you're in application-logic territory β the transport layer is fine.
A quick curl with -I (head only) or -v (verbose) tells you exactly what's happening:
# Just check the status code and headers
curl -I https://api.example.com/endpoint
# Full request/response including headers
curl -v https://api.example.com/endpoint
# With auth header
curl -v -H "Authorization: Bearer your-token" https://api.example.com/users/me
Quick Reference
Looking up a specific code while you're working? The HTTP Status Codes tool at ToolPal gives you instant descriptions, common causes, and usage guidance for every standard code. It's faster than opening an RFC.
| Code | Name | When to use |
|---|---|---|
| 200 | OK | Standard success |
| 201 | Created | Resource created (POST) |
| 204 | No Content | Success, no body (DELETE) |
| 301 | Moved Permanently | Permanent redirect |
| 302 | Found | Temporary redirect |
| 400 | Bad Request | Malformed request |
| 401 | Unauthorized | Not authenticated |
| 403 | Forbidden | Authenticated but not allowed |
| 404 | Not Found | Resource doesn't exist |
| 409 | Conflict | State conflict |
| 422 | Unprocessable Entity | Semantic validation error |
| 429 | Too Many Requests | Rate limited |
| 500 | Internal Server Error | Unhandled exception |
| 503 | Service Unavailable | Server overloaded/down |
Final Thoughts
HTTP status codes are a communication protocol between client and server developers. Using them correctly is a form of professional courtesy β it makes everyone's job easier, from the frontend developer consuming your API to the on-call engineer debugging a production incident at 2am.
The codes aren't complicated once you internalize the five categories. Memorize the common ones, look up the rest, and most importantly: be consistent and be specific. An API that uses status codes well is significantly easier to build on top of than one that returns 200 for everything with error details buried in the body.