Overview

Pick the status code by intent: the class (2xx, 3xx, 4xx, 5xx) tells the client what to do next and the code adds the reason. Names follow RFC 9110. Redirect handling for search is in redirects; framework error handling is in fastapi.

Success and redirects

Use the most specific success code, and 307 or 308 when the method must be kept.

CodeUse for
200 OKSuccess with a body.
201 CreatedNew resource; send a Location header.
202 AcceptedAsync job queued; Location points to a status URL.
204 No ContentSuccess with no body (DELETE, or a PUT that does not echo).
206 Partial ContentRange request honored.
301 / 308Permanent move. 308 keeps the method; clients may rewrite POST to GET on 301.
302 / 307Temporary move. 307 keeps the method; avoid 302 for non-GET.
303 See OtherAfter a POST, GET another URL (Post-Redirect-Get).
304 Not ModifiedCached copy valid after If-None-Match or If-Modified-Since.

Client errors

Be specific; clients debug from the code.

CodeUse for
400 Bad RequestUnparseable request or missing required header or query param.
401 UnauthorizedNot authenticated; send WWW-Authenticate.
403 ForbiddenAuthenticated but not allowed.
404 Not FoundUnknown resource or path.
405 Method Not AllowedWrong verb; send Allow.
408 Request TimeoutClient was too slow to send the request.
409 ConflictDuplicate insert or version conflict.
410 GoneRemoved on purpose; caches and crawlers stop retrying.
412 / 428If-Match precondition failed / required (optimistic locking).
413 Content Too LargeBody over the limit.
414 URI Too LongMove the data to a body.
415 Unsupported Media TypeContent-Type not handled.
422 Unprocessable ContentBody parsed but failed validation.
429 Too Many RequestsRate limit; send Retry-After.
431Request headers too large.
451Blocked for legal reasons.

Server errors

Return 5xx only for failures the client cannot fix; they should alert someone.

CodeUse for
500 Internal Server ErrorUnhandled exception.
501 Not ImplementedMethod not supported at all.
502 Bad GatewayUpstream returned an invalid response.
503 Service UnavailableOverload or maintenance; send Retry-After.
504 Gateway TimeoutUpstream did not answer in time.

Pick the status by action

Map each handler action to one status and one companion header.

ActionStatus
Created a resource201 + Location.
Updated; no body / body returned204 / 200.
Deleted204.
Validation failed on a JSON body422 with field errors.
Not logged in / lacks permission401 / 403.
Resource missing / retired404 / 410.
Duplicate or version conflict409.
Rate limited / maintenance window429 or 503, with Retry-After.
Async job accepted202 + Location.

Error body

RFC 9457 (Problem Details) defines application/problem+json. Keep status equal to the HTTP status and add extension members such as an errors array.

{
  "type": "https://example.com/errors/validation",
  "title": "Validation failed",
  "status": 422,
  "detail": "email must be a valid address",
  "instance": "/users"
}

Gotchas

Avoid these; each misleads clients, caches, or monitoring.

GotchaFix
200 with {"error": ...} hides failures from clients, caches, and monitoring.Return the real 4xx or 5xx.
401 means unauthenticated, despite its name.Use 403 for a known user without permission.
Browsers and many clients turn POST into GET after a 301 or 302.Use 303 for Post-Redirect-Get, 307 or 308 to keep the method.
Omitting Retry-After on 429 or 503 forces blind retries.Always send it.
Stack traces in error bodies leak internals.Log them; return an id in instance.