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.

ActionStatus
Created a resource201 with Location: header.
Updated a resource (no body returned)204.
Updated a resource (body returned)200.
Deleted a resource204 (or 200 with confirmation body).
Validation failed on JSON body422 with field errors.
Missing required header or query param400.
User not authenticated401 with WWW-Authenticate: header.
Authenticated user lacks permission403.
Resource missing404 (or 410 if it was deleted on purpose).
Duplicate insert / version conflict409.
Rate limit hit429 with Retry-After:.
Async job accepted202 with Location: to a status URL.
Maintenance window503 with Retry-After:.

Avoid the common status code gotchas

  • 200 OK with {"error": "..."} in the body is wrong. The status code is part of the API.
  • 401 Unauthorized is really “unauthenticated.” Use 403 when the user is known but lacks permission.
  • Many clients rewrite POST to GET on a 302. Use 303 for the Post-Redirect-Get pattern or 307 for “same method.”
  • Returning 200 from a POST that created a resource loses information that 201 carries.
  • 404 and 410 are both “not here.” Use 410 when the URL is intentionally retired so caches and crawlers stop trying.
  • Retry-After: is a header that pairs with 429 and 503. 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.