Using the wrong HTTP status code is the #1 REST API mistake. This cheat sheet covers all 50+ status codes with when to use each one, plus code examples you can copy.
Quick Reference Table: Most Common Status Codes
| Code | Name | When to Use |
|---|---|---|
| 200 | OK | Successful GET, PUT, PATCH, DELETE with response body |
| 201 | Created | Successful POST that created a new resource |
| 204 | No Content | Successful request with no response body (DELETE) |
| 301 | Moved Permanently | Resource permanently moved to new URL |
| 304 | Not Modified | Cached resource unchanged (conditional GET) |
| 400 | Bad Request | Invalid request syntax, malformed JSON |
| 401 | Unauthorized | Missing or invalid authentication |
| 403 | Forbidden | Authenticated but insufficient permissions |
| 404 | Not Found | Resource doesn't exist at this URL |
| 409 | Conflict | Request conflicts with current state (duplicate) |
| 422 | Unprocessable Entity | Valid syntax but semantic errors (validation) |
| 429 | Too Many Requests | Rate limit exceeded |
| 500 | Internal Server Error | Unexpected server error (bug, crash) |
| 502 | Bad Gateway | Invalid response from upstream server |
| 503 | Service Unavailable | Server temporarily unavailable (maintenance) |
| 504 | Gateway Timeout | Upstream server didn't respond in time |
2xx Success Codes (Detailed)
200 OK
Use for: Successful GET, PUT, PATCH, or DELETE when returning response body.
// GET /users/123
HTTP/1.1 200 OK
Content-Type: application/json
{
"id": 123,
"name": "John Doe",
"email": "john@example.com"
}
201 Created
Use for: Successful POST that creates a new resource. Include Location header.
// POST /users
HTTP/1.1 201 Created
Content-Type: application/json
Location: /users/456
{
"id": 456,
"name": "Jane Doe",
"email": "jane@example.com"
}
204 No Content
Use for: Successful operation with no response body. Common for DELETE.
// DELETE /users/123 HTTP/1.1 204 No Content
202 Accepted
Use for: Async operations where processing continues in background.
// POST /reports (starts long-running job)
HTTP/1.1 202 Accepted
Content-Type: application/json
{
"job_id": "abc123",
"status": "processing",
"check_url": "/jobs/abc123"
}
3xx Redirection Codes
301 Moved Permanently
Use for: Resource permanently moved. Browsers/crawlers update bookmarks.
// GET /api/v1/users (deprecated) HTTP/1.1 301 Moved Permanently Location: /api/v2/users
302 Found (Temporary Redirect)
Use for: Temporary redirect. Client should continue using original URL.
304 Not Modified
Use for: Conditional GET where resource hasn't changed. Saves bandwidth.
// GET /users/123 with If-None-Match header HTTP/1.1 304 Not Modified ETag: "abc123"
4xx Client Error Codes (The Tricky Ones)
400 Bad Request
Use for: Malformed syntax, invalid JSON, type errors.
// POST /users with invalid JSON
HTTP/1.1 400 Bad Request
Content-Type: application/json
{
"error": {
"code": "invalid_json",
"message": "Request body is not valid JSON",
"details": "Unexpected token at position 42"
}
}
401 Unauthorized vs 403 Forbidden
This is the most commonly confused pair.
| Scenario | Use Code | Why |
|---|---|---|
| No token provided | 401 | Not authenticated |
| Invalid/expired token | 401 | Not authenticated |
| Valid token, wrong role | 403 | Authenticated but not authorized |
| Valid token, resource forbidden | 403 | Authenticated but not authorized |
401 Unauthorized (actually means "Unauthenticated"):
// GET /account without Authorization header
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer
{
"error": {
"code": "authentication_required",
"message": "Please provide a valid access token"
}
}
403 Forbidden:
// GET /admin/users with valid user token (not admin)
HTTP/1.1 403 Forbidden
{
"error": {
"code": "insufficient_permissions",
"message": "Admin role required to access this resource"
}
}
404 Not Found
Use for: Resource doesn't exist at this URL.
// GET /users/99999
HTTP/1.1 404 Not Found
{
"error": {
"code": "not_found",
"message": "User with ID 99999 not found"
}
}
409 Conflict
Use for: Request conflicts with current resource state (duplicates, versioning).
// POST /users with existing email
HTTP/1.1 409 Conflict
{
"error": {
"code": "duplicate_email",
"message": "A user with this email already exists"
}
}
422 Unprocessable Entity vs 400 Bad Request
| Scenario | Use Code | Why |
|---|---|---|
| Invalid JSON syntax | 400 | Malformed request |
| Missing required field | 422 | Valid syntax, validation failed |
| Invalid email format | 422 | Valid syntax, semantic error |
| Type mismatch (string instead of int) | 400 | Schema violation |
// POST /users with invalid email (valid JSON, semantic error)
HTTP/1.1 422 Unprocessable Entity
{
"error": {
"code": "validation_error",
"message": "Validation failed",
"details": [
{
"field": "email",
"message": "Invalid email format"
},
{
"field": "age",
"message": "Must be a positive number"
}
]
}
}
429 Too Many Requests
Use for: Rate limit exceeded. Include Retry-After header.
HTTP/1.1 429 Too Many Requests
Retry-After: 60
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1704067200
{
"error": {
"code": "rate_limit_exceeded",
"message": "Rate limit exceeded. Please retry after 60 seconds."
}
}
5xx Server Error Codes
500 Internal Server Error
Use for: Unexpected server errors (bugs, uncaught exceptions).
HTTP/1.1 500 Internal Server Error
{
"error": {
"code": "internal_error",
"message": "An unexpected error occurred. Please try again.",
"request_id": "req_abc123" // For debugging
}
}
502 Bad Gateway
Use for: Your server received invalid response from upstream service.
503 Service Unavailable
Use for: Server temporarily unavailable (maintenance, overload).
HTTP/1.1 503 Service Unavailable
Retry-After: 3600
{
"error": {
"code": "service_unavailable",
"message": "Service is under maintenance. Please try again in 1 hour."
}
}
504 Gateway Timeout
Use for: Upstream service didn't respond in time.
Complete Status Code Reference
1xx Informational
| Code | Name | Use Case |
|---|---|---|
| 100 | Continue | Server received headers, client should send body |
| 101 | Switching Protocols | Upgrading to WebSocket |
| 102 | Processing | Request received, still processing (WebDAV) |
2xx Success
| Code | Name | Use Case |
|---|---|---|
| 200 | OK | Standard successful response |
| 201 | Created | Resource created successfully |
| 202 | Accepted | Request accepted, processing async |
| 204 | No Content | Success with no response body |
| 206 | Partial Content | Range request fulfilled |
3xx Redirection
| Code | Name | Use Case |
|---|---|---|
| 301 | Moved Permanently | Resource permanently moved |
| 302 | Found | Temporary redirect |
| 304 | Not Modified | Cached resource unchanged |
| 307 | Temporary Redirect | Redirect preserving method |
| 308 | Permanent Redirect | Permanent redirect preserving method |
4xx Client Errors
| Code | Name | Use Case |
|---|---|---|
| 400 | Bad Request | Malformed syntax |
| 401 | Unauthorized | Not authenticated |
| 403 | Forbidden | Authenticated but not authorized |
| 404 | Not Found | Resource doesn't exist |
| 405 | Method Not Allowed | HTTP method not supported |
| 409 | Conflict | Conflicts with current state |
| 410 | Gone | Resource permanently deleted |
| 413 | Payload Too Large | Request body too large |
| 415 | Unsupported Media Type | Content-Type not supported |
| 422 | Unprocessable Entity | Validation errors |
| 429 | Too Many Requests | Rate limited |
5xx Server Errors
| Code | Name | Use Case |
|---|---|---|
| 500 | Internal Server Error | Unexpected server error |
| 501 | Not Implemented | Server doesn't support functionality |
| 502 | Bad Gateway | Invalid upstream response |
| 503 | Service Unavailable | Temporarily unavailable |
| 504 | Gateway Timeout | Upstream timeout |
Frequently Asked Questions
What's the difference between 401 and 403?
401 = "Who are you?" (not authenticated). 403 = "I know who you are, but you can't do this" (not authorized). Use 401 when no valid credentials provided. Use 403 when credentials are valid but the user lacks permission for this specific action.
When should I use 400 vs 422?
400 = malformed request (can't parse). 422 = valid syntax but semantic errors (validation failed). Invalid JSON → 400. Valid JSON with invalid email format → 422. Some APIs use 400 for both, which is acceptable but less precise.
Should DELETE return 200 or 204?
204 if no response body, 200 if returning deleted resource data. 204 is more common and saves bandwidth. 200 with the deleted object is useful if clients need confirmation of what was deleted.
What should I return for a missing optional resource?
Return 200 with null or empty array, not 404. GET /users/123/avatar when user has no avatar → 200 with {"avatar": null}. 404 means "this URL doesn't exist." A user without an avatar is different from a nonexistent user.
Can I create custom status codes?
No. Use standard codes only. HTTP status codes are standardized. Custom codes (e.g., 499) break standard tooling and confuse developers. Put custom error information in the response body with standard status codes.
What status code for partial success in batch operations?
207 Multi-Status (WebDAV) or 200 with detailed response body. When a batch operation succeeds for some items and fails for others, return 200/207 with per-item status in the response body. Don't return 500 for partial failures.
Use this cheat sheet as your HTTP status code reference. For faster API documentation, try River's API documentation tools.