Why HTTP Status Codes Exist

HTTP defines dozens of status codes — Wikipedia currently lists 75 — but most developers will never encounter the majority of them. Obscure codes like 205 Reset Content, 300 Multiple Choices, 419 Authentication Timeout, and 450 Blocked by Windows Parental Controls are technically standardized but rarely seen in practice. Even the famous 418 I'm a teapot is an April Fools' joke from RFC 2324.

The status codes that matter were designed for a specific purpose: they let a generic client — a web browser — take the correct action automatically without any prior knowledge of the server it's talking to. The original HTTP/1.0 specification in RFC 1945 defined 16 such codes. 401 Unauthorized, for example, tells a browser to prompt the user for credentials. 206 Partial Content tells a browser that its range request was honored, so it shouldn't treat the response as the full resource. 304 Not Modified lets caching proxies safely reuse a stored response in conjunction with conditional GET requests. These codes are valuable precisely because they're understood across implementations.

API Clients Are Not Web Browsers

API clients occupy an awkward middle ground. Some behaviors are genuinely generic. A Python client using the requests library will transparently follow a 302 Found redirect:

>>> import requests
>>> requests.get('http://httpstat.us/302').status_code
200

The library knows what 302 means, follows the Location header, makes a second request, and returns the final 200 OK. In that respect, it behaves exactly like a browser.

But most API clients are deeply application-specific. A well-built Twilio library understands what error 21610 ("Message cannot be sent to the 'To' number because the customer has replied with STOP") means, but no generic client could. For these domain-specific failures, most API providers fall back on a single status code — commonly 400 Bad Request — and rely on the response body to carry the details.

The practical rule is this: standard status codes are valuable only where the client is generic enough to act on them. Where a client is purpose-built for one API, the choice of status code is largely a matter of convention.

Developers Have Expectations Too

API design isn't just about software behavior; it's also about the humans who write against the API. The principle of least astonishment applies: developers learn faster when an API behaves like others they already know. That's a moving target — conventions like Rails-style plural URLs (/people, /person/123) or Twitter-era /foo.json suffixes fell in and out of fashion. Status code usage is similar. Past a certain point, choosing one specific code over another is taste, not engineering. Listen to your audience and match their expectations.

A Minimal, Actionable Set

Status codes fall into five broadly understood numeric classes: 1xx informational, 2xx successful, 3xx redirection, 4xx client error, and 5xx server error. Many HTTP libraries already treat any 4xx or 5xx response as an error and raise an exception, so respecting these categories delivers immediate value to consumers.

Beyond the categories, a few specific codes are worth using because they enable generic clients to do something useful:

  • 302 Found (and other redirect codes) — Allows clients to follow a redirect, which can be useful when a request needs to be rerouted to another host.
  • 304 Not Modified — Combined with conditional requests using ETag/If-None-Match or Date/If-Modified-Since, it enables response caching in browsers and in HTTP libraries like OkHttp on Android and AFNetworking on iOS.
  • 429 Too Many Requests — Along with the Retry-After header, it lets clients back off automatically when rate-limited.

A pragmatic API can get by with as few as three status codes — 200, 400, and 500, for example — and should add others only when they carry actionable, cross-API meaning. Dropbox currently uses roughly ten codes (two for success, eight for errors) but plans to trim that list in a future API version.