HTTP Status Codes: Which One to Return, and When

2026-03-27 · SPUNK13 · spunk.bet

Most API bugs I have chased that involved status codes came from three habits: returning 200 with an error in the body, using 400 for everything that is not a 500, and returning 404 when the caller lacked permission. Each has a correct answer and it takes about ten minutes to fix.

The 2xx Codes You Actually Need

Redirects That Matter

301 and 308 are permanent; browsers cache them aggressively and will not re-request the old URL, sometimes for months. Never use 301 to test something. 302 and 307 are temporary. The difference within each pair is method preservation: 301 and 302 historically allowed clients to rewrite a POST into a GET, while 307 and 308 guarantee the method and body are preserved. For an API, use 307/308. 304 Not Modified is the one worth wiring up — combined with ETag and If-None-Match it turns repeat fetches into a header exchange with no body.

The 4xx Distinctions People Get Wrong

5xx and What They Tell Your Monitoring

500 means your code broke. 502 means an upstream returned garbage. 503 means you are deliberately unavailable — maintenance, or shedding load — and should carry Retry-After. 504 means an upstream timed out. Keeping these distinct matters because alerting on "5xx rate" without splitting them mixes your bugs with your dependency's outages, and those need different pagers.

One Error Body Shape, Everywhere

RFC 9457 defines application/problem+json and it is worth adopting simply so every client parses one shape:

HTTP/1.1 422 Unprocessable Content
Content-Type: application/problem+json

{
  "type": "https://example.com/errors/invalid-date-range",
  "title": "Invalid date range",
  "status": 422,
  "detail": "end_date must be after start_date",
  "instance": "/bookings/8814",
  "errors": [
    {"field": "end_date", "code": "before_start"}
  ]
}

Two rules make this useful. First, type is a stable identifier clients can branch on — never change it, and never make clients string-match on detail. Second, detail is for a human and must not contain stack traces, SQL, or internal hostnames.

Rules of Thumb

Match the status code to the class of problem, not to how bad it feels. Never return 200 for a failure; every retry, cache, circuit breaker and dashboard in the chain relies on the status line and none of them read your body. And when you are unsure between two adjacent codes, pick one, write it in the API docs, and stay consistent — consistency is worth more to integrators than being right about 400 versus 422.

Explore More

Free tools, guides, and resources.

Visit spunk.bet
400+ ToolsCasinoMemesAstrologyScam DB