Status codes are the API's way of saying "Here's what happened with your request."
Restaurant: "Here's your food, exactly as ordered"
When you see it: GET, PUT, PATCH requests succeeded
| GET /posts/42Response: 200 OKBody: { "id": 42, "title": "Hello World" } |
|---|
Restaurant: "Your order is placed and here's your receipt number"
When you see it: POST request successfully created something
| POST /postsResponse: 201 CreatedLocation: /posts/43Body: { "id": 43, "title": "New Post" } |
|---|
Key insight: Often includes a Location header pointing to the new resource!
Restaurant: "Your order is cancelled" (no food to bring back)
When you see it: DELETE succeeded, or update with no content to return
| DELETE /posts/42Response: 204 No Content(empty body) |
|---|
Restaurant: "We permanently moved to a new location"
Real-world: A company changes their website address
| GET /old-blogResponse: 301 Moved PermanentlyLocation: /new-blog |
|---|
Browser's reaction: "I'll automatically go to the new location and remember this forever"
Restaurant: "We're temporarily serving from the food truck outside"
Real-world: Maintenance mode, temporary URL changes
| GET /postsResponse: 302 FoundLocation: /maintenance |
|---|
Browser's reaction: "I'll go there now, but I'll try the original URL again next time"
Restaurant: "Same menu as yesterday, no need to print a new one"
Real-world: Caching - you already have the latest version
| GET /posts/42If-None-Match: "etag-12345"Response: 304 Not Modified |
|---|
Browser's reaction: "Great! I'll use my cached version"
Restaurant: "I can't understand your order"
Real-world: Malformed request, invalid JSON
POST /posts
| Body: { "title": } ← Invalid JSON!Response: 400 Bad Request{ "error": "Invalid JSON syntax" } |
|---|
Mental model: Like ordering "one of the purple" - what are you asking for?
Restaurant: "Show me your ID before I serve you alcohol"
Real-world: Missing or invalid authentication
| GET /admin/usersResponse: 401 Unauthorized{ "error": "Authentication required" } |
|---|
Common confusion: Despite the name, 401 means "NOT AUTHENTICATED" (you haven't identified yourself)
Restaurant: "Sorry, this is the VIP section"
Real-world: Authenticated but lacking permissions
| DELETE /users/adminResponse: 403 Forbidden{ "error": "Insufficient permissions" } |
|---|
Key difference from 401:
Restaurant: "We don't have that item on the menu"
Real-world: Resource doesn't exist
| GET /posts/999999Response: 404 Not Found{ "error": "Post not found" } |
|---|
Most famous code on the internet! Everyone's seen this.
Restaurant: "You already have an order in progress"
Real-world: Duplicate resource, conflicting state
| POST /usersBody: { "email": "existing@email.com" }Response: 409 Conflict{ "error": "Email already exists" } |
|---|
Restaurant: "You're ordering too fast, give us a minute"
Real-world: Rate limiting
| GET /api/data (100th request in 1 minute)Response: 429 Too Many RequestsRetry-After: 60{ "error": "Rate limit exceeded" } |
|---|
Restaurant: "The kitchen caught fire"
Real-world: Unhandled exception, code crashed
| GET /postsResponse: 500 Internal Server Error{ "error": "An unexpected error occurred" } |
|---|
What to do: Not your fault! Report to developers, try again later.
Restaurant: "Our supplier didn't deliver ingredients"
Real-world: Upstream service failed (database down, microservice timeout)
| GET /postsResponse: 502 Bad Gateway |
|---|
Mental model: Like a chain of dominoes - one service down affects others.
Restaurant: "Closed for maintenance"
| Real-world: Planned maintenance, overloaded serverGET /api/dataResponse: 503 Service UnavailableRetry-After: 3600 |
|---|
Did the request succeed? → YES: 2xx (200, 201, 204)
NO → Did the client make a mistake? → YES: 4xx
NO → Did the server make a mistake? → YES: 5xx