DEV Community

Cover image for Which HTTP Status Codes Should REST APIs Actually Use?
Wanda
Wanda

Posted on • Originally published at apidog.com

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
  • 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.

Try Apidog today

đź’ˇ 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"}
Enter fullscreen mode Exit fullscreen mode

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:

  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 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"
}
Enter fullscreen mode Exit fullscreen mode

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"
}
Enter fullscreen mode Exit fullscreen mode

Key points:

  • Include Location header 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
Enter fullscreen mode Exit fullscreen mode

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"}
  ]
}
Enter fullscreen mode Exit fullscreen mode

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"
}
Enter fullscreen mode Exit fullscreen mode

Key points:

  • Include WWW-Authenticate header
  • 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"
}
Enter fullscreen mode Exit fullscreen mode

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"
}
Enter fullscreen mode Exit fullscreen mode

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"
}
Enter fullscreen mode Exit fullscreen mode

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"
}
Enter fullscreen mode Exit fullscreen mode

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"
}
Enter fullscreen mode Exit fullscreen mode

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"
}
Enter fullscreen mode Exit fullscreen mode

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"
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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");
});
Enter fullscreen mode Exit fullscreen mode

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}
Enter fullscreen mode Exit fullscreen mode

Mistake 2: Using 200 for DELETE

// Wrong
DELETE /pets/{id}
200 OK
{"message": "Deleted successfully"}

// Correct
DELETE /pets/{id}
204 No Content
Enter fullscreen mode Exit fullscreen mode

Mistake 3: Confusing 401 and 403

// Wrong: User is authenticated but lacks permission
401 Unauthorized

// Correct
403 Forbidden
Enter fullscreen mode Exit fullscreen mode

Mistake 4: Using 500 for Client Errors

// Wrong: Validation error returns 500
POST /pets
500 Internal Server Error

// Correct
POST /pets
400 Bad Request
Enter fullscreen mode Exit fullscreen mode

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)