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
- 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? Still 200 OK.
This is incorrect. HTTP status codes exist to communicate results without parsing the body. Caches, proxies, and monitoring tools rely on them. Returning 200 for errors breaks the HTTP ecosystem.
The old Swagger Petstore made status code mistakes: returning 200 for POSTs that should be 201, using 200 for DELETE instead of 204, and missing key error codes. Modern PetstoreAPI fixes this with proper HTTP semantics.
đź’ˇ Tip: Apidog helps you validate status code usage, test error scenarios, and ensure your API follows HTTP standards. Define expected status codes, run automated tests, and catch incorrect responses before deployment.
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.
The Status Code Problem
Many APIs treat status codes as an afterthought, leading to 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 essentials.
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/validation error
- 401 Unauthorized – Missing/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 – Semantic errors
- 429 Too Many Requests – Rate limit exceeded
Server Errors (5xx):
- 500 Internal Server Error – Unexpected 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. Use different codes for different cases.
200 OK
Use for: Successful GET, PUT, PATCH that return data.
GET /pets/123
200 OK
{
"id": "019b4132-70aa-764f-b315-e2803d882a24",
"name": "Fluffy",
"species": "CAT"
}
Don’t use for: POST creates (use 201), DELETE (use 204).
201 Created
Use for: Successful POST that creates 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 new resource URL - Return the created resource in the body
Modern PetstoreAPI returns 201 for all POST creates.
204 No Content
Use for: Successful DELETE, PUT, PATCH 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
Client Error Codes (4xx)
Client errors mean the request was invalid. Do not retry without changes.
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 error format.
401 Unauthorized
Use for: Missing or invalid authentication.
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 - Prompt client for credentials or refresh token
- Don’t confuse with 403
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"
}
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 (duplicates, 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: Well-formed request but semantic/business rule 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"
}
400: Malformed request (invalid JSON, wrong types)
422: Well-formed, but breaks 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 errors indicate a failure on the server. Clients may retry.
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 or internal details 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 inform clients when to retry.
How Modern PetstoreAPI Uses Status Codes
Modern PetstoreAPI applies proper HTTP semantics on 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 more examples.
Testing Status Codes with Apidog
Apidog helps you test status codes in 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 (200, 201, 204)
- Validation errors (400, 422)
- Auth/authz (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 required by the HTTP specification and essential for building correct REST APIs.
Use the right codes:
- 200 for reads
- 201 for creates
- 204 for deletes
- 400 for client errors
- 401 for authentication
- 404 for not found
- 500 for server errors
Modern PetstoreAPI demonstrates correct usage. Study the REST API documentation for implementation details.
Test your status codes with Apidog to ensure you meet HTTP standards.
FAQ
Should I use 200 or 204 for successful DELETE?
Use 204 No Content. It indicates success without a response body. Use 200 only if you need to return information about the deleted resource.
What’s the difference between 400 and 422?
400: The request is malformed (invalid JSON, wrong types).
422: The request is well-formed but violates business rules.
When should I use 401 vs 403?
401: “Authenticate yourself” (missing or invalid credentials).
403: “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 inform clients when they can retry.
Should I use 500 for all server errors?
Use 500 for unexpected errors. Use 502 for upstream 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.
Top comments (0)