# Designing API responses
Source: https://speakeasy.com/api-design/responses

import { Callout } from '@/mdx/components';



The API response is the most important part of the entire API. 

- Did the API consumer ask a question? They need that answer.
- Did the API consumer ask to transfer £1,000,000? They need to be confident
that went well.
- Did the API consumer make a mistake? Tell them how to fix it so they can get
back to making you money.

Creating clear, consistent API responses is crucial for building usable APIs.
This guide covers essential patterns and best practices for API responses.

## Anatomy of an API Response

An API response is primarily made up of a status code, headers, and a response body,
so let's look at each of those parts in turn.

### Headers

Just like requests allow API consumers to add HTTP headers to act as metadata for the request, APIs and other network components can add headers to a response.

```http
HTTP/2 200 OK
Content-Type: application/json
Cache-Control: public, max-age=18000
RateLimit: "default";r=50;t=30

{
  "title": "something"
}
```

This is a successful request, with some JSON data as highlighted by the `Content-Type` header. The API has also alerted the API consumer that this is cacheable so they don't need to ask for it again for 5 hours, and explained that the client is running a little low on their rate limiting policy with only 50 more requests allowed in the next 30 seconds.

API responses contain lots of useful metadata in the headers, but data is going to be in the response body.

### Response Body

You should strive to keep response consistent and well-structured, with minimal nesting and correct use of data types.

```json
{
  "id": 123,
  "name": "High Wood",
  "location": {
    "lat": 50.464569783,
    "lon": -4.486597585
  },
  "created_at": "2024-10-24T12:00:00Z",
  "links": {
    "reviews": "/places/123/reviews"
  }
}
```

It's pretty common to add an `id` of some sort, often data will have dates, and
relationships and available actions can be linked allowing API consumers to easily
find related information without going on a scavenger hunt.

### Status Codes

So far we've only looked at success, but how do we know if something
has worked or not? 

You could look at the response body and try to figure it out, and for years
people were doing silly things like setting `{ "success": true/false }` in their
response body to give people a hint, but as always there's a far better way
defined in the HTTP spec which covers loads more use-cases and works out of the
box with loads of tools: HTTP Status Codes.

A status code is a number and a matching phrase, like `200 OK` and `404
Not Found`. There are countless status codes defined in the HTTP RFCs and
elsewhere, and some big companies have invented their own which became common
conventions, so there's plenty to choose from.

Arguments between developers will continue for the rest of time over the
exact appropriate status code to use in any given situation, but these are the
most important status codes to look out for in an API:

#### 2XX is all about success

Whatever the API was asked to do was successful, up to the point that the
response was sent. A `200 OK` is a generic "all good", a `201 Created` means
something was created, and a `202 Accepted` is similar but does not say anything
about the actual result, it only indicates that a request was accepted and is
being processed asynchronously. It could still go wrong, but at the time of
responding it was all looking good at least up until it was put in the queue.

The common success status codes and when to use them:

* **200** - Generic everything is OK.
* **201** - Created something OK.
* **202** - Accepted but is being processed async (for a video means.
encoding, for an image means resizing, etc.)
* **204** - No Content but still a success. Ideal for a successful `DELETE` request, for example.

Example success response

```http
HTTP/1.1 200 OK
Content-Type: application/json

{
  "user": {
    "id": 123,
    "name": "John Doe"
  }
}
```

#### 3XX is all about redirection

These are all about sending the calling application somewhere else for the
actual resource. The best known of these are the `303 See Other` and the `301
Moved Permanently`, which are used a lot on the web to redirect a browser to
another URL. Usually a redirect will be combined with a `Location` header to
point to the new location of the content.

#### 4XX is all about client errors

Indicate to your clients that they did something wrong. They might have
forgotten to send authentication details, provided invalid data, requested a
resource that no longer exists, or done something else wrong which needs fixing.

Key client error codes:

* **400** - Bad Request (should really be for invalid syntax, but some folks.
use for validation).
* **401** - Unauthorized (no current user and there should be).
* **403** - The current user is forbidden from accessing this data.
* **404** - That URL is not a valid route, or the item resource does not exist.
* **405** - Method Not Allowed (your framework will probably do this for you.)
* **406** - Not Acceptable (the client asked for a content type that the API does not support.)
* **409** - Conflict (Maybe somebody else just changed some of this data, or status cannot change from e.g: "published" to "draft").
* **410** - Gone - Data has been deleted, deactivated, suspended, etc.
* **415** - The request had a `Content-Type` which the server does not know how to handle.
* **429** - Rate Limited, which means take a breather, sleep a bit, try again.

#### 5XX is all about service errors

With these status codes, the API, or some network component like a load
balancer, web server, application server, etc. is indicating that something went
wrong on their side. For example, a database connection failed, or another
service was down. Typically, a client application can retry the request. The
server can even specify when the client should retry, using a `Retry-After` HTTP
header.

Key server error codes:

* **500** - Something unexpected happened, and it is the API's fault.
* **501** - This bit isn't finished yet, maybe it's still in beta and you don't have access.
* **502** - API is down, but it is not the API's fault.
* **503** - API is not here right now, please try again later.

As you can see, there are a whole bunch of HTTP status codes. You don't need to try and use
them all, but it is good to know what they are and what they mean so you can use
the right one for the job.

You have two choices, either read the [full list of status codes from the
IANA](https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml),
or swing by [http.cats](http://http.cat/) and see what the cats have to say
about it.

### Using Status Codes

```ts
import axios, { AxiosError } from 'axios';

async function makeHttpRequest() {
  try {
    const response = await axios.get('https://example.com/api/resource');
    console.log('Response:', response.data);
  } catch (error) {
    if (! axios.isAxiosError(error)) {
      console.error('An unexpected error occurred:', error);
      return;
    }
    const axiosError = error as AxiosError;
    if (axiosError.response?.status === 401) {
      console.error('You need to log in to access this resource.');
    } else if (axiosError.response?.status === 403) {
      console.error('You are forbidden from accessing this resource.');
    } else if (axiosError.response?.status === 404) {
      console.error('The resource you requested does not exist.');
    } else {
      console.error('An error occurred:', axiosError.message);
    }
  }
}

makeHttpRequest();
```

Now you can warn API consumers of fairly specific problems. Doing it way is
cumbersome, but there's plenty of generic libraries with various extensions and
"middlewares" that will help auto-retry any auto-retriable responses,
automatically cache cachable responses, and so on.

<Callout title="Note" type="info">
  Avoid confusing your API consumers by <a href="/docs/runtime/retries">enabling retry logic</a> in your Speakeasy SDK.
</Callout>

## Best Practices

### 1. Keep Status Codes Appropriate & Consistent

It's important to keep status codes consistent across your API, ideally across your entire organization. 

This is not just for nice feels, it helps with code reuse, allowing consumers to
share code between endpoints, and between multiple APIs. 

This means they can integrate with you quicker, and with less code, and less maintenance overhead.

### 2. Keep Request & Response Bodies Consistent

Sometimes API developers end up with divergent data models between the request
and the response, and this should be avoided whenever possible. 

Whatever shape you pick for a request, you should match that shape on the response.

```json
// POST /places 

{
  "name": "High Wood",
  "lat": 50.464569783,
  "lon": -4.486597585
}
```

```json
// GET /places/123

{
  "id": 123,
  "name": "High Wood",
  "lat": 50.464569783,
  "lon": -4.486597585,
  "created_at": "2024-10-24T12:00:00Z"
}
```

You can see that some differences, like `id` or `created_at` dates on the
response but not the request. That's OK, because they can be handled as
"read-only" or "write-only" fields in the API documentation and generated code,
meaning they are using the same models just ignoring a few fields depending on
the context.

The problem often comes from various clients having a word with the API
developers about "helping them out", because some library being used by the iOS
app would prefer to send coordinates as a string and they don't want to convert
them to a decimal for some reason. Then the API team wanted to have the
responses wrapped into objects to make it look tidy, but the React team said it
would be too hard to get their data manager to do that, so the request skipped
it.

```json
// POST /places

{
  "name": "High Wood",
  "lat": "50.464569783",
  "lon": "-4.486597585"
}
```

```json
// GET /places/123

{
  "id": 123,
  "name": "High Wood",
  "location": {
    "lat": 50.464569783,
    "lon": -4.486597585
  },
  "created_at": "2024-10-24T12:00:00Z"
}
```

Aghh!

This sort of thing causes confusion for everyone in the process, and whilst any
one change being requested might feel reasonable, when a few of them stack up
the API becomes horrible to work with. 

Push back against request/response model deviations. It's not worth it.

### 3. Return detailed errors

Just returning a status code and a message is not enough, at the bare minimum
add an error message in the JSON body that adds more context. 

```
HTTP/2 409 Conflict
Content-Type: application/json

{
  "error": "A place with that name already exists."
}
```

This is better than nothing but not ideal. Other information needs to be added
to help with debugging, and to help the API client differentiate between errors.

There is a better way: [RFC 9457](https://tools.ietf.org/html/rfc9457) which
defines a standard way to return errors in JSON (or XML).

```http
HTTP/2 409 Conflict
Content-Type: application/problem+json

{
  "type": "https://api.example.com/probs/duplicate-place",
  "title": "A place with that name already exists.",
  "detail": "A place with the name 'High Wood' already exists close to here, have you or somebody else already added it?",
  "instance": "/places/123/errors/<unique-id>",
  "status": 409
}
```

More on this in the [API Errors](/api-design/errors) guide.

## Best Practices

### 1. Keep Status Codes Appropriate & Consistent

It's important to keep status codes consistent across your API, ideally across your entire organization. 

This is not just for nice feels, it helps with code reuse, allowing consumers to
share code between endpoints, and between multiple APIs. 

This means they can integrate with you quicker, and with less code, and less maintenance overhead.

### 2. Keep Request & Response Bodies Consistent

Sometimes API developers end up with divergent data models between the request
and the response, and this should be avoided whenever possible. 

Whatever shape you pick for a request, you should match that shape on the response.

```json
// POST /places 

{
  "name": "High Wood",
  "lat": 50.464569783,
  "lon": -4.486597585
}
```

```json
// GET /places/123

{
  "id": 123,
  "name": "High Wood",
  "lat": 50.464569783,
  "lon": -4.486597585,
  "created_at": "2024-10-24T12:00:00Z"
}
```

You can see that some differences, like `id` or `created_at` dates on the
response but not the request. That's OK, because they can be handled as
"read-only" or "write-only" fields in the API documentation and generated code,
meaning they are using the same models just ignoring a few fields depending on
the context.

The problem often comes from various clients having a word with the API
developers about "helping them out", because some library being used by the iOS
app would prefer to send coordinates as a string and they don't want to convert
them to a decimal for some reason. Then the API team wanted to have the
responses wrapped into objects to make it look tidy, but the React team said it
would be too hard to get their data manager to do that, so the request skipped
it.

```json
// POST /places

{
  "name": "High Wood",
  "lat": "50.464569783",
  "lon": "-4.486597585"
}
```

```json
// GET /places/123

{
  "id": 123,
  "name": "High Wood",
  "location": {
    "lat": 50.464569783,
    "lon": -4.486597585
  },
  "created_at": "2024-10-24T12:00:00Z"
}
```

Aghh!

This sort of thing causes confusion for everyone in the process, and whilst any
one change being requested might feel reasonable, when a few of them stack up
the API becomes horrible to work with. 

Push back against request/response model deviations. It's not worth it.

### 3. Return detailed errors

Just returning a status code and a message is not enough, at the bare minimum
add an error message in the JSON body that adds more context. 

```
HTTP/2 409 Conflict
Content-Type: application/json

{
  "error": "A place with that name already exists."
}
```

This is better than nothing but not ideal. Other information needs to be added
to help with debugging, and to help the API client differentiate between errors.

There is a better way: [RFC 9457](https://tools.ietf.org/html/rfc9457) which
defines a standard way to return errors in JSON (or XML).

```http
HTTP/2 409 Conflict
Content-Type: application/problem+json

{
  "type": "https://api.example.com/probs/duplicate-place",
  "title": "A place with that name already exists.",
  "detail": "A place with the name 'High Wood' already exists close to here, have you or somebody else already added it?",
  "instance": "/places/123/errors/<unique-id>",
  "status": 409
}
```

More on this in the [API Errors](/api-design/errors) guide.