HTTP status for API Responses

In designing an API it’s a challenge to understand when to use HTTP error responses and HTTP OK (200) responses. As with many organizations it’s something that can be subject to lots of passionate debate. Let’s look at a bunch of different scenarios.

Let’s take an OAuth 2.0 API that looks something like this, with an assumed HTTP Authentication header.

/api/v1/data.search?query=EXPRESSION&count=INTEGER

Imaging responses look something like:

{ ok: BOOLEAN, error: STRING, ... }

Where error is option if the ok boolean is False, otherwise there is a data payload appropriate to the API response.

data.search(query=”car”, count=15)

Return ok=true, code=200 and results=[array]

data.search(count=15)

Return ok=false, code=400, error=”query_not_found” – required parameter not present

data.search(query=”(car”, count=15)

Return ok=false code=200 error=”query_syntax” – there is a syntax error in the query

data.search(query=”car”, count=”xyz”)

Return ok=false code=200 error=”malformed_count” – count isn’t a number

Shouldn’t errors be HTTP 400?

The discussion wouldn’t be so interesting if there was one answer to this. The objective is to differentiate between structural problems with your API requests and potentially customer generated syntax problem.

When you return a HTTP 400 level error you’re indicating a few key things:

  • Authentication errors
  • Rate limit errors
  • Non-existent endpoints
  • Missing mandatory parameters in requests

When you group syntax level events in here, it’s useful but what it means is when carried to an extreme you could have an autocomplete API endpoint that when there is no autocomplete results would return a HTTP 404 no results — that would be for a valid API query with valid parameters. When you’re doing an audit of your endpoints you cannot quickly determine if somebody is calling your API with missing parameters or it’s just normal events.

You could even argue in this model missing mandatory parameters are 200 – hold that thought.

Client Considerations

When a client dispatches a request what should it do with the error, let’s assume that you’re using a client that has request interceptors as part of the flow. Where if you were to call with a 401 Authentication error you would route the user to login in someway. This would be independent of the specific handler in your UI or application – similar if it’s server side the 429 rate limit would kick in and take an action above what your regular API was doing.

Now back to that syntax error, if you’re returning a 400 level event your interceptor logic is going to have to step in and try to understand why that problem happened and may or may not route back to the code that did the dispatch.

Now if you leave the 400 level HTTP errors to indicate that there was a problem with the call you can route all of these back to the code that dispatched and it can make local decisions for the right behavior is. It’s not an “error” for there to be no results to autocomplete, that’s normal expectations.

If you’re missing required parameters, you’ve got a serious bug in your code. That should be indicated in a way that’s distinct from normal failure.

Recommendation

Given the idea that you want to have errors be programatic errors and ok’s be valid, this is what should be used for HTTP response code for APIs.

HTTP Code Description
200 Structurally valid API request made – API “ok” boolean indicates details
400 No authentication provided or missing required parameter
401 Invalid authentication token
404 Endpoint doesn’t exist
429 Rate limited
500 Internal error – this should be returned even when a backend dies