Beginner20 min read

HTTP Status Codes

Master HTTP status codes, understand the 1xx-5xx families, learn when to use which code, and avoid common mistakes in API responses.

Understanding Status Codes

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 body
  • 101 Switching Protocols — Server is switching to WebSocket, for example

2xx — Success The request succeeded. The most common and desirable outcome.

  • 200 OK — Request succeeded, response body contains the result
  • 201 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 URL
  • 302 Found — Resource temporarily at a different URL
  • 304 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 syntax
  • 401 Unauthorized — Authentication required or failed
  • 403 Forbidden — Authenticated but not authorized
  • 404 Not Found — Resource doesn't exist

5xx — Server Error The server failed to fulfill a valid request. Something went wrong on the backend.

  • 500 Internal Server Error — Generic server error
  • 503 Service Unavailable — Server is temporarily down or overloaded

The 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.

The Most Important Status Codes for APIs

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.

html
GET /users/123 → 200 OK + user data
PUT /users/123 → 200 OK + updated user data

201 Created — Resource Created Use for successful POST requests that create a new resource. Include a Location header with the URL of the new resource.

html
POST /users → 201 Created + new user data
Location: /users/456

204 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.

html
100th request in 1 minute → 429 Too Many Requests
Retry-After: 60

500 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.

html
Any request during deployment → 503 Service Unavailable
Retry-After: 300

Status Code Decision Flow

html
<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>

Common Status Code Mistakes

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:

json
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:

html
429 Too Many Requests
Retry-After: 60

Best 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?

Ready to practice?

Create your free account to access the interactive code editor, run challenges, and track your progress.