Engineering Guide
A Pragmatic Guide to HTTP Status Codes
Stop returning 200 OK for errors. Learn when to actually use 400 vs 422, and how to structure error payloads.
The Big Three Categories
In modern API development, you realistically only need a handful of status codes. Trying to map exact HTTP specifications to business logic is an anti-pattern. Stick to the basics.
2xx: Success
- 200 OK: Standard response for successful GET, PUT, or PATCH.
- 201 Created: Return this when a POST request successfully creates a resource. Include the new resource ID in the response body.
- 204 No Content: Best used for DELETE requests where no body needs to be returned.
4xx: Client Errors (The user messed up)
This is where most debates happen. Here is a pragmatic approach:
| Code | Use Case | Example |
|---|---|---|
| 400 Bad Request | Malformed JSON syntax or completely missing required fields. | {"error": "Invalid JSON payload"} |
| 401 Unauthorized | Missing or invalid authentication token. | {"error": "Token expired"} |
| 403 Forbidden | Token is valid, but the user lacks permissions for this specific action. | {"error": "Requires Admin role"} |
| 404 Not Found | Resource ID does not exist in the database. | {"error": "User usr_123 not found"} |
| 422 Unprocessable | Valid JSON, but business logic validation failed (e.g. email already in use). | {"error": "Email must be unique"} |
5xx: Server Errors (We messed up)
You should never intentionally return a 500 error from your application code. These represent unhandled exceptions or infrastructure failures (database down). If a database fails during a transaction, your framework's error handler should catch the crash and return a generic 500 without leaking stack traces.
Structuring Error Payloads
A status code alone is not enough. You must provide context. We recommend following RFC 7807 (Problem Details for HTTP APIs).
{
"type": "https://api.example.com/probs/out-of-credit",
"title": "You do not have enough credit.",
"status": 403,
"detail": "Your current balance is 30, but that costs 50.",
"instance": "/account/12345/msgs/abc"
}
If you want to practice handling these errors, use our Mock API Generator to stub out endpoints that return specific 4xx and 5xx codes to test your frontend error boundaries.