DEV Community

Shibβ„’ πŸš€
Shibβ„’ πŸš€

Posted on • Originally published at apistatuscheck.com

API HTTP Status Codes: The Complete Developer Cheat Sheet (2026)

API HTTP Status Codes: The Complete Developer Cheat Sheet (2026)

Every API developer needs to understand HTTP status codes. They're how APIs communicate success, failure, rate limits, and outages. This is your comprehensive, bookmark-worthy reference.

Quick Reference Table

Code Name Meaning Action
1xx β€” Informational
100 Continue Request received, continue Proceed with request body
101 Switching Protocols Upgrading to WebSocket Connection upgraded
2xx β€” Success
200 OK Request succeeded Process response
201 Created Resource created Check Location header
202 Accepted Processing async Poll for completion
204 No Content Success, no body No response to parse
3xx β€” Redirection
301 Moved Permanently Resource relocated Update bookmark
302 Found Temporary redirect Follow redirect
304 Not Modified Cached version OK Use cached data
4xx β€” Client Error
400 Bad Request Invalid syntax Fix request format
401 Unauthorized Auth required Provide credentials
403 Forbidden Access denied Check permissions
404 Not Found Resource doesn't exist Verify endpoint/ID
405 Method Not Allowed Wrong HTTP method Use GET/POST/etc correctly
409 Conflict Resource state conflict Resolve conflict
422 Unprocessable Entity Validation failed Fix input data
429 Too Many Requests Rate limit exceeded Back off and retry
5xx β€” Server Error
500 Internal Server Error API crashed Retry with backoff
502 Bad Gateway Upstream failed API may be down
503 Service Unavailable API overloaded API is down/deploying
504 Gateway Timeout Upstream timeout API is slow/down

1xx: Informational β€” "Hold On, We're Working on It"

These are rare in REST APIs. You'll see them with WebSockets or long-running uploads.

100 Continue

What it means: "I got your headers, send me the rest of the request body."

When you'll see it: Large file uploads where the client checks if the server will accept the file before sending all the data.

How to handle:

// Browsers handle this automatically
// Just send your request normally
Enter fullscreen mode Exit fullscreen mode

101 Switching Protocols

What it means: "We're upgrading this connection to WebSocket."

When you'll see it: WebSocket handshake.

Example:

const ws = new WebSocket('wss://api.example.com/stream');
ws.onopen = () => console.log('Connection upgraded to WebSocket');
Enter fullscreen mode Exit fullscreen mode

2xx: Success β€” "Everything Worked!"

200 OK

What it means: The request succeeded. This is the "happy path."

When APIs return it:

  • GET requests that return data
  • POST requests that process data
  • PUT/PATCH requests that update resources

Real example (GitHub API):

// GET /user
fetch('https://api.github.com/user', {
  headers: { 'Authorization': 'token YOUR_TOKEN' }
})
.then(res => res.json()) // 200 OK + user data
Enter fullscreen mode Exit fullscreen mode

201 Created

What it means: "Resource successfully created."

When APIs return it:

  • POST requests that create new resources
  • Response includes Location header pointing to the new resource

Real example (Stripe API):

// POST /v1/customers
// Response: 201 Created
// Location: /v1/customers/cus_123456789
{
  "id": "cus_123456789",
  "object": "customer",
  "email": "user@example.com"
}
Enter fullscreen mode Exit fullscreen mode

How to handle:

if (response.status === 201) {
  const location = response.headers.get('Location');
  console.log('Created resource at:', location);
}
Enter fullscreen mode Exit fullscreen mode

202 Accepted

What it means: "Request accepted, but processing asynchronously."

When APIs return it:

  • Long-running operations (video encoding, bulk imports)
  • Background jobs
  • Webhooks that trigger later

Real example (AWS S3):

// POST /restore (S3 Glacier restore)
// Response: 202 Accepted
// Poll /status until 200 OK
Enter fullscreen mode Exit fullscreen mode

How to handle:

async function pollUntilComplete(statusUrl) {
  while (true) {
    const res = await fetch(statusUrl);
    if (res.status === 200) return res.json();
    if (res.status === 202) {
      await sleep(5000); // Wait 5 seconds
      continue;
    }
    throw new Error('Job failed');
  }
}
Enter fullscreen mode Exit fullscreen mode

204 No Content

What it means: "Success, but no response body."

When APIs return it:

  • DELETE requests (resource deleted, nothing to return)
  • PUT/PATCH updates where the response would just echo the request

Real example (Stripe API):

// DELETE /v1/customers/cus_123
// Response: 204 No Content (no JSON body)
Enter fullscreen mode Exit fullscreen mode

How to handle:

if (response.status === 204) {
  console.log('Success, but no content to parse');
  // Don't try to call response.json()
}
Enter fullscreen mode Exit fullscreen mode

3xx: Redirection β€” "Look Over There Instead"

301 Moved Permanently

What it means: "This endpoint has moved forever. Update your bookmarks."

When APIs return it:

  • API versioning (old endpoint β†’ new endpoint)
  • Domain changes

How to handle:

// Most HTTP clients auto-follow redirects
// But update your code to use the new URL
Enter fullscreen mode Exit fullscreen mode

304 Not Modified

What it means: "Your cached version is still good."

When APIs return it:

  • GET requests with If-None-Match or If-Modified-Since headers

How to handle:

const etag = localStorage.getItem('user-etag');
const res = await fetch('/api/user', {
  headers: { 'If-None-Match': etag }
});

if (res.status === 304) {
  console.log('Use cached data');
  return JSON.parse(localStorage.getItem('user-data'));
}
Enter fullscreen mode Exit fullscreen mode

4xx: Client Errors β€” "You Messed Up"

400 Bad Request

What it means: "Your request is malformed or missing required fields."

When APIs return it:

  • Invalid JSON syntax
  • Missing required parameters
  • Wrong data types

Real example (OpenAI API):

// POST /v1/chat/completions
// Missing "model" field
{
  "error": {
    "message": "you must provide a model parameter",
    "type": "invalid_request_error"
  }
}
Enter fullscreen mode Exit fullscreen mode

How to handle:

if (response.status === 400) {
  const error = await response.json();
  console.error('Bad request:', error.message);
  // Fix the request and retry
}
Enter fullscreen mode Exit fullscreen mode

401 Unauthorized

What it means: "You need to authenticate first."

When APIs return it:

  • Missing API key/token
  • Expired token
  • Invalid credentials

Real example (GitHub API):

// GET /user without Authorization header
// Response: 401 Unauthorized
{
  "message": "Requires authentication"
}
Enter fullscreen mode Exit fullscreen mode

How to handle:

if (response.status === 401) {
  // Redirect to login or refresh token
  refreshAuthToken();
}
Enter fullscreen mode Exit fullscreen mode

403 Forbidden

What it means: "I know who you are, but you don't have permission."

When APIs return it:

  • Authenticated but not authorized
  • API key lacks required scope/permission
  • Resource access denied

Real example (Stripe API):

// Trying to access another account's customer
{
  "error": {
    "type": "invalid_request_error",
    "message": "No such customer: cus_999"
  }
}
Enter fullscreen mode Exit fullscreen mode

How to handle:

if (response.status === 403) {
  console.error('Access denied. Check API key permissions.');
  // Don't retry β€” you need higher permissions
}
Enter fullscreen mode Exit fullscreen mode

404 Not Found

What it means: "This endpoint or resource doesn't exist."

When APIs return it:

  • Typo in URL
  • Resource was deleted
  • Wrong API version

Real example (GitHub API):

// GET /repos/nonexistent/repo
// Response: 404 Not Found
{
  "message": "Not Found"
}
Enter fullscreen mode Exit fullscreen mode

How to handle:

if (response.status === 404) {
  console.log('Resource not found');
  // Show 'Not found' UI, don't retry
}
Enter fullscreen mode Exit fullscreen mode

405 Method Not Allowed

What it means: "You used GET instead of POST (or vice versa)."

When APIs return it:

  • POST to read-only endpoint
  • GET to create endpoint

How to handle:

// Check API docs and use correct HTTP method
// GET for reading, POST for creating, PUT/PATCH for updating, DELETE for deleting
Enter fullscreen mode Exit fullscreen mode

409 Conflict

What it means: "This conflicts with the current state of the resource."

When APIs return it:

  • Trying to create a resource that already exists
  • Version mismatch (optimistic locking)
  • Concurrent updates

Real example (Stripe API):

// Creating duplicate customer with same email
{
  "error": {
    "type": "invalid_request_error",
    "message": "Customer already exists"
  }
}
Enter fullscreen mode Exit fullscreen mode

How to handle:

if (response.status === 409) {
  // Fetch current state, merge changes, retry
  const current = await fetchCurrentState();
  const merged = mergeChanges(current, yourChanges);
  await retry(merged);
}
Enter fullscreen mode Exit fullscreen mode

422 Unprocessable Entity

What it means: "Request syntax is valid, but data validation failed."

When APIs return it:

  • Email format invalid
  • Password too short
  • Business logic validation failed

Real example (GitHub API):

// Creating repo with invalid name
{
  "message": "Validation Failed",
  "errors": [
    {
      "field": "name",
      "code": "invalid"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

How to handle:

if (response.status === 422) {
  const errors = await response.json();
  errors.errors.forEach(err => {
    console.error(`${err.field}: ${err.message}`);
  });
  // Show validation errors to user
}
Enter fullscreen mode Exit fullscreen mode

429 Too Many Requests

What it means: "Slow down! You hit the rate limit."

When APIs return it:

  • Exceeded requests per second/minute/hour
  • Burst limit exceeded

Real example (OpenAI API):

{
  "error": {
    "message": "Rate limit exceeded. Please retry after 20 seconds.",
    "type": "rate_limit_error"
  }
}
Enter fullscreen mode Exit fullscreen mode

How to handle:

async function fetchWithRetry(url, options, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    const res = await fetch(url, options);

    if (res.status === 429) {
      const retryAfter = res.headers.get('Retry-After') || 60;
      console.log(`Rate limited. Retrying in ${retryAfter}s...`);
      await sleep(retryAfter * 1000);
      continue;
    }

    return res;
  }
  throw new Error('Max retries exceeded');
}
Enter fullscreen mode Exit fullscreen mode

5xx: Server Errors β€” "We Messed Up (or We're Down)"

These are the codes you'll see during API outages.

500 Internal Server Error

What it means: "Something crashed on our end."

When you'll see it:

  • Unhandled exception in API code
  • Database connection failed
  • Null pointer exception

Real example (any API during outage):

{
  "error": "Internal server error",
  "request_id": "req_abc123"
}
Enter fullscreen mode Exit fullscreen mode

How to handle:

if (response.status === 500) {
  // Retry with exponential backoff
  await retryWithBackoff(request);
}
Enter fullscreen mode Exit fullscreen mode

502 Bad Gateway

What it means: "The API's upstream dependency failed."

When you'll see it:

  • Load balancer can't reach backend
  • Reverse proxy got invalid response
  • Common during deployments

How to handle:

if (response.status === 502) {
  console.log('API may be deploying. Retry in 30s...');
  await sleep(30000);
  return retry(request);
}
Enter fullscreen mode Exit fullscreen mode

503 Service Unavailable

What it means: "API is temporarily down (maintenance, overload, or outage)."

When you'll see it:

  • Scheduled maintenance
  • Traffic spike overwhelming servers
  • Major outages

Real example (AWS during outage):

<Error>
  <Code>ServiceUnavailable</Code>
  <Message>Reduce your request rate.</Message>
</Error>
Enter fullscreen mode Exit fullscreen mode

How to handle:

if (response.status === 503) {
  const retryAfter = response.headers.get('Retry-After');
  if (retryAfter) {
    console.log(`Service down. Retry after ${retryAfter}s`);
    await sleep(retryAfter * 1000);
  } else {
    // Exponential backoff: 1s, 2s, 4s, 8s...
    await sleep(Math.pow(2, attempt) * 1000);
  }
  return retry(request);
}
Enter fullscreen mode Exit fullscreen mode

504 Gateway Timeout

What it means: "The API didn't respond in time (slow or down)."

When you'll see it:

  • Database queries timing out
  • Upstream API calls timing out
  • Partial outages (some requests work, some timeout)

How to handle:

if (response.status === 504) {
  console.log('API timed out. Possible performance issue.');
  // Retry with longer timeout
  return retry(request, { timeout: 60000 });
}
Enter fullscreen mode Exit fullscreen mode

Status Codes Cheat Sheet (Printable)

2xx: Success

  • 200 OK β€” Standard success
  • 201 Created β€” Resource created
  • 202 Accepted β€” Processing async
  • 204 No Content β€” Success, no body

4xx: Client Errors

  • 400 Bad Request β€” Fix request format
  • 401 Unauthorized β€” Need auth
  • 403 Forbidden β€” Permission denied
  • 404 Not Found β€” Doesn't exist
  • 429 Too Many Requests β€” Rate limited

5xx: Server Errors (Outages)

  • 500 Internal Server Error β€” Retry
  • 502 Bad Gateway β€” API may be down
  • 503 Service Unavailable β€” API is down
  • 504 Gateway Timeout β€” API slow/down

Retry Logic Best Practices

Use exponential backoff for 5xx errors:

async function retryWithBackoff(fn, maxRetries = 5) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      if (error.status >= 500 && attempt < maxRetries - 1) {
        const delay = Math.pow(2, attempt) * 1000; // 1s, 2s, 4s, 8s, 16s
        console.log(`Retry ${attempt + 1}/${maxRetries} in ${delay}ms...`);
        await sleep(delay);
        continue;
      }
      throw error;
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Monitor API Status Codes Automatically

Don't wait for users to report errors. Monitor API status codes in real-time:

API Status Check tracks HTTP status codes for 120+ APIs and alerts you instantly when they start returning 5xx errors.

What we monitor:

  • Error rate trends (spike in 500/502/503)
  • Response time degradation (504 timeouts)
  • Authentication failures (401/403 spikes)
  • Rate limiting (429 patterns)

Start monitoring API status codes β†’

FAQ

What's the difference between 401 and 403?

  • 401 Unauthorized: You're not authenticated (no credentials or invalid token)
  • 403 Forbidden: You're authenticated, but don't have permission

Should I retry 4xx errors?

No. 4xx errors mean you made a mistake. Fix your request instead of retrying.

Exception: 429 (rate limit) β€” retry after the Retry-After period.

Should I retry 5xx errors?

Yes. 5xx errors are server-side issues. Retry with exponential backoff.

What's the difference between 502, 503, and 504?

  • 502 Bad Gateway: Reverse proxy got invalid response from backend
  • 503 Service Unavailable: API is down (maintenance/overload)
  • 504 Gateway Timeout: Backend didn't respond in time

All three usually mean "API is having problems."

How do I know if an API is down?

5xx status codes (especially 502, 503, 504) indicate outages or severe performance issues. Monitor for patterns:

  • Sustained 503 responses = full outage
  • Intermittent 502/504 = partial outage or performance degradation

What does "Retry-After" header mean?

The Retry-After header (usually with 429 or 503) tells you when to retry. Wait that many seconds before retrying.

Why do some APIs return 200 with error in body?

Some APIs (especially older SOAP APIs) always return 200 and put error details in the response body. This is bad practice. Modern REST APIs use proper status codes.

Conclusion

HTTP status codes are how APIs communicate. Understand them, handle them correctly, and your apps will be more resilient.

Remember:

  • 2xx = success
  • 4xx = you messed up (don't retry)
  • 5xx = API messed up (retry with backoff)

Bookmark this page for quick reference when debugging API integrations.


Related resources:

Top comments (0)