| |

REST API 05 ๐ŸŒ HTTP Status Codes (2xx, 3xx, 4xx, 5xx)

โฑ๏ธ Estimated read: 12 min | |

HTTP status codes are the machine-readable verdict of every API call. A client reads the first digit to decide whether to parse the body, retry the request, or surface an error to the user. A monitoring system counts 5xx responses to page an on-call engineer and ignores 4xx responses because they indicate client mistakes, not server failures. The code is not decorationโ€”it is the contract that determines how every layer of the stack behaves before a single byte of the body is parsed .

The five families are defined by their first digit. 2xx means the request was received, understood, and accepted. 3xx means the client must take further action, usually following a Location header. 4xx means the client made a mistakeโ€”the request is malformed, unauthenticated, or references something that does not exist. 5xx means the server failed to fulfill a valid request . The single most important rule is never return 200 OK for a failure. A 200 wrapping {"error": "not found"} forces every client to parse bodies to detect errors, breaks caching, and hides failures from monitoring .

Key point: 2xx means success, 3xx means redirection, 4xx means client error (the client must change the request before retrying), and 5xx means server error (the request was valid, the server failed). Map every outcome to the narrowest code that describes it, never return 200 for a failure, and distinguish 401 (who are you?) from 403 (I know who you are, but you cannot do this).


Why the family matters more than the exact code

The monitoring problem. A 5xx spike pages an engineer because it means the server is broken. A 4xx spike does not, because it means clients are sending bad requests. If a server returns 200 for a validation error, the monitoring system sees success, the error rate stays at zero, and the broken endpoint goes unnoticed until a user complains. The family classification is what allows infrastructure to react correctly without understanding the application .

The retry problem. A client that receives a 5xx knows the request was valid and may retry. A client that receives a 4xx knows the request was wrong and retrying without changing it will fail again. A client that receives a 200 with an error body has no way to distinguish these cases without application-specific parsing, which defeats the purpose of a uniform interface .

The security problem. The choice between 403 and 404 can leak information. Returning 403 for a resource the user may not access confirms that the resource exists. Returning 404 uniformly hides its existence. For sensitive resources, the 404 is the safer choice . The choice between 401 and 403 has the same property: 401 means “I do not know who you are,” 403 means “I know who you are, and you are not allowed.” Returning 401 when the user is authenticated but unauthorized confuses the client and hides attacks from log analysis.


a. 2xx: success

The 2xx family means the request was successfully received, understood, and accepted . The two codes that matter most are 200 OK and 201 Created.

200 OK is the standard response for a successful GET, and for a PUT or PATCH that returns the updated resource in the body. The body contains the representation, and the client can parse it directly .

201 Created is the response for a successful POST that creates a new resource. It must include a Location header pointing to the new resource’s URI, and the body should contain the created representation .

POST /users HTTP/1.1
Content-Type: application/json

{ "name": "Alice" }

HTTP/1.1 201 Created
Location: /users/42
Content-Type: application/json

{ "id": 42, "name": "Alice" }

204 No Content is the response for a successful operation that has no body to return. It is the standard response for DELETE, and for PUT or PATCH when the server does not return the updated representation. The absence of a body is the signal: the client knows the operation succeeded because the status code says so, and there is nothing to parse .

202 Accepted is for asynchronous operations. The request was valid, but the processing is not complete. The body should contain a URL to check the status or a Retry-After header .


b. 3xx: redirection

The 3xx family means the client must take further action to complete the request, usually by following a Location header . These codes appear less often in API responses than in browser navigation, but they are not absent.

301 Moved Permanently means the resource has a new permanent URI. The client should update its bookmarks and use the new URI for future requests. 302 Found means a temporary redirect. 304 Not Modified is the caching redirect: the client’s cached copy is still valid, so the server returns only the status code and no body. The client uses its cache .

307 Temporary Redirect and 308 Permanent Redirect preserve the request method across the redirect. A POST that receives a 307 should re-send the POST to the new URI, whereas a 302 historically allowed clients to change the method to GET. For APIs, the method-preserving variants are the safer choice .


c. 4xx: client error

The 4xx family means the client made a mistake. The request is malformed, unauthenticated, or references a resource that does not exist. Retrying without changing the request will fail again .

400 Bad Request is for malformed syntax: invalid JSON, missing required fields, or a request body that does not match the expected schema. The client must fix the request before retrying .

401 Unauthorized means authentication is missing or invalid. The response should include a WWW-Authenticate header describing the authentication scheme. The client should prompt for credentials or refresh its token .

403 Forbidden means the server understood the request and the user is authenticated, but the user does not have permission to perform the action. The difference from 401 is precise: 401 is “who are you?”, 403 is “I know who you are, and you cannot do this” .

404 Not Found means the requested resource does not exist. It is the standard response for a GET on a non-existent ID, and for a DELETE on a resource that has already been removed .

405 Method Not Allowed means the URI exists but does not support the HTTP method used. The response should include an Allow header listing the supported methods .

409 Conflict means the request conflicts with the current state of the resource. A duplicate email on registration, a version mismatch on an optimistic update, or a state transition that is not allowed all produce a 409 .

422 Unprocessable Entity means the request is syntactically correct but semantically invalid. The JSON parses, the required fields are present, but a business rule is violated: an age is negative, a date range is backwards, a quantity exceeds the allowed maximum. The distinction from 400 is deliberate: 400 is “I cannot parse this,” 422 is “I parsed this and it violates a rule” .

429 Too Many Requests means the client has exceeded a rate limit. The response should include a Retry-After header telling the client when it may try again .


d. 5xx: server error

The 5xx family means the server failed to fulfill a valid request. The client did nothing wrong, and the request may succeed if retried later .

500 Internal Server Error is the generic catch-all. It means an unexpected condition prevented the server from fulfilling the request. The response body must not contain stack traces, database error messages, or internal detailsโ€”those belong in the server logs, not in the response .

503 Service Unavailable means the server is temporarily unable to handle the request, usually due to overload or maintenance. The response should include a Retry-After header telling the client when the service is expected to return .


Complete Example Session

The following session traces a set of API calls that produce each status code family and shows the response headers and bodies that accompany them.

// ============================================
// PART 1: 201 CREATED (POST)
// ============================================
POST /users HTTP/1.1
Content-Type: application/json

{ "name": "Alice" }

HTTP/1.1 201 Created
Location: /users/42
Content-Type: application/json

{ "id": 42, "name": "Alice" }
// The Location header points to the new resource
// The body contains the created representation

// ============================================
// PART 2: 200 OK (GET)
// ============================================
GET /users/42 HTTP/1.1

HTTP/1.1 200 OK
Content-Type: application/json

{ "id": 42, "name": "Alice" }
// Standard success for retrieval

// ============================================
// PART 3: 204 NO CONTENT (DELETE)
// ============================================
DELETE /users/42 HTTP/1.1

HTTP/1.1 204 No Content
// No body; the status code is the confirmation

// ============================================
// PART 4: 404 NOT FOUND (GET on missing)
// ============================================
GET /users/99 HTTP/1.1

HTTP/1.1 404 Not Found
Content-Type: application/problem+json

{
  "type": "https://api.example.com/errors/not-found",
  "title": "Not Found",
  "status": 404,
  "detail": "User 99 does not exist"
}
// The client knows the resource is missing without parsing an error field

// ============================================
// PART 5: 401 vs 403
// ============================================
GET /admin HTTP/1.1
// No Authorization header

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer
// "Who are you?" โ€” authentication missing

GET /admin HTTP/1.1
Authorization: Bearer valid-token-for-alice

HTTP/1.1 403 Forbidden
// "I know you are Alice, but you are not an admin"

// ============================================
// PART 6: 400 vs 422
// ============================================
POST /users HTTP/1.1
Content-Type: application/json

{ "name": "Bob", // invalid JSON, missing closing brace

HTTP/1.1 400 Bad Request
// The server cannot parse the body

POST /users HTTP/1.1
Content-Type: application/json

{ "name": "Bob", "age": -5 }

HTTP/1.1 422 Unprocessable Entity
// The JSON parses, but age is invalid

// ============================================
// PART 7: 500 INTERNAL SERVER ERROR
// ============================================
GET /users/42 HTTP/1.1

HTTP/1.1 500 Internal Server Error
Content-Type: application/problem+json

{
  "type": "https://api.example.com/errors/internal",
  "title": "Internal Server Error",
  "status": 500,
  "detail": "An unexpected error occurred"
}
// No stack trace, no database details, no internal paths

The seven parts demonstrate the full lifecycle: 201 for creation with Location, 200 for retrieval, 204 for deletion with no body, 404 for a missing resource, the 401/403 distinction, the 400/422 distinction, and 500 with a sanitized error body. What the session demonstrates is that the status code is the primary signalโ€”the body is secondaryโ€”and the family classification determines how the client and the infrastructure react.


Quick Reference

Status Code Families

FamilyMeaningClient Action
2xxSuccessProcess the body
3xxRedirectionFollow Location
4xxClient errorFix the request
5xxServer errorRetry or report

Common 2xx Codes

CodeUse
200 OKSuccessful GET, PUT, PATCH with body
201 CreatedSuccessful POST with Location
204 No ContentSuccessful DELETE or update without body
202 AcceptedAsync processing queued

Common 4xx Codes

CodeUse
400 Bad RequestMalformed syntax
401 UnauthorizedMissing/invalid credentials
403 ForbiddenAuthenticated but not permitted
404 Not FoundResource does not exist
405 Method Not AllowedMethod not supported on URI
409 ConflictDuplicate or state conflict
422 Unprocessable EntityValid syntax, invalid semantics
429 Too Many RequestsRate limit exceeded

Common 5xx Codes

CodeUse
500 Internal Server ErrorUnexpected server fault
503 Service UnavailableTemporary overload or maintenance

Critical Distinctions

PairDistinction
400 vs 422Cannot parse vs parsed but violates rule
401 vs 403Who are you? vs I know you, you cannot
403 vs 404Confirms existence vs hides existence

Best Practices

โœ… Do This:

# Creation
POST /users โ†’ 201 Created + Location

# Retrieval
GET /users/42 โ†’ 200 OK + body

# Deletion
DELETE /users/42 โ†’ 204 No Content

# Authentication failure
โ†’ 401 Unauthorized + WWW-Authenticate

# Authorization failure
โ†’ 403 Forbidden

โŒ Don’t Do This:

# 200 for a missing resource
HTTP/1.1 200 OK
{ "error": "not found" }  // breaks clients and monitoring

# 500 for a client validation error
HTTP/1.1 500 Internal Server Error
{ "error": "email is required" }  // should be 400 or 422

# 200 for a created resource without Location
HTTP/1.1 200 OK
{ "id": 42 }  // should be 201 with Location

Common Pitfalls

PitfallWhy It HappensFix
200 for errorsConvenienceUse 4xx/5xx
500 for validationConfused about faultUse 400 or 422
401 when authenticatedConfused 401/403Use 403
404 when forbiddenHiding existence is optionalUse 404 uniformly for sensitive resources
Stack trace in 500Debugging leftoverSanitize the body

Real-World Examples

1. Create success

POST /users โ†’ 201 Created
Location: /users/42

2. Read success

GET /users/42 โ†’ 200 OK

3. Delete success

DELETE /users/42 โ†’ 204 No Content

4. Missing resource

GET /users/99 โ†’ 404 Not Found

5. Authentication required

โ†’ 401 Unauthorized
WWW-Authenticate: Bearer

6. Authorization denied

โ†’ 403 Forbidden

7. Malformed body

POST with invalid JSON โ†’ 400 Bad Request

8. Semantic validation

POST with age: -5 โ†’ 422 Unprocessable Entity

9. Duplicate conflict

POST with existing email โ†’ 409 Conflict

10. Server fault

โ†’ 500 Internal Server Error
(no internal details in body)

Visual

Status Code Decision Flow

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Request processed                           โ”‚
โ”‚         โ”‚                                    โ”‚
โ”‚         โ–ผ                                    โ”‚
โ”‚  Success?                                    โ”‚
โ”‚  โ”œโ”€ YES โ†’ 2xx                                โ”‚
โ”‚  โ””โ”€ NO โ†’ Client's fault?                     โ”‚
โ”‚          โ”œโ”€ YES โ†’ 4xx                        โ”‚
โ”‚          โ””โ”€ NO โ†’ 5xx                         โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

2xx Selection

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Body to return?                             โ”‚
โ”‚  โ”œโ”€ YES โ†’ Resource created?                  โ”‚
โ”‚  โ”‚        โ”œโ”€ YES โ†’ 201 Created + Location    โ”‚
โ”‚  โ”‚        โ””โ”€ NO โ†’ 200 OK                     โ”‚
โ”‚  โ””โ”€ NO โ†’ 204 No Content                      โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

4xx Selection

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Authentication valid?                       โ”‚
โ”‚  โ”œโ”€ NO โ†’ 401 Unauthorized                    โ”‚
โ”‚  โ””โ”€ YES โ†’ Authorized?                        โ”‚
โ”‚          โ”œโ”€ NO โ†’ 403 Forbidden (or 404)      โ”‚
โ”‚          โ””โ”€ YES โ†’ Resource exists?           โ”‚
โ”‚                  โ”œโ”€ NO โ†’ 404 Not Found       โ”‚
โ”‚                  โ””โ”€ YES โ†’ Syntax valid?      โ”‚
โ”‚                          โ”œโ”€ NO โ†’ 400         โ”‚
โ”‚                          โ””โ”€ YES โ†’ Semantic?  โ”‚
โ”‚                                  โ”œโ”€ NO โ†’ 422 โ”‚
โ”‚                                  โ””โ”€ Conflict?โ”‚
โ”‚                                      โ””โ”€ 409  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Family and Retry

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  2xx โ†’ success, no retry                     โ”‚
โ”‚  3xx โ†’ follow redirect                       โ”‚
โ”‚  4xx โ†’ do not retry without changing request โ”‚
โ”‚  5xx โ†’ may retry later                       โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Summary

ItemValue
2xxSuccess
3xxRedirection
4xxClient error
5xxServer error
Create201 + Location
Read200
Delete204
Auth missing401
Auth denied403
Missing resource404
Validation422
Server fault500

Key takeaways:

  • The first digit defines the family, and the family determines the reaction. 2xx means success, 3xx means redirect, 4xx means client error, 5xx means server error. A client, a proxy, and a monitoring system all read the family before the body .
  • Never return 200 for a failure. A 200 with an error body forces every client to parse the body to detect errors, breaks caching and retries, and hides failures from monitoring .
  • 201 Created requires a Location header. The client needs to know the URI of the new resource. A 201 without Location is an incomplete response .
  • 204 No Content is the confirmation for DELETE. There is no body to parse; the status code is the signal .
  • 401 and 403 are different. 401 means authentication is missing or invalid. 403 means the user is authenticated but not authorized. Returning 401 when the user is logged in confuses the client .
  • 400 and 422 are different. 400 means the request cannot be parsed. 422 means it parses but violates a business rule. The distinction tells the client whether to fix the syntax or the value .
  • 500 must not leak internal details. Stack traces, database errors, and file paths belong in server logs. The response body is a generic message .

Remember: The status code is the first thing every client, proxy, and monitoring system reads. It is not decorationโ€”it is the machine-readable contract that determines whether to parse the body, retry the request, or page an engineer. The families are the coarse classification: 2xx success, 3xx redirect, 4xx client error, 5xx server error. The exact codes are the precise contract: 201 for creation with Location, 204 for deletion, 401 for missing authentication, 403 for denied authorization, 404 for missing resources, 422 for semantic validation failures, and 500 for unexpected server faults. Returning 200 for every outcome defeats the entire purpose of the protocol and makes the API impossible to monitor, cache, or retry correctly.



Stop using slow, ad-bloated tool sites! ๐Ÿคฎ

๐Ÿ”Ž Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
โœ… Finance (Mortgage, Interest, Inflation)
โœ… Tech (Base64, JSON, Dev Suite, IP)
โœ… Health (BMI, BMR, TDEE)
โœ… Productivity (Timer, Workspace, QR)

โšก๏ธ Fast & Private
๐Ÿ”’ No data leaves your device
๐Ÿ’Ž 100% Free

๐Ÿ”— Use it now: https://tools.kandz.me
๐Ÿ”– Bookmark itโ€”youโ€™ll need it later!