Understanding HTTP status codes for API developers
Status codes are the contract between your API and every client, proxy, cache and monitoring tool that touches it. Choosing them carelessly makes debugging harder for everyone downstream.
By Sk Md Rakib · Published · Updated · 9 min read
2xx — success, with meaning
200 OK is the standard success response for GET and for updates that return a body. 201 Created belongs on a POST that created a resource, and should carry a Location header pointing at the new resource. 204 No Content is the right answer for a successful DELETE, or any request where there is genuinely nothing to return. 202 Accepted says the work was queued but is not finished — essential for asynchronous jobs.
Returning a blanket 200 for everything is the most common API design mistake. Clients, caches and dashboards read these codes semantically: a 201 tells a client where the resource lives, a 202 tells it to poll, and a 204 tells it not to try parsing an empty body as JSON.
3xx — redirects and method preservation
301 Moved Permanently tells clients and crawlers to update their stored URL; 302 Found signals a temporary move. The subtlety that bites API authors is method rewriting: historically, browsers converted a POST following a 301 or 302 into a GET. 307 and 308 are the modern equivalents that preserve the method and body.
For an API version migration, use 301 or 308 so clients and search engines update permanently. Never redirect an authenticated POST through a 302 and expect the body to arrive intact.
4xx — the client's fault, and how to say so usefully
400 Bad Request means malformed input — always pair it with a body explaining precisely what was wrong. 401 Unauthorized is about authentication despite its name: no credentials, or invalid ones. 403 Forbidden means authenticated but not permitted. 404 Not Found covers missing resources, and is also the correct answer when revealing existence would leak information.
The codes teams underuse are the specific ones. 409 Conflict for duplicate keys and optimistic-locking failures. 422 Unprocessable Entity for syntactically valid but semantically invalid payloads, which is where validation errors belong. 429 Too Many Requests for rate limiting — and always with a Retry-After header, because without it clients can only guess and will guess badly.
- 401 = who are you; 403 = I know who you are and you still cannot.
- 400 = I could not parse it; 422 = I parsed it and it is wrong.
- 429 without Retry-After guarantees a retry storm.
- Return machine-readable error codes in the body, not just prose.
5xx — your fault, and what your on-call engineer needs
500 Internal Server Error is the catch-all for an unhandled failure; every one should be logged with full context because each represents a bug. 502 Bad Gateway means an upstream returned something invalid. 503 Service Unavailable means you are deliberately refusing work — maintenance, shed load, a tripped circuit breaker — and should include Retry-After. 504 Gateway Timeout means an upstream did not answer in time.
Separate these in monitoring. A rising 503 rate means capacity or a dependency is degraded and the system is behaving as designed; a rising 500 rate means code is broken. They page different people and demand different responses, and collapsing them into one 'error rate' metric destroys that signal.
Practical rules for your own API
Be consistent above all: pick the code for each situation once, document it, and enforce it in tests. Never return an error inside a 200 body — that pattern breaks every generic client, retry policy and alerting rule ever written. Keep error bodies uniform in shape, with a stable machine-readable code, a human message and, where relevant, a field path.
When consuming third-party APIs, defend against their inconsistency. Treat 5xx and 429 as retryable with exponential backoff and jitter; treat 4xx (except 429) as permanent and do not retry. Log the status code alongside a correlation identifier so a failure can be traced end to end — a UUID generated per request is enough.