TL;DR
REST APIs should use HTTP status codes correctly: 200 for successful GET, 201 for successful POST with resource creation, 204 for successful DELETE, 400 for client errors, 401 for authentication failures, 404 for not found, and 500 for server errors. Modern PetstoreAPI implements all standard HTTP status codes with proper semantics and RFC 9457 error responses.
Introduction
Your API returns 200 OK for everything. Success? 200 OK. Validation error? 200 OK with an error message in the body. Resource not found? 200 OK with {"error": "not found"}. Authentication failure? You guessed it—200 OK.
This is wrong. HTTP status codes exist for a reason. They tell clients what happened without parsing the response body. Caches, proxies, and monitoring tools rely on status codes. When you return 200 for errors, you break the entire HTTP ecosystem.
The old Swagger Petstore made status code mistakes: returning 200 for POST requests that should return 201, using 200 for DELETE operations that should return 204, and missing important error codes. Modern PetstoreAPI fixes this by implementing proper HTTP semantics across all endpoints.
In this guide, you’ll learn which HTTP status codes matter for REST APIs, when to use each one, and how Modern PetstoreAPI implements them correctly.
The Status Code Problem
Many APIs treat status codes as an afterthought. The result: broken HTTP semantics and confused clients.
The “200 OK for Everything” Anti-Pattern
// Success
GET /users/123
200 OK
{"id": 123, "name": "John"}
// Error (but still 200!)
GET /users/999
200 OK
{"error": "User not found"}
// Validation error (still 200!)
POST /users
200 OK
{"error": "Email is required"}
Problems:
- Clients can’t distinguish success from failure without parsing the body
- HTTP caches cache error responses
- Monitoring tools report false positives
- Retry logic doesn’t work correctly
Why This Happens
Developers return 200 because:
- They don’t know the other status codes
- They think status codes are optional
- They want to avoid “breaking” clients
- They’re copying bad examples (like old Swagger Petstore)
Essential HTTP Status Codes for REST APIs
You don’t need all 60+ HTTP status codes. Focus on these essential ones.
Quick Reference
Success (2xx):
- 200 OK - Successful GET, PUT, PATCH
- 201 Created - Successful POST with resource creation
- 204 No Content - Successful DELETE, PUT with no response body
Client Errors (4xx):
- 400 Bad Request - Invalid request format or validation error
- 401 Unauthorized - Missing or invalid authentication
- 403 Forbidden - Authenticated but not authorized
- 404 Not Found - Resource doesn’t exist
- 409 Conflict - Resource conflict (duplicate, version mismatch)
- 422 Unprocessable Entity - Valid format but semantic errors
- 429 Too Many Requests - Rate limit exceeded
Server Errors (5xx):
- 500 Internal Server Error - Unexpected server error
- 502 Bad Gateway - Upstream service error
- 503 Service Unavailable - Temporary unavailability
- 504 Gateway Timeout - Upstream timeout
Success Codes (2xx)
Success codes indicate the request succeeded. Different codes convey different meanings.
200 OK
Use for: Successful GET, PUT, PATCH requests that return data.
GET /pets/123
200 OK
{
"id": "019b4132-70aa-764f-b315-e2803d882a24",
"name": "Fluffy",
"species": "CAT"
}
Don’t use for: POST requests that create resources (use 201), DELETE requests (use 204).
201 Created
Use for: Successful POST requests that create a new resource.
POST /pets
201 Created
Location: https://petstoreapi.com/pets/019b4132-70aa-764f-b315-e2803d882a24
{
"id": "019b4132-70aa-764f-b315-e2803d882a24",
"name": "Fluffy",
"species": "CAT"
}
Key points:
- Include
Locationheader with the new resource URL - Return the created resource in the response body
- Clients know a resource was created, not just updated
Modern PetstoreAPI returns 201 for all POST operations that create resources.
204 No Content
Use for: Successful DELETE, PUT, or PATCH requests with no response body.
DELETE /pets/019b4132-70aa-764f-b315-e2803d882a24
204 No Content
Key points:
- No response body (saves bandwidth)
- Indicates success
- Common for DELETE operations
Client Error Codes (4xx)
Client error codes indicate the client made a mistake. The request should not be retried without modification.
400 Bad Request
Use for: Malformed requests, invalid JSON, missing required fields.
POST /pets
400 Bad Request
Content-Type: application/problem+json
{
"type": "https://petstoreapi.com/errors/validation-error",
"title": "Validation Error",
"status": 400,
"detail": "Request validation failed",
"invalid-params": [
{
"name": "name",
"reason": "Name is required"
}
]
}
Modern PetstoreAPI uses RFC 9457 format for all error responses.
401 Unauthorized
Use for: Missing or invalid authentication credentials.
GET /pets
401 Unauthorized
WWW-Authenticate: Bearer realm="PetstoreAPI"
{
"type": "https://petstoreapi.com/errors/authentication-required",
"title": "Authentication Required",
"status": 401,
"detail": "Valid authentication credentials required"
}
Key points:
- Include
WWW-Authenticateheader - Client should prompt for credentials or refresh token
- Don’t confuse with 403 (authorization)
403 Forbidden
Use for: Authenticated user lacks permission.
DELETE /pets/019b4132-70aa-764f-b315-e2803d882a24
403 Forbidden
{
"type": "https://petstoreapi.com/errors/insufficient-permissions",
"title": "Insufficient Permissions",
"status": 403,
"detail": "You don't have permission to delete this pet"
}
Difference from 401:
- 401: “Who are you?” (authentication)
- 403: “I know who you are, but you can’t do that” (authorization)
404 Not Found
Use for: Resource doesn’t exist.
GET /pets/nonexistent-id
404 Not Found
{
"type": "https://petstoreapi.com/errors/not-found",
"title": "Not Found",
"status": 404,
"detail": "Pet not found"
}
Don’t use for: Authorization failures (use 403), validation errors (use 400).
409 Conflict
Use for: Resource conflicts like duplicates or version mismatches.
POST /pets
409 Conflict
{
"type": "https://petstoreapi.com/errors/duplicate-resource",
"title": "Duplicate Resource",
"status": 409,
"detail": "A pet with this microchip ID already exists"
}
422 Unprocessable Entity
Use for: Valid request format but semantic errors.
POST /pets
422 Unprocessable Entity
{
"type": "https://petstoreapi.com/errors/business-rule-violation",
"title": "Business Rule Violation",
"status": 422,
"detail": "Cannot adopt more than 5 pets per household"
}
Difference from 400:
- 400: Malformed request (invalid JSON, wrong types)
- 422: Well-formed request but violates business rules
429 Too Many Requests
Use for: Rate limit exceeded.
GET /pets
429 Too Many Requests
RateLimit-Limit: 100
RateLimit-Remaining: 0
RateLimit-Reset: 1678886400
{
"type": "https://petstoreapi.com/errors/rate-limit-exceeded",
"title": "Rate Limit Exceeded",
"status": 429,
"detail": "Rate limit of 100 requests per hour exceeded"
}
Modern PetstoreAPI uses IETF rate limit headers.
Server Error Codes (5xx)
Server error codes indicate the server failed. Clients can retry these requests.
500 Internal Server Error
Use for: Unexpected server errors.
GET /pets
500 Internal Server Error
{
"type": "https://petstoreapi.com/errors/internal-error",
"title": "Internal Server Error",
"status": 500,
"detail": "An unexpected error occurred"
}
Don’t include: Stack traces, internal details, database errors in production.
503 Service Unavailable
Use for: Temporary unavailability (maintenance, overload).
GET /pets
503 Service Unavailable
Retry-After: 3600
{
"type": "https://petstoreapi.com/errors/service-unavailable",
"title": "Service Unavailable",
"status": 503,
"detail": "Service temporarily unavailable for maintenance"
}
Include Retry-After header to tell clients when to retry.
How Modern PetstoreAPI Uses Status Codes
Modern PetstoreAPI implements proper HTTP semantics across all endpoints.
Example: Pet Management
// List pets
GET /pets
200 OK
// Create pet
POST /pets
201 Created
Location: https://petstoreapi.com/pets/{id}
// Get pet
GET /pets/{id}
200 OK (found) or 404 Not Found
// Update pet
PUT /pets/{id}
200 OK (with body) or 204 No Content
// Delete pet
DELETE /pets/{id}
204 No Content (success) or 404 Not Found
Error Responses
All errors use RFC 9457 format:
{
"type": "https://petstoreapi.com/errors/validation-error",
"title": "Validation Error",
"status": 400,
"detail": "Request validation failed",
"instance": "/pets",
"invalid-params": [
{
"name": "name",
"reason": "Name must be between 1 and 100 characters"
}
]
}
See the Modern PetstoreAPI error handling documentation for complete examples.
Testing Status Codes with Apidog
Apidog helps you test status code behavior across all scenarios.
Define Expected Status Codes
paths:
/pets:
post:
responses:
'201':
description: Pet created
'400':
description: Validation error
'401':
description: Authentication required
'429':
description: Rate limit exceeded
Test All Scenarios
Create test cases for:
- Success scenarios (200, 201, 204)
- Validation errors (400, 422)
- Authentication/authorization (401, 403)
- Not found (404)
- Rate limiting (429)
- Server errors (500, 503)
Automated Testing
// Apidog test script
pm.test("Returns 201 for successful creation", () => {
pm.response.to.have.status(201);
pm.response.to.have.header("Location");
});
pm.test("Returns 400 for missing required fields", () => {
pm.response.to.have.status(400);
pm.expect(pm.response.json().type).to.include("validation-error");
});
Common Mistakes to Avoid
Mistake 1: Using 200 for POST
// Wrong
POST /pets
200 OK
// Correct
POST /pets
201 Created
Location: https://petstoreapi.com/pets/{id}
Mistake 2: Using 200 for DELETE
// Wrong
DELETE /pets/{id}
200 OK
{"message": "Deleted successfully"}
// Correct
DELETE /pets/{id}
204 No Content
Mistake 3: Confusing 401 and 403
// Wrong: User is authenticated but lacks permission
401 Unauthorized
// Correct
403 Forbidden
Mistake 4: Using 500 for Client Errors
// Wrong: Validation error returns 500
POST /pets
500 Internal Server Error
// Correct
POST /pets
400 Bad Request
Conclusion
HTTP status codes are not optional. They’re part of the HTTP specification and essential for building proper REST APIs.
Use the right status codes:
- 200 for successful reads
- 201 for successful creates
- 204 for successful deletes
- 400 for client errors
- 401 for authentication
- 404 for not found
- 500 for server errors
Modern PetstoreAPI demonstrates correct status code usage across all endpoints. Study the REST API documentation to see proper implementation.
Test your status codes with Apidog to ensure your API follows HTTP standards.
FAQ
Should I use 200 or 204 for successful DELETE?
Use 204 No Content. It indicates success without a response body, saving bandwidth. Use 200 only if you need to return information about the deleted resource.
What’s the difference between 400 and 422?
400 means the request is malformed (invalid JSON, wrong types). 422 means the request is well-formed but violates business rules.
When should I use 401 vs 403?
401 means “authenticate yourself” (missing or invalid credentials). 403 means “you’re authenticated but not authorized” (insufficient permissions).
Should I return 404 or 403 for resources users can’t access?
Return 403 if the resource exists but the user lacks permission. Return 404 if you want to hide the resource’s existence from unauthorized users.
How do I test all status code scenarios?
Use Apidog to create test cases for success, validation errors, authentication failures, not found, and server errors. Run automated tests in CI/CD.
What status code for rate limiting?
Use 429 Too Many Requests with RateLimit-* headers. Include Retry-After to tell clients when they can retry.
Should I use 500 for all server errors?
Use 500 for unexpected errors. Use 502 for upstream service failures, 503 for temporary unavailability, and 504 for timeouts.
How does Modern PetstoreAPI handle errors?
All errors use RFC 9457 format with proper status codes. See the error handling documentation for examples.



