> ## Documentation Index
> Fetch the complete documentation index at: https://data-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://data-api.orbbit.co/v1/commodities/metric-values?metric_names=cheddar_close' \
  --header 'authorization: Bearer YOUR_API_KEY'
```

```json 400 Unknown metric theme={null}
{
  "error": {
    "type": "invalid_request",
    "message": "Unknown metrics: cheddar_close"
  }
}
```

| 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 or show it to a developer, but don't parse it, because 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 malformed, unknown, or revoked. | Send `Authorization: Bearer <key>` with a live key. |
| `402` | `insufficient_balance` | Paid calls only. Your balance can't cover the call (`Insufficient balance`), or this key has used up its monthly spend budget (`Monthly spend cap reached`). Refused calls are not charged. | Top up on **Balance** or turn on auto-recharge, or raise the key's budget. See [Pricing & Billing](https://data-docs.orbbit.co/pricing.md). |
| `402` | `payment_required` | A paid call with no API key and no valid wallet payment. The body is the x402 price quote, not the `error` object above. Nothing was charged. | Pay with x402, or send an API key. See [Pay With x402](https://data-docs.orbbit.co/for-agents/x402.md). |
| `403` | `forbidden` | The key is valid but lacks this endpoint's scope. | Add the scope in the console. See [Authentication](https://data-docs.orbbit.co/authentication.md#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. |
| `429` | `rate_limited` | Calls without an API key only. More than 60 calls in a minute from one IP address. | Wait and try again, or send an API key. |
| `503` | `service_unavailable` | Wallet payments only. The payment service for the chosen network could not be reached. Nothing was charged. | Retry, or pay on the other network. |
| Other `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/metric-values` | `Unknown metrics: <names>` | A name in `metric_names` or `ids` doesn't exist. Get valid names from `GET /v1/commodities`. |
| Any endpoint | `query: Unrecognized key: "<name>"` | The call sends a parameter the endpoint doesn't take, such as the older `metrics`, `from` or `to`. `GET /v1/commodities/metric-values` takes `metric_names`, `ids`, `start_date` and `end_date`. |
| `GET /v1/commodities/metric-values` | `This call would return <n> prices, more than 50000; …` | The series and dates asked for hold more than 50,000 values. Narrow `start_date` and `end_date`, or ask for fewer series. |
| `GET /v1/commodities/metric-values` | `metric_names: give ids or metric_names` | Neither `metric_names` nor `ids` was sent. |
| `GET /v1/commodities` | `Unknown attribute words: <words>` | An `attributes[<word>]` filter names a word no product has. |
| `GET /v1/commodities` | `Unknown category <value>; category is one of …` | `category` is not one of `dairy`, `poultry`, `meat`, `grains`, `fruits` or `vegetables`. |
| `GET /v1/commodities/metric-values` | None | More than 100 series are named, a date is not a real `YYYY-MM-DD` day, or `start_date` is later than `end_date`. |
| `GET /v1/commodities` | `limit: Too big: expected number to be <=500` | `limit` is above 500. |
| `GET /v1/branded-foods` | `at most 200 per page` | `limit` is above 200. |
| Any endpoint with `limit` | None | `limit` is not a whole number of at least 1. |

## Retrying

Retry only `5xx` errors, and `429` once the minute is up. Every other error will fail again until you change the request, or, for `402`, until you add balance. 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
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.