July 30, 2026
HTTP Status Codes Every Developer Should Know
HTTP status codes are a three-digit contract between client and server, grouped into five classes that tell you whether a request worked, where to look next, and whose fault it is when it did not. Most day-to-day debugging comes down to recognizing the dozen that show up constantly.

Every HTTP response carries a three-digit status code, and the first digit tells you almost everything you need to know. The spec groups codes into five classes, and within each class the common ones show up over and over. The status line is the API's way of telling you what happened — read it before you reach for the response body, because the body often cannot be trusted to explain itself.
The five classes at a glance
The first digit of any status code identifies its class, and the five classes are easy to remember because they map onto the obvious story of a request:
- 1xx — Informational: the request was received, keep going.
- 2xx — Success: the server accepted, understood, and acted on it.
- 3xx — Redirection: the resource lives somewhere else, follow along.
- 4xx — Client error: you sent something wrong.
- 5xx — Server error: the server broke.
The remaining two digits name specific responses within each class. You will never meet most of them; the ones that matter for day-to-day work are a short list.
The codes you will actually see
In the success class, three codes dominate. 200 OK is the generic "it worked" — GET returned a body, POST created something and is returning a representation, PUT updated a resource. 201 Created is the more precise sibling for resource creation: the server made a new thing, and the response usually points at it with a Location header. 204 No Content means success with an empty body — common for DELETE and for PUT updates where there is nothing new to return.
In the redirect class, 301 Moved Permanently tells clients and crawlers to update their links; the URL is now somewhere else forever. 302 Found is its temporary counterpart, historically abused and now mostly replaced by 303 and 307 for clarity. 304 Not Modified is the workhorse of caching: the client sent conditional headers, the server confirmed nothing changed, and the body is intentionally omitted so the client reuses its cached copy.
Client errors are the ones you debug most. 400 Bad Request is the catch-all for malformed input — broken JSON, missing required fields, a body the server could not parse. 401 Unauthorized really means unauthenticated: no credentials, or credentials the server did not accept. 403 Forbidden is different and sharper — you are authenticated, but you are not allowed to do this. 404 Not Found is self-explanatory and wildly overloaded; many APIs return it for both "does not exist" and "exists but you cannot see it" to avoid leaking existence. 429 Too Many Requests is the rate-limit signal, and well-behaved clients back off when they see it rather than hammering harder.
Server errors start and end with 500 Internal Server Error, the generic "something blew up on our side." 502 Bad Gateway means an upstream proxy got an invalid response from the real server behind it. 503 Service Unavailable usually means overloaded or down for maintenance, and is the right thing to return during a deploy.
HTTP/1.1 201 Created
Location: /users/7
Content-Type: application/json
{"id": 7, "name": "Ada"}
Try it: JSON Formatter
Format, validate, and minify JSON instantly in your browser — no upload, no signup.
Reading codes as a debugging signal
A surprising amount of debugging is just pattern-matching on status codes against what you sent. A 400 against a POST usually means the body was wrong, so pretty-print the JSON you are sending and confirm it parses before chasing server bugs. A 401 versus a 403 tells you whether to fix authentication or authorization — sending another login attempt at a 403 is a waste of time. A 502 or 503 is rarely something your client code can fix; it usually means retry with backoff, and if it persists, page someone.
Redirects trip people up because the client may or may not follow them automatically. Browsers follow most redirects transparently, fetch in JavaScript follows by default, but lower-level HTTP clients sometimes hand you the 3xx response and expect you to make the next request yourself. If a redirect loop appears in your logs, look for a Location pointing back at the original URL — that is almost always a misconfigured base URL or a trailing-slash mismatch.
Codes are a contract, not a suggestion
Treat status codes as part of the API contract, not as decoration. If you are designing an endpoint, return the most specific code that fits — 201 for create, 204 for empty success, 429 for rate limits, 503 for planned downtime — because clients, proxies, and monitoring tools all branch on these values. A catch-all 200 with an error stashed in the response body defeats the entire point of HTTP and forces every consumer to re-implement status semantics in their own code.
When you are on the consuming side and a request fails, inspect the status, the headers, and only then the body. If the response is JSON and your client renders it as a wall of unformatted text, paste it through the JSON Formatter before reading. And if the failing URL is full of special characters, percent-encode it correctly first with the URL Encode/Decode tool — a 400 from a server that cannot parse your query string is a common and entirely avoidable failure.