Beyond the Familiar Status Codes
Beyond 200, 404 and 500, HTTP defines dozens of status codes with precise, useful semantics — codes like 429 Too Many Requests (rate limiting), 451 Unavailable For Legal Reasons (legal takedowns), 308 Permanent Redirect (a method-preserving 301), 103 Early Hints (preload hints during a slow backend), 421 Misdirected Request (wrong server on a coalesced HTTP/2 connection), 226 IM Used (delta-encoded responses), 511 Network Authentication Required (captive portals), and the famous joke code 418 I'm a Teapot. Each one lets a server tell an intelligent client exactly what happened instead of collapsing every outcome into a generic success or error.
That is the summary an AI overview gives you. What it can't give you is the working knowledge below: which RFC defines each code, exactly when to reach for it, and the gotchas that make the difference between a self-documenting API and one that lies to its clients with a blanket 200.
The lesser-known codes at a glance
Start here. These eight are the genuinely underused codes worth knowing — the ones that carry meaning no common code can. Every RFC listed is a stable, checkable standard.
| Code | Name | RFC | What it means | When to use it |
|---|---|---|---|---|
| 103 | Early Hints | RFC 8297 | Informational hints sent before the final response | Push Link: preload/preconnect headers while the backend is still working, to speed up page load |
| 226 | IM Used | RFC 3229 | The server applied an instance manipulation (usually a delta) | Return just the diff against a version the client already has, in reply to an A-IM request |
| 308 | Permanent Redirect | RFC 7538 | Permanent move that preserves the method and body | A permanent redirect where a POST must stay a POST (the method-safe 301) |
| 418 | I'm a Teapot | RFC 2324 / 7168 | A teapot refuses to brew coffee (April Fools' joke) | Easter eggs and playful "impossible request" responses — never for real logic |
| 421 | Misdirected Request | RFC 9110 | The request reached a server that can't serve this authority | HTTP/2/3 connection coalescing hit the wrong host; the client should retry on a new connection |
| 429 | Too Many Requests | RFC 6585 | The client is being rate limited | Throttling and abuse prevention; always pair with a Retry-After header |
| 451 | Unavailable For Legal Reasons | RFC 7725 | Content withheld due to a legal demand | Court orders, government blocks, DMCA/GDPR takedowns — distinct from 403/404 |
| 511 | Network Authentication Required | RFC 6585 | You must authenticate to the network to get access | Captive portals (hotel/airport Wi-Fi) intercepting traffic before you log in |
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": "john@example.com"}
201 Created
Location: /users/123
Content-Type: application/json
{"id": 123, "name": "John", "email": "john@example.com"}
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)
The genuinely lesser-known codes
These are the codes from the table above that you'll rarely see in a tutorial but which solve real problems. Each maps to a stable RFC.
103 Early Hints (RFC 8297)
Meaning: "Here are some resources to start fetching while I finish the real response."
103 is an informational (1xx) response. The server can send it — with Link headers carrying preload and preconnect hints — before the final 200, during the time it spends querying databases or rendering. The browser uses that head start to fetch CSS, fonts, and scripts, which can meaningfully cut Largest Contentful Paint on slow backends.
GET /article HTTP/1.1
103 Early Hints
Link: </style.css>; rel=preload; as=style
Link: </hero.jpg>; rel=preload; as=image
200 OK
Content-Type: text/html
[the full page]
Chrome and Cloudflare support 103. Because 1xx responses are advisory, older clients that don't understand it simply ignore it.
226 IM Used (RFC 3229)
Meaning: "I applied an instance manipulation — here's the delta, not the whole thing."
When a client sends an A-IM (Accept-Instance-Manipulation) header offering to accept a delta against a version it already holds, a server that complies answers 226 IM Used and returns just the difference. It's the standards-based way to say "here's the diff," saving bandwidth on large, incrementally-changing resources. It's rare in the wild, but it's the correct code when you build delta encoding into an API.
308 Permanent Redirect (RFC 7538)
Meaning: "This resource has permanently moved — and keep your method."
308 is the missing piece of the redirect family. A classic 301 is permanent but permits clients to rewrite a POST into a GET when they follow it; 308 is permanent and forbids that rewrite. It's to 301 what 307 is to 302.
Reach for 308 when a permanently-moved endpoint must keep receiving POSTs — an API version cutover, for instance — where silently turning them into GETs would break clients.
418 I'm a Teapot (RFC 2324 / RFC 7168)
Meaning: "I'm a teapot; I can't brew coffee."
This comes from the Hyper Text Coffee Pot Control Protocol, a 1998 April Fools' RFC. It's not part of standard HTTP, and RFC 7168 later reserved the number so no serious code could ever claim it. Frameworks keep it around as an Easter egg and some APIs use it as a playful "you can't do that here" response — but never for real control flow.
421 Misdirected Request (RFC 9110)
Meaning: "This request reached a server that can't answer for the host you asked about."
421 shows up in HTTP/2 and HTTP/3. To save handshakes, clients coalesce connections — reusing one TLS connection for multiple hostnames that share a certificate. If that reused connection lands on a server that doesn't actually serve the requested authority, it replies 421, and the client should retry the request on a fresh, dedicated connection. It's niche, but if you run services behind shared certificates you will eventually meet it.
429 Too Many Requests (RFC 6585)
Meaning: "You've exceeded the rate limit; slow down."
Covered above in the 4xx section — the key detail is to always pair it with a Retry-After header (and ideally RateLimit headers) so well-behaved clients back off instead of hammering you. See the deep dive in HTTP status codes for rate limiting and throttling.
451 Unavailable For Legal Reasons (RFC 7725)
Meaning: "Content withheld because of a legal demand."
451 exists to distinguish legal unavailability from a missing resource (404) or an application-level denial (403). Use it for court orders, government blocks, DMCA takedowns, and GDPR-driven geo-restrictions. The response can carry a Link header (rel="blocked-by") identifying who demanded the block — a transparency feature no other code offers. The number is a deliberate homage to Ray Bradbury's Fahrenheit 451.
Real examples:
- GitHub returns 451 when blocking repositories under DMCA takedowns
- CDNs and hosts use it for content geo-blocked to comply with local law
511 Network Authentication Required (RFC 6585)
Meaning: "You must authenticate to the network before you can reach anything."
511 is the captive-portal code. When hotel, airport, or coffee-shop Wi-Fi intercepts your traffic to force you through a login page, the correct signal is 511 — it tells software that the block comes from the network, not the origin server, so clients don't cache the portal page in place of the real site. Many portals still return a 200 with an HTML login page instead, which is exactly the confusion 511 was standardized to fix.
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.