Master HTTP status codes, understand the 1xx-5xx families, learn when to use which code, and avoid common mistakes in API responses.
Every HTTP response includes a status code — a three-digit number that tells the client what happened. Did the request succeed? Was there an error? Should the client retry?
Status codes are organized into five families, each with a specific meaning:
1xx — Informational The request was received and is being processed. Rarely used in REST APIs.
100 Continue — Server received headers, client should send body101 Switching Protocols — Server is switching to WebSocket, for example2xx — Success The request succeeded. The most common and desirable outcome.
200 OK — Request succeeded, response body contains the result201 Created — New resource was created (POST)204 No Content — Request succeeded but no content to return (DELETE)3xx — Redirection The client needs to take additional action. The resource has moved.
301 Moved Permanently — Resource has permanently moved to a new URL302 Found — Resource temporarily at a different URL304 Not Modified — Resource hasn't changed since last request (caching)4xx — Client Error The client made a mistake. Bad request, unauthorized, not found.
400 Bad Request — Malformed request syntax401 Unauthorized — Authentication required or failed403 Forbidden — Authenticated but not authorized404 Not Found — Resource doesn't exist5xx — Server Error The server failed to fulfill a valid request. Something went wrong on the backend.
500 Internal Server Error — Generic server error503 Service Unavailable — Server is temporarily down or overloadedThe first digit tells you the category. If you see a 4xx code, you know it is a client error. If you see a 5xx code, you know it is a server error. This structure makes status codes predictable and easy to handle programmatically.
While there are dozens of status codes, only a handful are essential for building REST APIs. Here are the ones you will use most:
200 OK — General Success Use for successful GET, PUT, or PATCH requests when returning data.
GET /users/123 → 200 OK + user data
PUT /users/123 → 200 OK + updated user data201 Created — Resource Created
Use for successful POST requests that create a new resource. Include a Location header with the URL of the new resource.
POST /users → 201 Created + new user data
Location: /users/456204 No Content — Success with No Response Body Use for successful DELETE requests or updates that don't return data.
DELETE /users/123 → 204 No Content
400 Bad Request — Malformed or Invalid Request Use when the request is syntactically incorrect or missing required fields.
POST /users with invalid JSON → 400 Bad Request
401 Unauthorized — Authentication Required Use when the request lacks valid authentication credentials. The name is misleading — it really means "unauthenticated".
GET /users without auth token → 401 Unauthorized
403 Forbidden — Authenticated but Not Authorized Use when the user is logged in but doesn't have permission for this action.
DELETE /users/123 as a non-admin → 403 Forbidden
404 Not Found — Resource Doesn't Exist Use when the requested resource doesn't exist.
GET /users/999999 → 404 Not Found
409 Conflict — Request Conflicts with Current State Use when the request would create a conflict, like a duplicate resource.
POST /users with existing email → 409 Conflict
422 Unprocessable Entity — Validation Failed Use when the request is well-formed but fails business logic validation.
POST /users with email missing @ symbol → 422 Unprocessable Entity
429 Too Many Requests — Rate Limit Exceeded Use when the client has sent too many requests in a given time period.
100th request in 1 minute → 429 Too Many Requests
Retry-After: 60500 Internal Server Error — Something Broke Use when an unexpected error occurs on the server.
GET /users causes database crash → 500 Internal Server Error
503 Service Unavailable — Server Temporarily Down Use when the server is under maintenance or overloaded.
Any request during deployment → 503 Service Unavailable
Retry-After: 300<div style="font-family:sans-serif; padding:20px; line-height:1.8;">
<h3>Choosing the Right Status Code</h3>
<div style="background:#f3f4f6; padding:16px; border-radius:8px; border-left:4px solid #3b82f6;">
<strong>1. Did the request succeed?</strong><br>
✅ Yes → Use 2xx<br>
❌ No → Continue to step 2
</div>
<div style="background:#fef3c7; padding:16px; border-radius:8px; border-left:4px solid #f59e0b; margin-top:12px;">
<strong>2. Who caused the problem?</strong><br>
👤 Client error (bad input, auth failure) → Use 4xx<br>
🖥️ Server error (crash, timeout) → Use 5xx
</div>
<div style="background:#dbeafe; padding:16px; border-radius:8px; border-left:4px solid #3b82f6; margin-top:12px;">
<strong>3. What specifically happened?</strong><br>
<table style="width:100%; margin-top:8px; font-size:14px;">
<tr><td>Created new resource?</td><td><strong>201 Created</strong></td></tr>
<tr><td>Deleted successfully?</td><td><strong>204 No Content</strong></td></tr>
<tr><td>Missing auth token?</td><td><strong>401 Unauthorized</strong></td></tr>
<tr><td>Not allowed to access?</td><td><strong>403 Forbidden</strong></td></tr>
<tr><td>Resource not found?</td><td><strong>404 Not Found</strong></td></tr>
<tr><td>Validation failed?</td><td><strong>422 Unprocessable Entity</strong></td></tr>
<tr><td>Server crashed?</td><td><strong>500 Internal Server Error</strong></td></tr>
</table>
</div>
</div>Many APIs misuse status codes, leading to confusion and poor client experience. Here are the most common mistakes and how to avoid them:
Mistake 1: Returning 200 OK for Errors
Some APIs return 200 OK for every response and put the error in the response body:
200 OK
{ "success": false, "error": "User not found" }This is wrong. Status codes exist to communicate success or failure. Use 404 Not Found instead. Clients should be able to check the status code without parsing the body.
Mistake 2: Using 404 for Validation Errors
POST /users with invalid email → 404 Not Found ❌
404 means the resource doesn't exist. Validation failures should return 400 Bad Request or 422 Unprocessable Entity.
Mistake 3: Confusing 401 and 403
401 Unauthorized — You are not logged in. Send credentials.403 Forbidden — You are logged in, but you don't have permission.If a user without an auth token tries to delete a resource, return 401. If a regular user tries to delete an admin resource, return 403.
Mistake 4: Using 500 for Expected Errors
GET /users/invalid-id → 500 Internal Server Error ❌
If the client sends an invalid ID, that is a client error (400 Bad Request). Reserve 500 for unexpected server crashes and exceptions.
Mistake 5: Not Including Retry Information
When returning 429 Too Many Requests or 503 Service Unavailable, always include a Retry-After header telling the client when to try again:
429 Too Many Requests
Retry-After: 60Best Practice: Be Specific
Don't just use 400 Bad Request for everything. If it is a validation error, use 422. If it is a conflict, use 409. If it is a not found, use 404. Specific status codes help clients handle errors intelligently.
A user tries to update another user's profile. They are logged in, but they don't have permission. What status code should the API return?