HTTP status codes aren't just numbers—they're a machine-readable contract that determines how your API integrates with client libraries, AI agents, and automated tooling. When you choose the right status code, SDKs can automatically handle retries, generate typed exceptions, and provide clear error messages, while MCP servers can give LLMs precise signals about tool call outcomes.
This guide covers the essential status codes that shape how developers and AI systems interact with your API, from distinguishing between 400
and 422
validation errors to using 503
for automatic retry logic. You'll learn which codes enable better SDK generation, how status codes influence AI agent behavior, and the common mistakes that break client integrations.
What makes a good API status code strategy
Choosing the right HTTP status code is about creating a clear, machine-readable contract for your API. A consistent strategy tells client applications, SDKs, and AI agents exactly how to interpret a response, which is critical for building reliable integrations. The Stainless SDK generator uses these status codes to automatically build type-safe client libraries. When automated tools know whether to retry a request, invalidate a cache, or throw a specific typed error, it dramatically reduces integration time and future support tickets.
This contract is the foundation for robust tooling. For example, a well-generated SDK can automatically handle retries on a 503 Service Unavailable
but will know not to retry on a 400 Bad Request
. Similarly, a Model Context Protocol (MCP) server uses status codes to tell an LLM whether a tool call succeeded or failed, directly influencing the agent's next decision. You can generate an MCP server from an OpenAPI spec to expose these status codes as clear signals to AI agents.
Which success code should your API return
When a request succeeds, you have a few options. Choosing the right one gives the client important context about the operation.
200 OK
: This is the general-purpose success code. Use it for successfulGET
requests that return data, or forPUT
orPATCH
updates that return the modified resource in the response body.201 Created
: Use this specifically after aPOST
request successfully creates a new resource. A201
response should also include aLocation
header pointing to the URL of the newly created resource.204 No Content
: This code signals that the server successfully processed the request but has no content to return. It's the perfect choice for aDELETE
request or aPATCH
request where you don't need to send the updated resource back.
This precision allows an SDK generator to infer the correct return type for each API method. A method that maps to a 204
response might return void
or None
, while one that returns 200
or 201
will be typed to the corresponding response body schema.
Which client error tells users what went wrong
Client errors, the 4xx
series, are your API's way of telling the user, "The problem is on your end." Being specific here is key to a good developer experience, as it helps users debug their own code faster.
400 vs 422 for validation errors
This is a common point of confusion. Both codes indicate a problem with the user's request, but the distinction is important.
400 Bad Request
: Use this for requests that are syntactically incorrect. The server couldn't even parse the request. A common example is malformed JSON.422 Unprocessable Entity
: Use this when the request syntax is perfectly valid, but the data fails semantic validation. For example, a user sends a JSON object where anemail
field contains a string that isn't a valid email address.
A well-typed SDK can use this distinction to generate different error classes, allowing developers to programmatically handle a 422 APIValidationError
differently from a generic 400 APIBadRequestError
.
401 vs 403 for auth failures
Authentication and authorization errors are not the same, and your status codes should reflect that.
401 Unauthorized
: This means the server doesn't know who the user is. The request lacks valid authentication credentials. The user is unauthenticated.403 Forbidden
: This means the server knows who the user is, but that specific user is not permitted to perform the requested action. The user is authenticated but unauthorized.
This difference is crucial for client-side logic. An SDK receiving a 401
might trigger a flow to refresh an expired token, while a 403
simply informs the user they don't have the necessary permissions.
404 vs 410 for deleted resources
Both codes mean a resource can't be found, but 410
provides a crucial piece of extra information.
404 Not Found
: This is the generic "I can't find what you're looking for" response. It doesn't say whether the resource ever existed.410 Gone
: This is a more definitive signal. It tells the client that the resource used to exist at this URL but has been permanently deleted.
Using 410
allows clients to confidently update their own state, for example by removing the resource from a local cache or search index.
When should your API return server errors
The 5xx
series of status codes indicates that something went wrong on your server, and it's not the client's fault.
500 Internal Server Error
: This is the catch-all error. It means an unexpected condition was encountered on the server. You should avoid using this for predictable errors and always log these occurrences for debugging.502 Bad Gateway
: This indicates that your server, while acting as a gateway or proxy, received an invalid response from an upstream server.503 Service Unavailable
: This means the server is temporarily unable to handle the request, perhaps due to being overloaded or down for maintenance. This is a key signal for clients to retry the request later.504 Gateway Timeout
: This is similar to502
, but specifically means an upstream server failed to respond in time.
Client libraries often build automatic retry logic around 503
and other transient server errors, making your API more resilient without any extra work from your users.
When do APIs actually need redirects
Redirects are less common in JSON APIs than in traditional web applications, but they have their uses.
301 Moved Permanently
: Use this to indicate that a resource or endpoint has permanently moved to a new URL. This is useful when versioning your API via the URL path (e.g.,/v1/users
is now/v2/users
).307 Temporary Redirect
: This tells the client to resubmit the request to a different URL, but that the original URL should be used for future requests.
Most modern HTTP clients, and by extension the SDKs built on them, will automatically follow these redirects, making the process seamless for the end-user.
What status code mistakes break client integrations
A poor status code strategy can actively harm your developer experience and break automated tools. Here are some common anti-patterns to avoid.
Always-200: Returning a
200 OK
for every request, even for errors, and putting the error details in the response body. This breaks all standard HTTP client error handling.Blanket-500: Using a generic
500 Internal Server Error
for client-side validation issues. This misleads the client into thinking the server is broken.HTML for Errors: Returning an HTML error page instead of a structured JSON error object. This is impossible for a machine to parse reliably.
Custom Codes: Inventing custom status codes like
451
(before it was standardized) or600
. Stick to the IANA-registered codes for maximum compatibility.
How status codes shape sdk behavior and ai agents
A thoughtful status code strategy directly enables more powerful and resilient client libraries and AI agents. The signals you send are not just for humans; they are instructions for machines.
For example, when an SDK encounters different error codes, it can map them to specific, typed exceptions. This allows a developer to write clean, targeted error-handling logic, and any custom code you add to handle specific error cases will persist through SDK regeneration.
For instance, in a Stainless-generated TypeScript client you might configure 422 to map to MySDK.ValidationError
and 401 to MySDK.AuthenticationError
. Here’s how you could handle those errors:
For AI agents using MCP, this is even more critical. If a tool call to your API returns a 429 Too Many Requests
, the agent knows it's being rate-limited and can pause before retrying. If it gets a 403 Forbidden
, it knows it lacks permission and can ask the user for elevated access instead of retrying fruitlessly. Your status codes become the agent's senses for interacting with your service.
Frequently asked questions about API status codes
Should I invent custom status codes?
No, you should stick to the standard IANA-registered status codes. For custom error details, provide a rich, structured JSON error body instead. When you create OpenAPI specs, these standard codes and error schemas enable tooling to understand your API's behavior.
How many status codes should I support?
You don't need to support every code. Focus on a core set that covers success (200
, 201
, 204
), client errors (400
, 401
, 403
, 404
, 422
), and server errors (500
, 503
).
Which code signals rate limiting?
Use 429 Too Many Requests
. You should also include a Retry-After
header to tell the client how many seconds to wait before trying again. When you integrate SDK snippets with your API docs, developers can see exactly how each SDK handles rate limiting automatically.
How do status codes interact with API versioning?
You can use a 301 Moved Permanently
to redirect clients from an old versioned endpoint, though using a custom version header is often a more flexible approach.
Can I change status codes without breaking clients?
Changing the status code for a given outcome is a breaking change. If you must change a code, treat it like any other breaking API change with a new major version and a clear migration path.
Ready to ship APIs with SDKs that just work? Get started for free.