HTTP Status Codes

Standard response codes that indicate the result of an HTTP request.

HTTP status codes are three-digit numbers that servers return to tell clients what happened with their request. They're the first thing you should check when debugging API issues - before looking at response bodies or logs, the status code tells you the category of the problem.

Status Code Categories

The first digit tells you the category:

RangeCategoryMeaning
1xxInformationalRequest received, continuing process
2xxSuccessRequest was successful
3xxRedirectionClient must take additional action
4xxClient ErrorProblem with the request
5xxServer ErrorServer failed to fulfill valid request

Success Codes (2xx)

CodeNameWhen to Use
200OKStandard success. GET returned data, PUT/PATCH updated resource.
201CreatedPOST successfully created a new resource. Include Location header with URL to new resource.
202AcceptedRequest accepted for processing but not completed yet. Use for async operations.
204No ContentSuccess but nothing to return. Perfect for DELETE operations.

Common mistake: Returning 200 for everything. If you create a resource, return 201. If you delete something, return 204.

Redirection Codes (3xx)

CodeNameWhen to Use
301Moved PermanentlyResource has a new permanent URL. Search engines will update.
302FoundTemporary redirect. Original URL should still be used.
304Not ModifiedClient's cached version is still valid. Don't send body.
307Temporary RedirectLike 302 but preserves request method. POST stays POST.
308Permanent RedirectLike 301 but preserves request method.

Client Error Codes (4xx)

These mean the client did something wrong:

CodeNameWhen to Use
400Bad RequestMalformed syntax, invalid JSON, missing required fields.
401UnauthorizedNo authentication provided. Token missing or invalid.
403ForbiddenAuthenticated but not allowed. User can't access this resource.
404Not FoundResource doesn't exist at this URL.
405Method Not AllowedEndpoint exists but doesn't support this HTTP method.
409ConflictRequest conflicts with current state. Duplicate email, version conflict.
422Unprocessable EntitySyntax is correct but semantically invalid. Email format valid but domain doesn't exist.
429Too Many RequestsRate limit exceeded. Include Retry-After header.

401 vs 403:

  • 401: "Who are you?" (not authenticated)
  • 403: "I know who you are, but you can't do this" (not authorized)

400 vs 422:

  • 400: Can't even parse the request (invalid JSON)
  • 422: Parsed OK but doesn't make sense (end_date before start_date)

Server Error Codes (5xx)

These mean the server failed:

CodeNameWhen to Use
500Internal Server ErrorSomething broke. Generic "our fault" error.
502Bad GatewayProxy/gateway got invalid response from upstream server.
503Service UnavailableServer temporarily overloaded or down for maintenance. Include Retry-After.
504Gateway TimeoutUpstream server didn't respond in time.

Never expose stack traces in 500 responses. Log them server-side, return a generic message to clients.

Common Mistakes

1. Using 200 for errors

āŒ 200 OK { "success": false, "error": "User not found" }
āœ… 404 Not Found { "error": "User not found" }

HTTP clients and monitoring tools rely on status codes. A 200 with an error body is invisible to them.

2. Using 500 for client errors If the user sent bad data, that's a 4xx. 500 means your code is broken. Too many 500s trigger alerts and make debugging harder.

3. Returning 200 for empty results An empty list is still a success. GET /users?name=xyz returning zero results should be 200 with an empty array, not 404.

4. Misusing 404 404 means "this URL doesn't exist." If the URL is valid but the resource was deleted, consider 410 Gone. If the user isn't allowed to know it exists, use 403 or 404 (depending on security needs).

Best Practices

Be specific: Don't use 400 for everything. If authentication failed, use 401. If validation failed, use 422. Specific codes help clients handle errors properly.

Include helpful error bodies: Status code says what category. Body says exactly what went wrong:

{
  "error": "Validation failed",
  "details": [
    {"field": "email", "message": "must be a valid email address"},
    {"field": "age", "message": "must be at least 18"}
  ]
}

Use Retry-After for 429 and 503: Tell clients when they can try again: Retry-After: 60 (seconds) or Retry-After: Wed, 21 Oct 2024 07:28:00 GMT

Document your codes: Your API docs should list which status codes each endpoint can return and what they mean in context.

Code Examples

Handling Status Codes in Fetch

async function apiCall(url) {
  const res = await fetch(url);

  switch (res.status) {
    case 200:
    case 201:
      return res.json();

    case 204:
      return null; // No content

    case 400:
      const error = await res.json();
      throw new ValidationError(error.details);

    case 401:
      // Redirect to login
      window.location = '/login';
      break;

    case 403:
      throw new Error('You do not have permission');

    case 404:
      throw new Error('Resource not found');

    case 429:
      const retryAfter = res.headers.get('Retry-After');
      throw new RateLimitError(retryAfter);

    case 500:
    case 502:
    case 503:
      throw new Error('Server error. Please try again later.');

    default:
      throw new Error(`Unexpected status: ${res.status}`);
  }
}