Which HTTP Status Codes Should REST APIs Actually Use?

Most REST APIs misuse HTTP status codes, returning 200 OK for everything. Learn which status codes matter, when to use them, and how Modern PetstoreAPI implements proper HTTP semantics.

Ashley Innocent

Ashley Innocent

13 March 2026

Which HTTP Status Codes Should REST APIs Actually Use?

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.

💡
If you’re building or testing REST APIs, Apidog helps you validate status code usage, test error scenarios, and ensure your API follows HTTP standards. You can define expected status codes, run automated tests, and catch incorrect responses before deployment.
button

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:

Why This Happens

Developers return 200 because:

  1. They don’t know the other status codes
  2. They think status codes are optional
  3. They want to avoid “breaking” clients
  4. 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):

Client Errors (4xx):

Server Errors (5xx):

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:

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:

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:

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:

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:

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:

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:

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.

Explore more

Socket.IO vs Native WebSocket: Which Should You Use?

Socket.IO vs Native WebSocket: Which Should You Use?

Socket.IO adds features like automatic reconnection and fallbacks, but Native WebSocket is simpler and faster. Learn when to use each and how Modern PetstoreAPI implements both.

13 March 2026

When Should You Use MQTT Instead of HTTP for APIs?

When Should You Use MQTT Instead of HTTP for APIs?

MQTT excels for IoT devices with limited bandwidth and unreliable networks. Learn when MQTT beats HTTP and how Modern PetstoreAPI uses MQTT for pet tracking devices and smart feeders.

13 March 2026

WebSocket vs Server-Sent Events: Which Is Better for Real-Time APIs?

WebSocket vs Server-Sent Events: Which Is Better for Real-Time APIs?

WebSocket and Server-Sent Events both enable real-time communication, but they solve different problems. Learn when to use each and how Modern PetstoreAPI implements both protocols.

13 March 2026

Practice API Design-first in Apidog

Discover an easier way to build and use APIs