Skip to main content
All Posts
2026AI API vs Traditional API: What's Actually DifferentBuilding LLM-Backed APIs: A Practical Architecture GuideEmbedding APIs Explained: How They Work and When to Use ThemCost Optimization for LLM APIs: Caching, Routing, and Model SelectionLLM Rate Limiting: Token Buckets and Concurrency ControlStreaming LLM Responses: Patterns and PitfallsOpenAI API vs Anthropic API vs Gemini API: Production Trade-offsOpen vs Proprietary LLM APIs: Trade-offs for ProductionRAG APIs: Architecting Retrieval-Augmented Generation EndpointsVertical AI API Strategy: Building Domain-Specific AI APIsSmarter AI Traffic with Jev: How WSO2 AI Gateway Mediates LLM and MCP CallsAPI Analytics: The Metrics That Actually Matter (2026 Guide)How to Build a CI/CD Pipeline for APIsAPI Design Tools: Choosing the Right Stack for API-First TeamsAPI Gateway vs Load Balancer vs Reverse Proxy: Clearing the ConfusionAPI Management Platforms: Cloud vs. Self-Hosted (2026)API Management Tools: A Complete Buyer's Guide for 2026Cloud Native API Gateway: Architecture Guide for 2026Envoy vs NGINX: Which Proxy Fits Your API Gateway?Internal Developer Portal: What It Is and How to Build OneKong vs Tyk: Open Source API Gateway ShowdownKubernetes API Gateway: The 2026 Guide to Gateway APIMuleSoft vs WSO2: Enterprise API Management ComparedOpen Source API Gateways Compared: Kong, Tyk, Gravitee, WSO2What Is an API Gateway? Functions, Architecture & Use CasesAPI Architecture: Patterns, Principles & Modern ApproachesAPI Caching Strategies: Edge, Gateway, and Application LayersAPI Federation: Patterns for Multi-Team API ProgramsAPI-First Architecture: Why Design Before Code WinsAPI Gateway Architecture: How API Gateways Actually WorkAPI Security Architecture: Layered Defense for Modern APIsCircuit Breakers and Resilience Patterns for APIsAPI Architecture Diagrams: From L0 to Ln (Complete Guide)Backend-for-Frontend (BFF) Pattern: When and WhyEvent-Driven APIs: Webhooks, WebSockets, and SSEMicroservices vs Monolithic: Trade-offs and When to ChooseService Mesh vs API Gateway: When You Need Each (or Both)API Authentication Best Practices: OAuth, Keys, mTLS, JWTAPI Error Handling: Best Practices for REST APIsAPI Documentation Best Practices: Beyond Reference to Developer ExperienceAPI Rate Limiting: How It Works and Best PracticesAPI Pagination: Design Patterns & Best PracticesAPI Versioning Best Practices: Strategies for Long-Lived APIsGraphQL vs REST: Key Differences & When to Use EachHTTP Status Codes for API Design: A Practical GuideIdempotency Keys for APIs: The Complete GuideJWT vs OAuth: What's the Difference and When to Use EachOAuth 2.1: What's New and How It Differs from OAuth 2.0REST API Design: A Complete Guide for 2026What Is a REST API? A Plain-English GuideAI Agent API Integration: Patterns & Best PracticesEnterprise MCP: The Control Plane for AI AgentsMCP Gateway vs MCP Proxy: What's the Difference?MCP Governance: Controlling Third-Party MCP ToolsMCP Monitoring: Observability for MCP ServersMCP Security: Risks, Threats and ControlsMCP Server Security: 8 Best PracticesMCP Server Catalog: Building an Internal RegistryMCP Tool Poisoning: How It Works & How to Stop ItWhat Is an MCP Gateway? Features & BenefitsWhat Is an AI Gateway? Features, Benefits and How It WorksAI Gateway Comparison: Top Solutions in 2026AI Gateway Security: Guardrails for LLM TrafficAI Gateway vs API Gateway: Key DifferencesBest LiteLLM Alternatives in 2026AI Gateway Observability: Key Metrics, Logging, and Tracing for LLM TrafficLLM Cost Control: Strategies to Cut AI SpendLLM Routing: How It Works, Strategies, and Why It MattersPrompt Injection Defense: Techniques That Actually Work7 Billion Calls a Day: One WSO2 GatewayLLM Fallback: How to Build Resilient AI ApplicationsREST API to MCP Server: A Practical GuideAzure API Management vs AWS API Gateway: Key Differences and FeaturesMuleSoft vs AWS API Gateway: Key Differences and FeaturesMuleSoft vs Azure API Management: Key Differences and FeaturesApigee vs AWS API Gateway: Key Differences and FeaturesApigee vs Azure API Management: Key Differences and FeaturesApigee vs MuleSoft: Key Differences and FeaturesGravitee vs AWS API Gateway: Key Differences and FeaturesGravitee vs Azure API Management: Key Differences and FeaturesGravitee vs MuleSoft: Key Differences and FeaturesGravitee vs Apigee: Key Differences and FeaturesKong vs AWS API Gateway: Key Differences and FeaturesKong vs Azure API Management: Key Differences and FeaturesKong vs MuleSoft: Key Differences and FeaturesKong vs Apigee: Key Differences and FeaturesKong vs Gravitee: Key Differences and FeaturesTop 6 AWS API Gateway Alternatives of 2026Top 6 Azure API Management Alternatives of 2026Top 6 MuleSoft Alternatives of 2026Top 10 Apigee Alternatives of 2026Top 4 Gravitee Alternatives of 2026Top 6 Kong Alternatives of 2026

HTTP Status Codes for API Design: A Practical Guide

· 15 min read
Technical Writer, WSO2

Every REST API conversation eventually comes down to three digits. A client sends a request, your API sends back a number, and that number does more communication work than almost anything else in the response. Get it right, and clients handle success, retries, and failure without guessing. Get it wrong, and you end up with the industry's most common anti-pattern: a 200 OK wrapped around an error message.

This guide covers the status codes that actually matter for API design, where teams tend to misuse them, and how to apply them consistently.

What Are HTTP Status Codes?​

An HTTP status code is a three-digit number a server returns in response to a client's request to say what happened: whether the request succeeded, whether the client needs to change something, or whether the problem was on the server's side. Codes are grouped into five classes by their first digit (1xx to 5xx), and each class carries a distinct signal that a client can act on programmatically, without parsing a response body to work out what went wrong.

RFC 9110 is the core reference for HTTP semantics, including most status codes. The IANA HTTP Status Code Registry is the canonical, continuously updated index, and for codes RFC 9110 doesn't define itself, such as 429, it points to the RFC that does (RFC 6585, in that case). These are the sources worth citing when precision matters, because RFC 9110 supersedes the older RFC 2616 that some references still point to.

The Five Classes of HTTP Status Codes​

Before memorizing individual codes, get the classes right. A client that checks only the first digit, rather than the exact code, can still handle a response correctly in most cases. That's the whole point of the class system: it gives clients a coarse-grained fallback when they receive a status code they don't specifically handle.

ClassRangeMeaningCommon Example
1xx100–199Informational: the request was received and processing continues100 Continue
2xx200–299Success: the request was received, understood, and accepted200 OK
3xx300–399Redirection: further action is needed to complete the request301 Moved Permanently
4xx400–499Client error: the request has invalid syntax or can't be fulfilled404 Not Found
5xx500–599Server error: the server failed to fulfill a valid request500 Internal Server Error

1xx Informational​

These are interim responses telling the client that processing hasn't finished, without ending the exchange. REST API design rarely touches them directly. They're handled by the HTTP stack (client libraries and servers) below the level where your application code runs, so most API teams never build a 1xx response by hand.

2xx Success​

The request was received and processed as intended. This is where the differences between codes matter more than teams expect: 200, 201, and 204 all mean success, but they tell the client different things about what now exists and what to do next.

3xx Redirection​

The client needs to take further action, usually by following a different URI, to complete the request. In APIs, these codes appear mostly in versioning and canonical-URL strategies, and less often in everyday resource responses.

4xx Client Error​

The request itself is the problem: bad syntax, missing authentication, a resource that doesn't exist, or a request that conflicts with the current state of the resource. This is the class where API design gets opinionated, and where most code review disagreements happen.

5xx Server Error​

The request was valid, but something failed on the server while fulfilling it. A well-designed API keeps this class narrow.

If malformed client input reaches an unhandled exception, the resulting 500 accurately describes a real server failure; it isn't a mislabeled client error. So the fix isn't to swap in a 4xx after the fact. The fix is validation: reject the malformed input before it reaches the code path that crashes, so the request never gets the chance to cause that exception.

HTTP Status Codes Every API Developer Should Know​

You don't need all 60-plus registered codes in active use. Most production REST APIs rely on a working set of 15 to 20. Here's the set worth knowing, with the specific behavior each one signals.

200 OK​

The default success response for GET, PUT, or PATCH. RFC 9110 defines it simply as meaning the request succeeded. Use it for requests that don't create a new resource; that case is what 201 is for.

201 Created​

Returned when a request has successfully created a resource, usually after a POST. The response should include a Location header pointing to the new resource, so the client doesn't have to guess the URI it just created.

204 No Content​

The request succeeded, but there's nothing to send back. This is the right response for a successful DELETE, or for a PUT where the client doesn't need the updated representation returned.

301 Moved Permanently​

The resource has permanently moved to a new URI. For historical reasons, user agents are allowed to change the request method from POST to GET when following a 301, which can silently turn a write into a read for some clients. If the redirected route needs to receive the same method and body, 308 Permanent Redirect is the safer choice for API versioning. Use 301 only when the destination is contract-compatible and you've confirmed how your actual client libraries behave on redirect.

304 Not Modified​

Used with conditional requests (an If-None-Match or If-Modified-Since header) to tell the client that its cached copy is still valid. It's worth implementing on high-traffic GET endpoints, because it saves bandwidth on both sides.

400 Bad Request​

The server can't or won't process the request because of something the client did: malformed JSON, a missing required field, or a parameter of the wrong type. It's the generic client-error catch-all, and it's often overused for cases that really deserve 401, 403, or 422.

401 Unauthorized​

The request lacks valid authentication credentials. Despite the name, this is an authentication failure, not an authorization failure: the client hasn't proven who it is. RFC 9110 makes this more than a naming convention. A server that generates a 401 response MUST send a WWW-Authenticate header with at least one applicable challenge, so the client learns how to authenticate, not just that it needs to.

403 Forbidden​

The server understands the request but refuses to fulfill it. This is the authorization failure, but it doesn't require the client to have authenticated first. When credentials were sent, it means the server considers them insufficient for this request, and a server can just as easily return 403 to an anonymous request as to a logged-in one. RFC 9110 allows a client to retry with new or different credentials, but not simply to repeat the request with the same ones. A 403 can also result from reasons that have nothing to do with credentials.

404 Not Found​

The requested resource doesn't exist, or at least isn't one the server is willing to reveal. For security-sensitive resources, a common pattern is to return 404 instead of 403, so an unauthorized client can't tell "doesn't exist" apart from "exists but you can't see it."

405 Method Not Allowed​

The resource exists but doesn't support the HTTP method used, as when a POST is sent to a read-only endpoint. The origin server MUST (not just should) send an Allow header listing the methods the resource currently supports. A 405 without one leaves the client no way to correct its request.

409 Conflict​

The request conflicts with the current state of the target resource. This is the code for attempts to create a duplicate resource and for application-level state conflicts, such as an operation that's only valid when the resource is in a different status from its current one.

Keep it separate from 412 Precondition Failed, which is specifically for a client-supplied conditional header (If-Match, If-Unmodified-Since) that evaluated to false. If a client is racing another client using ETags, a failed condition is a 412. A business-rule conflict with no conditional header involved is a 409.

422 Unprocessable Content​

The request is syntactically well-formed but semantically invalid, such as a field with the right type but an out-of-range value. RFC 9110 renamed this code from "Unprocessable Entity" to "Unprocessable Content," which is worth knowing when you're cross-referencing older documentation.

429 Too Many Requests​

The client has sent too many requests in a given time window. This code isn't defined in RFC 9110 at all; it comes from the separate RFC 6585. A well-formed 429 response usually includes a Retry-After header telling the client how long to back off.

500 Internal Server Error​

A generic signal that something went wrong on the server and no more specific 5xx code applies. Treat it as a last resort, and log enough detail on the server that every 500 is something you can investigate.

502 Bad Gateway​

A server acting as a gateway or proxy received an invalid or malformed response from an upstream server it depended on to fulfill the request. Keep it separate from 504 Gateway Timeout: 502 means a response came back but was unusable, while 504 means no response came back in time. If your API sits in front of other services, use 502 when a downstream dependency responds with garbage, and 504 when it doesn't respond at all.

503 Service Unavailable​

The server is temporarily unable to handle the request, often because of maintenance or overload. When the downtime is planned, include a Retry-After header so well-behaved clients know when to try again.

Common Mistakes with HTTP Status Codes in API Design​

  1. Returning 200 for everything. The most common failure is an API that always returns 200 and hides the real outcome in a status or success field in the JSON body. That defeats the purpose of status codes. Clients, proxies, caches, and monitoring tools all read the status code without parsing the body, and an API that hides failures inside a 200 breaks every one of them.
  2. Confusing 401 and 403. These get swapped all the time. 401 means the request doesn't carry valid credentials, so authenticating (or re-authenticating) is the right next step. 403 means the server understood the request and refuses it; if credentials were sent, it considers them insufficient, so sending the same credentials again won't help, although different ones might. Returning 401 when you mean 403 sends a client into a pointless re-login loop.
  3. Returning 500 for client errors. When invalid client input crashes a handler, many frameworks turn the unhandled exception into a 500 by default. The 500 itself is accurate, because an unhandled exception is a genuine server-side failure, and downgrading the status to a 4xx without fixing anything would just hide a real bug from monitoring. The real fix sits upstream of the status code: add the validation that should have rejected the malformed input with a proper 4xx before it reached the code that crashed.
  4. Using one generic error code for every 4xx case. An API that returns 400 for a missing field, a business-rule violation, and a rate limit alike forces every client to parse error bodies to work out what happened. The more specific codes exist so clients can branch on the status code alone.

Best Practices for Using HTTP Status Codes in Your API​

Consistency matters more than perfection. Choose a convention for each case, document it, and apply it the same way across the whole API, rather than optimizing each endpoint on its own.

  • Use the narrowest accurate code. A 422 tells a client more than a 400 does. Wherever a more precise code exists, prefer it over the generic catch-all.
  • Keep error response bodies structured and consistent. The status code carries the category, but a well-designed error body (a machine-readable error code, a human-readable message, and, where relevant, the field that failed) helps clients build useful error handling without guessing at your conventions.
  • Rate limit deliberately, not incidentally. If your API enforces limits, return 429 with a Retry-After header, rather than a generic 400 or, worse, a 200 with a "rejected" flag hidden in the body.
  • Don't let redirects surprise API clients. Whether a 3xx is followed automatically, and whether the method and body survive the hop, depends entirely on the client library and its configuration. Some follow redirects by default, others need it switched on, and none of this is guaranteed just because a code is technically permanent or method-preserving. Use redirects for genuine URI changes, not as a substitute for a documented version bump, and check the behavior against the client libraries you actually expect to serve.
  • Log status codes as a first-class signal. A spike in 5xx responses and a spike in 4xx responses tell you completely different things: the first is your problem, and the second is usually a client integration issue. Accurate codes are what make that signal trustworthy.

Here's a structured error body paired with 422 Unprocessable Content:

{
"error": {
"code": "invalid_field_value",
"message": "The 'quantity' field must be a positive integer.",
"field": "quantity"
}
}

And here's a 201 Created response with its Location header:

HTTP/1.1 201 Created
Location: /orders/48213
Content-Type: application/json

{
"id": "48213",
"status": "pending",
"createdAt": "2026-09-03T10:15:00Z"
}

How WSO2 Standardizes Status Code Handling​

Status codes have been part of HTTP since HTTP/1.0 in 1996, and getting them right is less about innovation than consistency. That consistency gets harder once an organization runs APIs across more than one gateway, because different gateway runtimes don't always default to the same error-response conventions.

WSO2 API Platform tackles part of this at the control-plane level rather than the level of individual gateways. The platform's gateway federation brings AWS, Azure, Kong, and Envoy gateways under the same control plane as WSO2's own gateways. It discovers and catalogs their APIs and monitors them for governance compliance, which centralizes how API definitions are managed across a mixed gateway estate.

That isn't the same as uniform status-code behavior automatically appearing on every underlying gateway. Each gateway type still runs its own runtime, and consistent error mapping across all of them depends on how policy is configured and applied through the control plane, not on the control plane simply existing. The distinction matters most for organizations with a genuine multi-gateway setup, whether from mergers, regional deployments, or using different gateways for different workloads, and much less for a team running a single gateway with one convention already in place.

On WSO2's own gateways, the status codes for gateway-generated responses are documented and configurable rather than left to chance:

That turns status-code behavior at the gateway into a documented contract rather than an implementation detail.

None of this replaces good design discipline on the team building the API. Consistent status codes still start with the choices covered above. What a unified control plane changes is how much of that consistency survives once an API is deployed across more than one type of gateway.

Conclusion​

Status codes carry more of an API's meaning than most teams realize. The five classes are the foundation: get them right, and a client can make sensible decisions even about codes it has never seen. Add the 15 to 20 codes that actually come up in REST API design, apply them consistently, and most of the ambiguity behind fragile client integrations disappears. If you're running APIs across more than one gateway, WSO2 API Platform's unified control plane is worth a look for keeping that consistency intact as the number of gateways grows.

Frequently Asked Questions​

Is HTTP 429 a rate limit?​

Yes. 429 Too Many Requests is the standard status code for rate limiting, defined in RFC 6585. A well-formed 429 response usually includes a Retry-After header telling the client how long to wait before trying again.

How do I rate limit an API to 10 requests per minute?​

Rate limiting is a policy decision, usually enforced at the gateway or middleware layer with a sliding window or token bucket algorithm, and it's a separate concern from the status code. Once the limit is reached, the API should return 429 with a Retry-After header, rather than silently dropping or queuing the request.

What do HTTP status codes beginning with 2xx indicate?​

Success: the server received, understood, and accepted the request. Which 2xx code to use depends on what happened: 200 for a general success, 201 when a new resource was created, and 204 when there's no content to return.

What is the 204 HTTP status code?​

204 No Content means the request succeeded but there's nothing to send back in the response body. It's commonly used for successful DELETE requests, and for PUT or PATCH requests where the client doesn't need the updated resource returned.

WSO2 API PlatformWSO2 API Platform

The open, universal platform for managing every API and AI service at scale. 100% open source.

Explore

BlogTutorialsTopics
© WSO2 LLC. All rights reserved.
WSO2 LegalDo Not Sell My Personal InformationModern Slavery Statement