Beyond the Familiar Status Codes
Every web developer knows HTTP 200 (OK), 404 (Not Found), and 500 (Server Error). But the HTTP specification defines dozens more status codes that provide granular semantics for different situations. Understanding lesser-known status codes helps you build more precise APIs, communicate better with clients, and handle edge cases more elegantly.
These codes aren't just academic—they serve real purposes when your API needs to communicate specific situations to intelligent clients. Some are underutilized but powerful; others are designed for specific scenarios where they shine.
1xx Informational Responses
100 Continue
Meaning: "I've received your request headers; send your body"
Use case: Large uploads or POST requests with bodies
Mechanism:
Client sends: Headers + "Expect: 100-continue"
Server responds: 100 Continue
Client sends: Request body
Server responds: 200 OK or appropriate code
Why useful: Prevents wasting bandwidth sending large bodies if server will reject anyway
Example:
Request: POST /upload HTTP/1.1
Expect: 100-continue
Content-Length: 1000000
Server: 100 Continue
Client: [sends 1MB file]
Server: 201 Created
101 Switching Protocols
Meaning: "Switching to requested protocol (WebSocket, HTTP/2, etc.)"
Use case: Upgrading connections
Example:
Client: GET / HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Server: 101 Switching Protocols
Upgrade: websocket
[Now using WebSocket protocol on same connection]
Why useful: Allows upgrading connection type without reconnecting
2xx Success (Beyond 200)
201 Created
Meaning: "Resource created successfully; see Location header"
Use case: POST requests that create resources
Best practice:
POST /users HTTP/1.1
Content-Type: application/json
{"name": "John", "email": "[email protected]"}
201 Created
Location: /users/123
Content-Type: application/json
{"id": 123, "name": "John", "email": "[email protected]"}
Why useful: Clients know the POST succeeded and can find the new resource
Common mistake: Using 200 OK for creation instead of 201. This loses semantic information.
202 Accepted
Meaning: "Request accepted for processing, but not complete"
Use case: Asynchronous processing
Example:
POST /reports/generate HTTP/1.1
202 Accepted
Location: /reports/generate-job/789
{"status": "processing", "job_id": "789"}
Client can check /reports/generate-job/789 for status later.
Why useful: Tells client the request is valid and being processed; don't wait for completion
Common use cases:
- Long-running batch jobs
- Video encoding
- Complex calculations
- Email sending
- Background task processing
204 No Content
Meaning: "Success, but no response body"
Use case: DELETE requests, successful PATCH/PUT with no data to return
Example:
DELETE /users/123 HTTP/1.1
204 No Content
Why useful: Tells client deletion succeeded without sending redundant data
Important: Browser treats 204 differently; doesn't follow redirects or open new page
206 Partial Content
Meaning: "Returning partial resource; range specified in headers"
Use case: Range requests (resumable downloads, video streaming)
Example:
GET /video.mp4 HTTP/1.1
Range: bytes=1000-9999
206 Partial Content
Content-Range: bytes 1000-9999/100000
Content-Length: 9000
[9000 bytes of content]
Why useful: Enables:
- Resumable downloads (pause and resume)
- Video streaming (serve video in chunks)
- Efficient bandwidth usage
- Streaming large files
3xx Redirection
301 Moved Permanently
Meaning: "Resource permanently moved; update your bookmarks"
Use case: Permanent URL changes
Example:
GET /old-path HTTP/1.1
301 Moved Permanently
Location: /new-path
Important: Browsers and clients will automatically follow. Future requests should use new URL.
SEO impact: Search engines will update index to new URL
Difference from 302: 301 is permanent; clients should remember and update bookmarks
304 Not Modified
Meaning: "Resource unchanged since you last requested it"
Use case: Conditional requests with ETag or Last-Modified headers
Example:
GET /data HTTP/1.1
If-None-Match: "abc123"
304 Not Modified
[No response body; browser uses cached version]
Why useful:
- Saves bandwidth (no body sent)
- Reduces server load
- Improves perceived performance
- Enables efficient caching
Client behavior: Browser uses cached copy instead of re-downloading
307 Temporary Redirect
Meaning: "Temporarily moved; preserve request method"
Difference from 302:
- 302: Browser may change POST to GET when following redirect
- 307: Browser must preserve original method
Example:
POST /form HTTP/1.1
307 Temporary Redirect
Location: /form-handler
[Browser re-POSTs to /form-handler instead of changing to GET]
Why useful: When method preservation is critical (preventing accidental GET conversion)
4xx Client Errors
400 Bad Request
Meaning: "Request malformed; server can't understand"
Use case: Invalid JSON, missing required fields, bad syntax
Example:
POST /api/data HTTP/1.1
Content-Type: application/json
{invalid json
400 Bad Request
{"error": "Invalid JSON in request body"}
403 Forbidden
Meaning: "You're authenticated, but not authorized for this resource"
Difference from 401:
- 401: Not authenticated (no credentials provided)
- 403: Authenticated but lacking permission
Example:
GET /admin/users HTTP/1.1
Authorization: Bearer valid_token
403 Forbidden
{"error": "User role 'member' cannot access admin resources"}
Why useful: Tells client "prove you're allowed, not that you need to log in"
409 Conflict
Meaning: "Request conflicts with current state"
Use case: Concurrent modifications, constraint violations, version conflicts
Example:
PUT /document/123 HTTP/1.1
Content-Type: application/json
If-Match: "v1"
{"content": "new version"}
409 Conflict
{"error": "Document was modified by another user (version v2); cannot update"}
Why useful: Tells client to fetch current version and retry
Common scenarios:
- Optimistic locking conflicts
- Duplicate unique constraints
- Cannot delete due to dependencies
- Version conflicts in collaborative editing
410 Gone
Meaning: "Resource permanently deleted; don't ask again"
Difference from 404:
- 404: Resource doesn't exist (maybe never did)
- 410: Resource existed but is permanently deleted
Example:
GET /discontinued-product/456 HTTP/1.1
410 Gone
{"error": "This product has been discontinued"}
Why useful:
- Search engines understand not to index again
- Clients won't keep retrying
- Clear signal of permanent deletion vs. temporary unavailability
422 Unprocessable Entity
Meaning: "Request format correct, but content invalid"
Difference from 400:
- 400: Malformed syntax
- 422: Valid syntax but semantic validation failed
Example:
POST /users HTTP/1.1
Content-Type: application/json
{"name": "John", "email": "invalid-email"}
422 Unprocessable Entity
{"error": "Invalid email format", "field": "email"}
Why useful: Tells client the format was fine; the data doesn't meet business rules
Common use: Form validation errors, business logic violations
429 Too Many Requests
Meaning: "Rate limit exceeded; retry after specified time"
Use case: Rate limiting, abuse prevention
Example:
GET /api/search HTTP/1.1
429 Too Many Requests
Retry-After: 60
{"error": "Rate limit exceeded; retry after 60 seconds"}
Why useful:
- Informs clients they're being rate-limited
- Provides retry guidance
- Prevents thundering herd of retries
- Standard rate-limiting response
5xx Server Errors
502 Bad Gateway
Meaning: "Upstream server returned invalid response"
When to use: When your server receives bad response from upstream service
Example:
Client requests: GET /data
Your API server forwards to microservice
Microservice returns corrupted response
502 Bad Gateway
{"error": "Upstream service returned invalid response"}
Why useful: Tells client the problem is upstream, not your API
503 Service Unavailable
Meaning: "Temporarily unavailable; try again later"
Use case: Maintenance, overload, dependencies down
Example:
GET /api/data HTTP/1.1
503 Service Unavailable
Retry-After: 300
{"error": "Service undergoing maintenance; try again in 5 minutes"}
Why useful:
- Clients know to retry
- Search engines understand it's temporary
- Can specify retry-after guidance
504 Gateway Timeout
Meaning: "Upstream server took too long to respond"
Use case: Upstream service is slow or unresponsive
Example:
Client requests through API gateway
Upstream service doesn't respond in time
API gateway returns:
504 Gateway Timeout
{"error": "Upstream service did not respond in time"}
Why useful: Distinguishes between your service being down (503) vs. upstream being slow (504)
Specialized Use Cases
307/308 for Safe Retries
Use these for POST-safe redirects:
307: Temporary redirect (POST preserved)
308: Permanent redirect (POST preserved)
Most APIs use 307 for safety when redirecting POST requests.
418 I'm a Teapot
Meaning: "I'm a teapot; I can't brew coffee"
Actual use: This is an April Fools RFC (RFC 2324). Some APIs use it for fun error responses.
Real-world use: Easter egg responses, humorously indicating impossible requests
451 Unavailable For Legal Reasons
Meaning: "Content unavailable due to legal restrictions"
Use case: Content blocked for legal/compliance reasons
Real example:
- GitHub uses this when blocking content due to DMCA takedowns
- Services in countries with censorship
HTTP Status Code Strategy
For APIs
Follow RESTful conventions:
- 2xx: Success
- 3xx: Redirect/condition
- 4xx: Client error (client's fault)
- 5xx: Server error (server's fault)
For common operations:
POST (create): 201 Created (or 202 Accepted for async)
GET (read): 200 OK (or 304 Not Modified if cached)
PUT (update): 200 OK (or 204 No Content)
PATCH (update): 200 OK (or 204 No Content)
DELETE: 204 No Content (or 200 with details)
For Web Applications
Browser requests:
- 200: Success
- 301: Permanent redirect (update bookmarks)
- 302: Temporary redirect (follow location)
- 304: Use cache
- 400: Malformed request
- 401: Need authentication
- 403: Forbidden (lacks permission)
- 404: Not found
- 500: Internal error
- 503: Unavailable
Error Response Body Format
Consistent error responses:
{
"error": "Descriptive message",
"code": "SPECIFIC_ERROR_CODE",
"details": {
"field": "email",
"issue": "Invalid format"
},
"request_id": "abc-123"
}
Common Mistakes
Mistake 1: Using 200 for Everything
Wrong:
POST /users → 200 OK
DELETE /users/123 → 200 OK
Invalid JSON → 200 OK
[Everything returns 200]
Right:
POST /users → 201 Created
DELETE /users/123 → 204 No Content
Invalid JSON → 400 Bad Request
Mistake 2: Not Using 202 for Async
Wrong:
Long-running job request hangs for 30 seconds
Finally returns 200 OK
Right:
Request immediately returns 202 Accepted
Includes job tracking URL
Client polls for status
Mistake 3: Confusing 401 and 403
Wrong:
Authenticated user lacks permission: 401 Unauthorized
[Confusing because user IS authorized to be there]
Right:
Not authenticated: 401 Unauthorized
Authenticated but no permission: 403 Forbidden
Conclusion
HTTP provides a rich set of status codes beyond the common few. Lesser-known codes like 202 (Accepted), 206 (Partial Content), 409 (Conflict), and 422 (Unprocessable Entity) provide precise semantics that help API clients handle situations intelligently.
Using appropriate status codes:
- Improves API clarity and usability
- Helps automated clients react correctly
- Provides semantic meaning beyond response body
- Follows HTTP standards and conventions
- Reduces ambiguity in error conditions
Invest time understanding HTTP status codes fully. Your APIs and their clients will be better for it.

