Technical

How to Explain HTTP Status Codes Clearly in API Documentation

Reference guides and handling strategies that help developers integrate successfully

By Chandler Supple11 min read
Generate HTTP Status Code Documentation

AI creates comprehensive status code reference tables with descriptions, examples, and client handling advice for your API endpoints

Your API returns a 500 error. The developer integrating with your service doesn't know if it's temporary or permanent, whether to retry, how long to wait, or if they did something wrong. They retry immediately 50 times in a row, DDoS'ing your already-struggling server. Meanwhile, they're cursing your documentation because all it says is "500: Internal Server Error" with no guidance on what to do.

Or your API returns 403 Forbidden when it should return 404 Not Found. The developer spends hours debugging their authentication, convinced their credentials are wrong, when actually they're just requesting a resource that doesn't exist. Your mixing of 403 and 404 codes based on permission visibility created more confusion than clarity.

HTTP status codes are your API's primary communication mechanism for errors and responses. Good documentation doesn't just list codes—it explains what each means in your specific context, when you use it, and critically, what developers should do when they receive it. This guide breaks down how to document HTTP status codes clearly to prevent common integration mistakes.

Why Status Code Documentation Matters

HTTP status codes are standardized, right? Everyone knows what 404 means. Why document them?

Because the standard definitions are incomplete for real-world API design. The RFCs tell you 404 means "Not Found" but they don't tell you:

  • Should you return 404 or 403 when a resource exists but user lacks permission?
  • When exactly do you return 200 vs. 201 vs. 204?
  • What's in the response body for each code?
  • Should clients retry on 500 errors? How long should they wait?
  • What's the difference between 401 and 403 in your API?
  • How do your rate limits work and what happens at 429?

Developers integrating with your API need to know your specific implementation decisions. Without clear documentation, they'll guess—and guess wrong, leading to flaky integrations, poor error handling, and frustrated developers.

The Essential Status Code Reference Table

Start your documentation with a comprehensive table. This gives developers a quick reference without reading paragraphs of explanation.

What to Include in the Table

For each status code you use, include:

  • Code: The numeric status code (404)
  • Name: Standard name (Not Found)
  • When Used: Specific scenarios in your API
  • Response Body: What's in the response
  • Client Action: What developers should do

Example entry:

404 Not Found
When Used: Resource ID doesn't exist or was deleted
Response Body: Error object with resource type and ID
Client Action: Verify resource ID, check if deleted, don't retry

Group by Category

Organize codes into standard categories:

  • 2xx Success: Request succeeded
  • 3xx Redirection: Further action needed
  • 4xx Client Error: Client did something wrong
  • 5xx Server Error: Server failed to fulfill valid request

This grouping helps developers quickly understand fault: 4xx = their problem to fix, 5xx = your problem (but they should retry).

Explaining 2xx Success Codes

Success codes seem straightforward but subtle differences matter.

200 OK vs. 201 Created vs. 204 No Content

These are often confused. Here's when to use each:

200 OK: General success. Use for:

  • Successful GET returning data
  • Successful PUT/PATCH updating resource
  • Successful DELETE that returns confirmation data

Response includes the requested or updated resource.

201 Created: New resource created. Use for:

  • Successful POST creating new resource

Response includes:

  • The created resource in body
  • `Location` header with URL of new resource
  • Resource ID

204 No Content: Success with no data. Use for:

  • Successful DELETE with no return data
  • Successful update with no changed data to return

Response body is empty. Saves bandwidth for operations where response data isn't needed.

Document your choice:

"We return 201 Created for POST requests that create resources, with the new resource in the response body and its URL in the `Location` header. We return 204 No Content for DELETE requests since there's no meaningful data to return after deletion."

202 Accepted for Async Operations

If your API processes requests asynchronously (background jobs), 202 is critical:

"We return 202 Accepted for operations that take longer than 5 seconds. The response includes a job ID and status check URL. Poll the status URL until the job completes."

Example response:

```json
HTTP/1.1 202 Accepted

{
"job_id": "job_abc123",
"status": "processing",
"status_url": "https://api.example.com/jobs/job_abc123",
"estimated_completion": "2024-12-15T10:35:00Z"
}
```

Explain the polling pattern and how often to check ("Poll every 5 seconds, max 100 times before timing out").

The 4xx Client Errors: Blame Assignment

4xx codes mean the client did something wrong. Your documentation must explain what went wrong and how to fix it.

400 Bad Request: Be Specific

"Bad Request" is vague. Your API should return detailed validation errors:

```json
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed",
"details": [
{
"field": "email",
"message": "Invalid email format",
"received": "not-an-email"
},
{
"field": "age",
"message": "Must be a number between 1 and 120",
"received": "not a number"
}
]
}
}
```

Document your error response format and explain that developers should NOT retry 400 errors without fixing the request.

401 vs. 403: The Authentication vs. Authorization Confusion

Developers constantly confuse these. Make the distinction crystal clear:

401 Unauthorized: "Who are you?"

  • No credentials provided
  • Invalid API key
  • Expired access token

Action: Provide valid authentication credentials.

403 Forbidden: "I know who you are, but you can't do this."

  • Authenticated successfully
  • Lacks required permission
  • Account suspended
  • IP blocked

Action: Request permission, use different account, or contact admin.

Document explicitly:

"401 means authentication failed—check your API key or access token. 403 means you're authenticated but lack permission for this operation—you'll need the `users:write` permission to modify users."

404 vs. 403 for Security

Here's a design decision: If a user requests a resource they're not allowed to see, should you return 404 (resource doesn't exist) or 403 (you can't access this)?

403 (honest): Reveals the resource exists, potentially leaking information.
404 (security through obscurity): Hides existence of resource, but might confuse developers.

Most APIs choose 404 for security. Document this:

"We return 404 Not Found for resources that don't exist OR that you don't have permission to view. This prevents information disclosure. If you expected a resource to exist and get 404, verify both the resource ID and your permissions."

422 Unprocessable Entity for Semantic Validation

Use 422 when request is syntactically valid but semantically wrong:

  • 400: JSON is malformed, required field missing
  • 422: JSON is valid, but email domain is blacklisted, or withdrawal amount exceeds balance

Explain the distinction and when you use each.

Documenting your API's status codes and error handling?

River's AI creates comprehensive HTTP status code reference tables with examples, retry logic guidance, and client handling advice specific to your API.

Generate API Docs

The 5xx Server Errors: Retry Guidance is Critical

5xx errors mean you (the API) messed up, not the client. But clients still need to know what to do.

500 Internal Server Error

This is your catch-all for unexpected errors. Documentation should address:

What it means: "Something unexpected went wrong on our end. It's not your fault."

What to include in response:

  • Generic error message (don't expose stack traces)
  • Request ID for support/debugging
  • Link to status page if applicable

Retry guidance: "500 errors are often transient. Retry with exponential backoff (wait 1s, then 2s, then 4s, etc.). If errors persist after 5 attempts, alert your on-call team."

503 Service Unavailable

Use this for planned maintenance or when deliberately returning errors due to overload.

Always include `Retry-After` header indicating when to retry:

```http
HTTP/1.1 503 Service Unavailable
Retry-After: 300
```

"Retry-After: 300" means wait 300 seconds (5 minutes) before retrying.

Document: "503 means we're temporarily unavailable (maintenance or overload). Check the `Retry-After` header and wait that many seconds before retrying. Don't retry immediately or you'll make overload worse."

502 vs. 504: Gateway Errors

502 Bad Gateway: Upstream service returned invalid response
504 Gateway Timeout: Upstream service didn't respond in time

Both suggest transient issues with dependencies. Retry guidance:

"502 and 504 indicate problems with our upstream services. These are usually temporary. Retry with exponential backoff. If you're seeing consistent 504s, your requests may be too complex or timing out—consider breaking them into smaller requests."

Rate Limiting (429) and Retry-After

Rate limiting is essential but often poorly documented. Developers need to understand:

Your Rate Limit Policy

Be explicit about limits:

"Rate limits per API key:

  • Free tier: 100 requests/minute
  • Pro tier: 1,000 requests/minute
  • Enterprise: Custom limits

Limits are per API key with rolling 60-second windows. Exceeding limits returns 429 Too Many Requests."

Rate Limit Headers

Include rate limit info in every response:

```http
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 47
X-RateLimit-Reset: 1702650000
```

Explain each header:

  • X-RateLimit-Limit: Max requests in current window
  • X-RateLimit-Remaining: Requests left in window
  • X-RateLimit-Reset: Unix timestamp when limit resets

Handling 429 Errors

Provide clear guidance:

"When you receive 429:

  1. Read the `Retry-After` header (seconds to wait)
  2. Wait that many seconds
  3. Retry your request
  4. Monitor `X-RateLimit-Remaining` to avoid future rate limits

Don't retry immediately—you'll just get rate limited again. Consider batching requests, caching responses, or upgrading your plan if you regularly hit rate limits."

Idempotency and Safe Retries

Critical concept developers need to understand: which methods are safe to retry?

Idempotent Methods (Safe to Retry)

  • GET: Reading data, no side effects
  • PUT: Updating to specific state (repeating = same result)
  • DELETE: Deleting already-deleted resource = still deleted

"GET, PUT, and DELETE are idempotent. Retrying these methods (even multiple times) is safe and won't cause duplicate effects."

Non-Idempotent Method (POST)

POST is dangerous to retry:

"POST creates new resources or triggers actions. Retrying a POST may create duplicate resources or trigger duplicate actions (double charges, duplicate emails, etc.)."

Idempotency Keys for POST

If you support idempotency keys, document them:

"To safely retry POST requests, include an `Idempotency-Key` header with a unique value (we recommend UUIDs):

```http
POST /api/payments
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
```

If your request fails or times out, retry with the SAME idempotency key. We'll recognize the duplicate and return the original result instead of creating a duplicate payment.

Idempotency keys expire after 24 hours. Use a new key for genuinely new requests."

Retry Logic Best Practices

Provide developers with concrete retry guidance, ideally with code examples.

When to Retry

Retry these codes:

  • 408 Request Timeout
  • 429 Too Many Requests (after waiting)
  • 500 Internal Server Error
  • 502 Bad Gateway
  • 503 Service Unavailable
  • 504 Gateway Timeout
  • Network errors (connection failed, timeout)

Never retry these codes:

  • 400 Bad Request
  • 401 Unauthorized (fix auth first)
  • 403 Forbidden
  • 404 Not Found
  • 422 Unprocessable Entity

Exponential Backoff

Explain and provide example:

"Use exponential backoff with jitter to avoid thundering herd:

```python
import random
import time

def make_request_with_retry(url, max_attempts=5):
for attempt in range(max_attempts):
response = requests.get(url)

if response.status_code < 500:
return response

if attempt < max_attempts - 1:
wait = (2 ** attempt) + random.uniform(0, 1)
time.sleep(wait)

raise Exception("Max retries exceeded")
```

This waits 1s, 2s, 4s, 8s, 16s (with random jitter) between retries."

Common Mistakes in Status Code Documentation

Only listing status codes without context: "404: Not Found" tells developers nothing about your specific usage.

No retry guidance: Developers don't know whether to retry, how long to wait, or how many times to try.

Inconsistent error response format: Sometimes errors are strings, sometimes objects, sometimes arrays. Pick one format and stick to it.

No client action guidance: Every status code explanation should answer: "What do I do now?"

Missing rate limit documentation: Developers hit rate limits and don't know why or how to fix it.

Confusing 401 and 403: Use them consistently and explain the difference clearly.

No request IDs in errors: When developers contact support about errors, they have no way to reference specific failed requests.

Key Takeaways

HTTP status code documentation must go beyond RFC definitions to explain your specific implementation decisions. Provide comprehensive reference tables showing when each code is used, what's in the response, and what action clients should take.

Distinguish clearly between 401 (authentication failed) and 403 (authorization failed). Explain whether you return 404 or 403 for permission-denied resources and why.

For 4xx errors, provide detailed error response bodies with field-specific validation errors. Make it clear these shouldn't be retried without fixing the request.

For 5xx errors, provide retry guidance with exponential backoff recommendations. Include `Retry-After` headers for 429 and 503 responses. Explain which methods are safe to retry (GET, PUT, DELETE) vs. dangerous (POST without idempotency keys).

Document your rate limiting policy, include rate limit headers in responses, and explain how to handle 429 errors. Provide code examples for retry logic and idempotency key usage.

The status codes that cause fewest integration problems are the ones with clearest documentation about what they mean in your API and what developers should do when they receive them.

Frequently Asked Questions

Should we document every possible HTTP status code or just the ones we use?

Document only the codes you actually return. Including codes you never use creates confusion ('Do they return 418 I'm a Teapot or not?'). Focus on codes that matter to your API: success codes you use, client errors developers might trigger, server errors they'll encounter, and rate limiting. Typical APIs document 10-15 codes thoroughly.

When should we return 400 vs. 422 for validation errors?

Use 400 for syntactic errors (malformed JSON, wrong data types, missing required fields). Use 422 for semantic errors (valid format but business rule violation, like withdrawal exceeding balance or email domain blacklisted). Consistency matters more than perfect categorization—pick an approach and stick to it.

Should we expose detailed error messages or keep them vague for security?

Balance security and developer experience. Return detailed errors for client mistakes (validation failures, missing fields) since attackers could discover these anyway through testing. For server errors (5xx), return generic messages ('Internal server error') without exposing stack traces or database details. Always include request IDs for support.

How should we handle breaking changes to status codes?

Treat status code changes as breaking changes—clients may have logic that depends on specific codes. If you must change (e.g., fixing incorrect use of 404 vs. 403), version your API or provide long deprecation period with warnings in API responses. Document the change prominently in changelog and migration guide.

Should rate limit information be in headers or response body?

Both. Include X-RateLimit-* headers in every response (even successful ones) so clients can track limits proactively. In 429 responses, also include rate limit info in response body with 'Retry-After' for clients that don't parse headers. Redundancy helps developers integrate correctly.

What's the best way to test that our API returns correct status codes?

Write integration tests for each endpoint covering: success scenarios (200, 201, 204), validation failures (400, 422), authentication/authorization (401, 403), not found (404), rate limits (429), and error cases (500, 503). Use tools like Postman or REST-assured. Document expected codes in tests so they serve as living documentation.

Chandler Supple

Co-Founder & CTO at River

Chandler spent years building machine learning systems before realizing the tools he wanted as a writer didn't exist. He founded River to close that gap. In his free time, Chandler loves to read American literature, including Steinbeck and Faulkner.

About River

River is an AI-powered document editor built for professionals who need to write better, faster. From business plans to blog posts, River's AI adapts to your voice and helps you create polished content without the blank page anxiety.