← Back to W30D1

HTTP Status Code Reference

Every code you'll use, return, or debug — with API-shaped guidance

The Five Classes

Every HTTP status code has three digits. The first tells you the kind of response, which is enough to route your error handling before you even parse the body.

1xx — Informational

"I'm still working on it." Rare outside of WebSockets, protocol upgrades, and streaming hints.

2xx — Success

"Got your request. Here's what happened." 200 OK is the default; the others signal how it succeeded.

3xx — Redirection

"The thing moved. Look somewhere else." Common with moved URLs, caching, and auth flows.

4xx — Client error

"You messed up." The client sent something wrong — bad format, missing auth, nonexistent resource.

5xx — Server error

"I messed up." The server blew up handling a valid-looking request. These should page someone.

No codes match that search. Try a different term or reset the filter.
1xx Informational Provisional responses — the final response will follow
100Continue
Client sent Expect: 100-continue; server is ready for the body.
In the wild: you almost never return this yourself — your framework handles it.
101Switching Protocols
Server is switching to the protocol the client requested (e.g., HTTP → WebSocket).
In the wild: see this when upgrading to WebSocket for streaming chat UIs.
103Early Hints
Preload hints sent before the final response. Lets clients fetch resources while the server is still working.
In the wild: rare. Some CDNs and modern frontends use it for latency optimization.
2xx Success The request was received and accepted
200OK⭐ common
Standard success. The body contains what the client asked for.
For ML APIs: use for POST /v1/predict when the inference ran and returned a result — even if confidence is low.
201Created⭐ common
Resource was successfully created. Response should include the new resource in the body or the URL in a Location header.
For ML APIs: returning a newly enqueued job (POST /jobs) or a saved prediction record.
202Accepted⭐ common
Request is valid and accepted, but processing hasn't finished yet. Usually returns a job ID for polling.
For ML APIs: long-running inference, fine-tuning jobs, batch pipelines. Pair with GET /jobs/{id}.
204No Content
Success, but there's nothing to return in the body. Common for DELETE and some PUT responses.
Don't send a body with 204. Some clients treat it as protocol error.
206Partial Content
Client requested a byte range; server is returning just that range. Used for resumable downloads and streaming.
In the wild: large file downloads and video seeking. Rare for inference APIs.
3xx Redirection Further action needed to complete the request
301Moved Permanently
Resource has a new URL, forever. Clients and search engines should update their bookmarks.
For APIs: use sparingly. Renaming endpoints is usually better handled with a version bump.
302Found
Temporary redirect. The resource is at a different URL right now but may come back.
For APIs: OAuth flows and login redirects often use this.
304Not Modified⭐ common
Client's cached copy is still valid. No body needed. Triggered by If-None-Match or If-Modified-Since headers.
For ML APIs: use for metadata or model-list endpoints that rarely change. Save bandwidth.
307Temporary Redirect
Like 302, but the request method must be preserved (a POST stays a POST).
For APIs: if you must redirect a POST, this is the safe one. 302 was historically ambiguous.
308Permanent Redirect
Like 301, but method-preserving. Prefer this over 301 for API moves.
For APIs: the modern way to handle permanent path migrations. Pairs well with version bumps.
4xx Client Error The request is wrong — the client needs to fix it
400Bad Request⭐ common
Request is malformed. Missing required fields, invalid JSON, or failed business-rule validation.
For ML APIs: text empty after trimming, image above size limit, unsupported language. Include a detail field explaining what's wrong.
401Unauthorized⭐ common
Authentication is missing or invalid. The client doesn't know who they are.
Misnamed: this is about authentication, not authorization. Use 403 for "we know who you are, but you can't do this."
403Forbidden⭐ common
Client is authenticated, but not allowed to do this operation. Permission problem.
For ML APIs: free-tier user hitting a paid model, user hitting another user's data.
404Not Found⭐ common
The URL doesn't map to a resource. Either it never existed, or it was deleted.
Privacy tip: returning 404 instead of 403 for resources the user doesn't own can leak existence. Pick your stance.
405Method Not Allowed
URL exists, but not for that HTTP method (e.g., POST on a GET-only endpoint).
For APIs: always include an Allow header listing the methods that are supported.
406Not Acceptable
Server can't produce a response matching the client's Accept header.
For APIs: pure JSON APIs rarely need this. Comes up with content negotiation (JSON vs. XML vs. MessagePack).
408Request Timeout
Server got tired waiting for the client to finish sending the request.
In the wild: slow uploads over flaky connections. Clients usually retry.
409Conflict
Request conflicts with current server state. The classic example: writing stale data over newer data.
For ML APIs: updating a model that's being fine-tuned, uploading training data with a duplicate ID.
410Gone
Resource used to exist but is now permanently removed. Stronger signal than 404.
For APIs: return this for sunset endpoints — tells clients "this is never coming back, stop retrying."
413Payload Too Large⭐ common
Request body is bigger than the server is willing to process.
For ML APIs: text over max length, image over upload limit, batch with too many items.
415Unsupported Media Type
Client sent a Content-Type the endpoint doesn't accept.
For APIs: common when clients send form data to JSON-only endpoints.
422Unprocessable Entity⭐ common
Request is syntactically valid (good JSON) but semantically wrong (wrong field types, missing required fields).
For FastAPI: this is what Pydantic validators return. Default for tonight's POST /v1/predict when fields don't match the schema.
429Too Many Requests⭐ common
Client is hitting the API too fast. Rate limit exceeded.
For APIs: always include a Retry-After header with seconds (or an HTTP date). The class's own /llm/ endpoint returns this at 20 req/min.
5xx Server Error Something broke on the server side. These should page someone.
500Internal Server Error⭐ common
Something blew up inside the server. Unhandled exception, bug, logic failure.
Don't leak stack traces in the response body. Log them server-side, return a generic message with a request ID.
501Not Implemented
Server doesn't support the functionality required (e.g., an HTTP method it doesn't recognize).
In the wild: rare. Usually either the framework handles it or you'd return 405.
502Bad Gateway⭐ common
Your server is acting as a proxy and got a garbage response from the upstream it forwarded to.
For ML APIs: if you're proxying to a model server and the model server crashed, this is what your client sees.
503Service Unavailable⭐ common
Server is intentionally not serving right now — overloaded, down for maintenance, or a dependency is unreachable.
For ML APIs: model unloaded, GPU out of memory, backend queue full. Include Retry-After.
504Gateway Timeout
Server is a proxy and the upstream didn't respond in time.
For ML APIs: slow model inference on a cold GPU. Tune your proxy's read timeout or add async job pattern (202).
507Insufficient Storage
Server can't store the representation needed to complete the request.
In the wild: full disks, out-of-quota cloud buckets. Your framework may surface this as a 500 unless you handle it.

"Which Code Should I Return?" — Decisions

Common API scenarios and the code most reviewers will expect.

User sent JSON with a missing required field.
422 Unprocessable Entity (schema validation) or 400 Bad Request (general malformed). FastAPI picks 422 by default for Pydantic errors.
User sent a text field that's 10,000 characters long when the limit is 5,000.
413 Payload Too Large. Don't wait until your model chokes.
User isn't logged in.
401 Unauthorized. Include a WWW-Authenticate header if you want to be rigorous.
User is logged in but on a free plan hitting a paid model.
403 Forbidden. They know who they are; they just can't do this.
Your model predicts "positive" with low confidence. Should that be an error?
200 OK. The inference succeeded — confidence is a signal for the caller, not an error. Returning 4xx confuses monitoring.
The model service crashed and your proxy can't reach it.
503 Service Unavailable. Pair with Retry-After. Monitoring should page on 5xx rates.
Request is valid but will take 3 minutes to process.
202 Accepted with a job ID. Client polls GET /jobs/{id} — each poll returns 200 with the current state.
User hitting 30 requests per second on a 10/second endpoint.
429 Too Many Requests with Retry-After.
You renamed POST /predict to POST /v1/predict and want old clients to be safe.
→ Bump a version prefix instead. If you must redirect, use 308 Permanent Redirect (method-preserving) — never 301/302 for POSTs.

Anti-Patterns to Avoid

❌ 200 OK with an error body

Returning {"error": "..."} with status 200 breaks every monitoring tool, load balancer, and retry logic. They look at status codes, not your body.

✅ 4xx or 5xx with a structured error body

Match the status code to the problem. Put details in detail or an errors array. Clients can still read the body, but the status tells the whole stack what happened.

❌ Leaking 500 stack traces

Production clients get your Python traceback, including file paths and library versions. Attackers love this.

✅ Generic 500 message + request ID

{"detail": "internal error", "request_id": "req_01H..."}. Log the traceback server-side. Support searches logs by request ID.

❌ 404 for auth failures

Hiding a 401 behind a 404 "for security" confuses legitimate clients and their error handlers. Real attackers probe both anyway.

✅ Use the right code, scrub the body

Return 401 or 403 with a minimal message. If you truly want to hide existence, return 404 only for GET on specific resources — and be consistent.

❌ Inventing your own codes

Status 230? 451 (oh wait, that's real)? Made-up codes break everything that classifies by number range.

✅ Use standard codes, customize the body

The status code is for the HTTP stack. The body is where your error taxonomy lives: {"code": "MODEL_OVERLOADED", "detail": "..."}.