When an API request fails in production, the first thing developers check is the status code. But many systems don’t actually use them very well. You’ll often see APIs returning 200 OK even when something went wrong, or 500 for every possible failure. At that point, the status code stops being useful. The client doesn’t know what happened, and debugging becomes harder than it needs to be.
HTTP status codes exist to communicate state between systems. When used correctly, they make APIs predictable, observable, and easier to operate. If you’re looking for an intro to web development, one of the earliest architectural ideas you’ll encounter is how HTTP communicates success and failure. Getting this right early helps avoid subtle API problems later in production.
Status codes are a communication protocol
When writing application code, developers often think in binary terms: did the function succeed or fail?
HTTP works differently. Instead of a simple boolean result, requests return granular states that explain what happened. A client doesn’t just need to know that something failed. It needs to know:
- Was the request valid?
- Did authentication fail?
- Is the server overloaded?
- Should the request be retried?
HTTP status codes encode these states. When used correctly, they allow systems to behave predictably without requiring custom logic for every scenario.

HTTP status codes that matter in real APIs
There’s a comprehensive list of defined status codes, but most production systems rely on a much smaller set. These are the ones developers encounter most often.
Successful responses (2xx)
These codes indicate that the request was handled successfully.
- 200 OK: This is the standard response for successful requests. It’s most commonly used for read operations, such as fetching data. For example:
GET /users/42
→ 200 OK
- 201 Created: This status code indicates that a new resource was successfully created. Using
201signals that the request caused a state change in the system. This is typically returned after a successfulPOSTrequest that results in a new database record. For example:
POST /users
→ 201 Created
- 202 Accepted: It is useful for asynchronous workflows. Sometimes a request triggers work that takes longer than a normal HTTP request should remain open. Instead of blocking the connection, the server acknowledges the request and processes it later. This tells the client, “Your request is valid and has been queued for processing.” This pattern is common when working with background jobs, queues, or large processing tasks. For example:
POST /video-upload
→ 202 Accepted
Caching and redirection (3xx)
The 3xx range helps clients avoid unnecessary work. These are the redirection codes that tell the client that the requested resource lives somewhere else. These responses are common during URL migrations, domain changes, versioned APIs, login flows, and load-balancing redirects.
301 Moved Permanently: This status code indicates that a resource has permanently moved to a new location. Browsers and search engines treat this as a permanent change and will update their cached URLs accordingly. Because of this caching behavior,301should only be used when the move is truly permanent. For example:
GET /old-endpoint
→ 301 Moved Permanently
Location: /new-endpoint
302 Found: This status code indicates a temporary redirect. The client follows the redirect but does not permanently update the original URL. This is commonly used in authentication flows or temporary routing logic.
GET /login
→ 302 Found
Location: /auth-provider
304 Not Modified: It is one of the most important performance-related status codes. If the client already has a cached copy of a resource and the data hasn’t changed, the server can return304 Not Modified. The client can then reuse its cached version instead of downloading the resource again. This typically works together with cache-control headers likeETag,Cache-Control, andIf-None-Match. When used correctly, this significantly reduces bandwidth usage and improves response times.
Client errors (4xx)
The 4xx range indicates that the client sent an invalid request. The request cannot succeed unless the client changes something.
400 Bad Request: This code indicates that the request itself is malformed. It can be due to invalid JSON, missing required parameters, or incorrect query syntax. The client must correct the request before retrying.
401 Unauthorized: This code is returned when authentication fails. It can be due to missing authentication tokens, expired tokens, or invalid credentials. The client must authenticate successfully before retrying.
403 Forbidden: This code means that authentication has succeeded, but the user does not have permission to access the resource. For example:
GET /admin/users
→ 403 Forbidden

404 Not Found: This code is returned when the requested resource does not exist. Using404correctly helps clients distinguish between missing data and server failures. For example:
GET /users/9999
→ 404 Not Found
- 422 Unprocessable Content: Returning
422status code means that the request format is correct, but the data itself is invalid. This is widely used in modern REST APIs. For example:
POST /users
{
"email": ""
}
Note that the JSON is valid, but the data violates validation rules.
429 Too Many Requests: This status code is returned when a client exceeds rate limits. Rate limiting protects APIs from both malicious abuse and accidental overload. For example:
429 Too Many Requests
Retry-After: 60
Note that the Retry-After header tells the client when it is safe to retry.
Server errors (5xx)
The 5xx range indicates that the request was valid, but the server failed to complete it. These errors usually point to infrastructure or system issues.
500 Internal Server Error: This is the generic fallback for unexpected server failures. It should typically be used only when the server encounters an error that doesn’t fit a more specific status code.
502 Bad Gateway: Returning502means that a server acting as a gateway or proxy receives an invalid response from an upstream service. For example, an API server calls another internal service, but the upstream service crashes or returns invalid data.-
503 Service Unavailable: This status code indicates that the server is temporarily unable to handle the request. This can occur due to maintenance windows, service overload, or autoscaling delays. In such cases, clients should usually retry later.
504 Gateway Timeout: This status code means that an upstream dependency took too long to respond. This often points to slow databases, overloaded services, and network latency issues. Repeated504responses often indicate infrastructure bottlenecks rather than application bugs.

Reference map: Common production scenarios
Many API interactions map cleanly to specific status codes.
| What You Are Trying to Do | Recommended Code | Why It Matters |
|---|---|---|
| Creating a resource | 201 Created | Standardizes that a mutation occurred successfully. |
| Queuing an async background job | 202 Accepted | Keeps the UI unblocked while heavy processing happens. |
| Fetching data that hasn’t changed | 304 Not Modified | Offloads work to the browser’s cache, saving bandwidth. |
| Blocking access to an admin route | 403 Forbidden | Clarifies it’s a permission issue, not a login issue. |
| Rejecting invalid form inputs | 422 Unprocessable Content | Separates business logic validation from bad syntax (400). |
| Handling an upstream database crash | 502 Bad Gateway | Proves the failure happened deep in the infrastructure. |
Go deeper: Authoritative references
Simply put, HTTP status codes are one of the simplest tools in the web platform, but they carry a surprising amount of meaning.
If you want to understand these patterns in detail and see how they are implemented in the real world, these resources are a solid place to start:
- MDN Web Docs (HTTP response status codes): This is the industry-standard starting point for web developers, explaining every single code with clear, practical examples.
- RFC 9110 (HTTP Semantics): This page is considered the ultimate source of truth for how HTTP methods and status codes are defined at the architectural level.
- OWASP REST Security Cheat Sheet: A community-driven security standard that explains exactly how to use codes like
401and403to secure your APIs properly.
Remember that you don’t need to memorize every status code to build better APIs. But choosing the right ones for common scenarios goes a long way toward making systems easier to operate in production. Happy learning!