Status Codes & Headers
The 2xx-5xx status code families, the codes that matter most, and key HTTP headers.
Status code categories
HTTP status codes are grouped into five categories by their first digit. Knowing the category alone tells a client (or a developer skimming logs) roughly what happened, even without knowing the specific code:
| Range | Category | Meaning |
|---|---|---|
1xx |
Informational | Request received, still processing (rarely seen directly in application code). |
2xx |
Success | The request was received, understood, and accepted. |
3xx |
Redirection | Further action is needed to complete the request (usually following a new URL). |
4xx |
Client error | The request is invalid, malformed, unauthorized, or refers to something that doesn't exist — the problem is on the caller's side. |
5xx |
Server error | The server failed to fulfill a valid request — the problem is on the server's side. |
Getting the category right matters even more than getting the exact code right — an API that returns 500 for a validation error (which is really a 4xx client mistake) actively misleads monitoring and alerting, which typically treats 5xx as "something is broken on our end, page someone."
The codes you'll use constantly
200 OK — the generic success response for GET, PATCH, or a PUT that updated an existing resource. The response body contains the resource.
201 Created — a resource was successfully created (typically the response to POST /posts). Should include a Location header pointing at the new resource, and a body representing it.
HTTP/1.1 201 Created
Location: /posts/124
Content-Type: application/json
{"id": 124, "title": "New Post", "status": "draft"}
204 No Content — the request succeeded, but there's nothing to send back. Common for DELETE requests, or a PUT/PATCH where the client doesn't need the updated resource echoed back. The response has no body at all.
400 Bad Request — the request itself is malformed — invalid JSON syntax, a missing required field, a value of the wrong type. This is about the shape of the request, not permissions or existence.
401 Unauthorized — the request has no valid authentication credentials (despite the name, this is about authentication — "who are you?" — not permissions). The correct response when a bearer token is missing, expired, or invalid.
403 Forbidden — the client is authenticated, but isn't allowed to perform this action on this resource. The correct response when a logged-in user tries to delete someone else's post.
404 Not Found — no resource exists at this URL. Used both for genuinely missing resources and, sometimes deliberately, to avoid confirming a resource's existence to an unauthorized caller (returning 404 instead of 403 so an attacker can't tell the difference between "doesn't exist" and "exists but you can't see it").
409 Conflict — the request conflicts with the current state of the resource — for example, trying to create a user with an email address that's already taken, or a version-mismatch on an optimistic-locking update.
422 Unprocessable Entity — the request is well-formed JSON (unlike 400) but fails semantic/business validation — a startDate that's after the endDate, a password that doesn't meet complexity rules. Many APIs use 400 and 422 somewhat interchangeably; the distinction, where teams draw it, is "malformed" (400) vs. "syntactically valid but semantically invalid" (422).
500 Internal Server Error — a generic, unexpected server failure. Should never intentionally leak stack traces or internal details to the client in production.
Choosing between similar codes — a quick decision table
| Situation | Code |
|---|---|
| Missing/invalid auth token | 401 |
| Valid auth, but not allowed to do this | 403 |
| Resource doesn't exist | 404 |
| Malformed JSON / missing required field | 400 |
| Well-formed but fails validation rules | 422 |
| Duplicate / state conflict (e.g. unique email taken) | 409 |
| Successful delete, nothing to return | 204 |
| Successful creation | 201 |
Key headers
Content-Type — tells the receiver what format the body is in. application/json is the overwhelming default for modern APIs. A server should set this on every response with a body, and a client sending a body should set it on every request:
Content-Type: application/json
Authorization — carries credentials, almost always as a bearer token for modern APIs:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Location — set on a 201 Created response, pointing at the URL of the newly created resource, so the client doesn't have to guess or construct it:
HTTP/1.1 201 Created
Location: /orders/9931
Accept — sent by the client to indicate what response formats it can handle (mostly relevant for APIs that support multiple representations, e.g. JSON and XML):
Accept: application/json
Common mistakes
- Returning
200 OKfor everything, including errors, with the actual error state buried in the response body — this defeats HTTP's own error-signaling mechanism and breaks generic HTTP tooling (caching, monitoring, retries). - Using
401when you mean403, or vice versa —401means "I don't know who you are (or your credentials are invalid)";403means "I know who you are, and the answer is no." - Forgetting
Content-Typeon a JSON response — some HTTP clients will refuse to auto-parse the body without it.
Interview questions
Q: What's the difference between a 401 and a 403 response?
401 Unauthorized means the request lacks valid authentication — the server doesn't know who's asking (or the token given is invalid/expired). 403 Forbidden means the server does know who's asking, but that identity isn't permitted to perform the requested action on that resource.
Q: Why might an API intentionally return 404 instead of 403 for a resource a user isn't allowed to see?
To avoid leaking information — returning 403 confirms the resource exists (just not accessible to this caller), while 404 reveals nothing. This is a deliberate security choice for resources where even confirming existence could be sensitive (e.g., another user's private data).
Q: When would you use 204 instead of 200?
When the request succeeded but there's genuinely no content to return — the classic case is a DELETE request, or a PUT/PATCH where the client already has the data it sent and doesn't need it echoed back. 204 responses have no body at all.