Overview
Use this card when writing an endpoint and deciding what to return. It maps the action the handler performed to one status code and the header that should travel with it. For the meaning of every individual code, see http-status-codes. Framework-level error handling for Python APIs is in fastapi.
Pick the status from the action the endpoint performed
The most common API decisions in one place.
| Action | Status |
|---|---|
| Created a resource | 201 with Location: header. |
| Updated a resource (no body returned) | 204. |
| Updated a resource (body returned) | 200. |
| Deleted a resource | 204 (or 200 with confirmation body). |
| Validation failed on JSON body | 422 with field errors. |
| Missing required header or query param | 400. |
| User not authenticated | 401 with WWW-Authenticate: header. |
| Authenticated user lacks permission | 403. |
| Resource missing | 404 (or 410 if it was deleted on purpose). |
| Duplicate insert / version conflict | 409. |
| Rate limit hit | 429 with Retry-After:. |
| Async job accepted | 202 with Location: to a status URL. |
| Maintenance window | 503 with Retry-After:. |
Avoid the common status code gotchas
200 OKwith{"error": "..."}in the body is wrong. The status code is part of the API.401 Unauthorizedis really “unauthenticated.” Use403when the user is known but lacks permission.- Many clients rewrite
POSTtoGETon a302. Use303for the Post-Redirect-Get pattern or307for “same method.” - Returning
200from aPOSTthat created a resource loses information that201carries. 404and410are both “not here.” Use410when the URL is intentionally retired so caches and crawlers stop trying.Retry-After:is a header that pairs with429and503. Clients honor it; omitting it forces blind retries.
Return a structured error body with every 4xx and 5xx
The status code tells the client what class of failure happened; the body tells it which field or rule failed. RFC 9457 (Problem Details for HTTP APIs) defines a standard shape served as application/problem+json:
{
"type": "https://example.com/errors/validation",
"title": "Validation failed",
"status": 422,
"detail": "email must be a valid address",
"instance": "/users"
}Add extension members such as an errors array for per-field messages. Keep the status member identical to the HTTP status line, and never put stack traces in the body.