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

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');

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

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"
}

How to handle:

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

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

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');
  }
}

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)

How to handle:

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

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

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'));
}

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"
  }
}

How to handle:

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

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"
}

How to handle:

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

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"
  }
}

How to handle:

if (response.status === 403) {
  console.error('Access denied. Check API key permissions.');
  // Don't retry — you need higher permissions
}

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"
}

How to handle:

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

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

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"
  }
}

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);
}

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"
    }
  ]
}

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
}

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"
  }
}

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');
}

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"
}

How to handle:

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

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);
}

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>

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);
}

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 });
}

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;
    }
  }
}

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 →

📡 Turn status code monitoring into automated incident detection. Better Stack tracks HTTP status codes across your entire infrastructure, triggers alerts on error rate spikes, and creates incident timelines automatically — so your team responds to patterns, not individual errors.

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:

🛠 Tools We Use & Recommend

Tested across our own infrastructure monitoring 200+ APIs daily

Better StackBest for API Teams

Uptime Monitoring & Incident Management

Used by 100,000+ websites

Monitors your APIs every 30 seconds. Instant alerts via Slack, email, SMS, and phone calls when something goes down.

We use Better Stack to monitor every API on this site. It caught 23 outages last month before users reported them.

Free tier · Paid from $24/moStart Free Monitoring
1PasswordBest for Credential Security

Secrets Management & Developer Security

Trusted by 150,000+ businesses

Manage API keys, database passwords, and service tokens with CLI integration and automatic rotation.

After covering dozens of outages caused by leaked credentials, we recommend every team use a secrets manager.

OpteryBest for Privacy

Automated Personal Data Removal

Removes data from 350+ brokers

Removes your personal data from 350+ data broker sites. Protects against phishing and social engineering attacks.

Service outages sometimes involve data breaches. Optery keeps your personal info off the sites attackers use first.

From $9.99/moFree Privacy Scan
ElevenLabsBest for AI Voice

AI Voice & Audio Generation

Used by 1M+ developers

Text-to-speech, voice cloning, and audio AI for developers. Build voice features into your apps with a simple API.

The best AI voice API we've tested — natural-sounding speech with low latency. Essential for any app adding voice features.

Free tier · Paid from $5/moTry ElevenLabs Free
SEMrushBest for SEO

SEO & Site Performance Monitoring

Used by 10M+ marketers

Track your site health, uptime, search rankings, and competitor movements from one dashboard.

We use SEMrush to track how our API status pages rank and catch site health issues early.

From $129.95/moTry SEMrush Free
View full comparison & more tools →Affiliate links — we earn a commission at no extra cost to you

API Status Check

Stop checking API status pages manually

Get instant email alerts when OpenAI, Stripe, AWS, and 100+ APIs go down. Know before your users do.

Get Alerts — $9/mo →

Free dashboard available · 14-day trial on paid plans · Cancel anytime

Browse Free Dashboard →