> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbbit.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Errors

> Every error the API returns, in one shape, with what caused it and what to do.

Every error, on every endpoint, has the same shape: a status code and a JSON body with an `error` object. For example, asking for a series that doesn't exist:

```bash theme={null}
curl --request GET \
  --url 'https://api.orbbit.co/v1/commodities/prices?series=butter-spot' \
  --header 'authorization: Bearer YOUR_API_KEY'
```

```json 400 — unknown series theme={null}
{
  "error": {
    "type": "invalid_request",
    "message": "Unknown series: butter-spot"
  }
}
```

| Field           | Type   | Description                                                                                                      |
| --------------- | ------ | ---------------------------------------------------------------------------------------------------------------- |
| `error.type`    | string | A stable, machine-readable name for the kind of error. Branch on this.                                           |
| `error.message` | string | A human-readable explanation. Log it, show it to a developer — but don't parse it, since the wording can change. |

## Error types

| Status | `error.type`      | What happened                                                                                | What to do                                                                                      |
| ------ | ----------------- | -------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
| `400`  | `invalid_request` | A parameter is missing, the wrong type, or out of range.                                     | Fix the request. The message names the parameter.                                               |
| `401`  | `unauthorized`    | The API key is missing, malformed, unknown, or revoked.                                      | Send `Authorization: Bearer <key>` with a live key.                                             |
| `403`  | `forbidden`       | The key is valid but lacks this endpoint's scope.                                            | Add the scope in the console. See [Authentication](/authentication#choose-what-a-key-may-call). |
| `404`  | `not_found`       | No record has the ID in the path.                                                            | Check the ID came from a search result.                                                         |
| `5xx`  | `internal`        | Something went wrong on Orbbit's side. The message is always `An unexpected error occurred`. | Retry with exponential backoff. If it persists, contact support.                                |

## Common 400 errors

| Endpoint                     | Message                  | Cause                                                                                                              |
| ---------------------------- | ------------------------ | ------------------------------------------------------------------------------------------------------------------ |
| `GET /v1/commodities/prices` | `Unknown series: <slug>` | A slug in `series` is not in the catalog. Get valid slugs from `GET /v1/commodities`.                              |
| `GET /v1/commodities/prices` | —                        | `series` is missing, has more than 100 slugs, a date is not a real `YYYY-MM-DD` day, or `from` is later than `to`. |
| `GET /v1/branded-foods`      | `at most 200 per page`   | `limit` is above 200.                                                                                              |
| Any endpoint with `limit`    | —                        | `limit` is not a whole number of at least 1.                                                                       |

## Retrying

Retry only `5xx` errors. Every other error will fail again until you change the request. Every endpoint only reads data, so retrying a request never changes anything.

```python Python theme={null}
import random, time, requests

def get_with_retry(url, headers, params=None, attempts=5):
    for attempt in range(attempts):
        response = requests.get(url, headers=headers, params=params)
        if response.status_code < 500:
            return response
        time.sleep(min(2 ** attempt, 30) + random.random())
    return response
```
