---
title: "Errors"
description: "The Tapetide API error envelope, every error code, and which errors are charged."
canonical: "https://developers.tapetide.com/errors/"
---

# Errors

Every error has the same shape and an HTTP status that matches it:

```json
{
  "error": {
    "code": "invalid_request",
    "message": "\"limit\" number must be less than or equal to 500.",
    "details": { "param": "limit" }
  }
}
```

-   `code` is stable. Branch on it, not on `message`.
-   `message` is for people and may change wording.
-   `details` is optional. For parameter errors it names the offending `param`.

## Error codes

| Status | `code` | When | What to do |
| --- | --- | --- | --- |
| 400 | `invalid_request` | A parameter is unknown, malformed, out of range, or a cursor was not issued by the API. | Fix the parameter named in `details.param`. |
| 401 | `authentication_required` | No bearer token was sent. | Send `Authorization: Bearer <token>`. |
| 401 | `invalid_token` | The token is invalid, expired or revoked. | Create a new token at [tapetide.com/settings](https://tapetide.com/settings). |
| 404 | `not_found` | The symbol, index or document does not exist. | Look the symbol up with `GET /v1/stocks?q=`. |
| 404 | `route_not_found` | No such path. | Check the path against the [reference](/api/). |
| 405 | `method_not_allowed` | A method other than `GET` (or `OPTIONS`). | The API is read-only. |
| 429 | `rate_limited` | Per-minute burst cap reached. | Wait `Retry-After` seconds. |
| 429 | `quota_exhausted` | Your plan’s daily or monthly quota is used. | Wait until `X-RateLimit-Reset`, or upgrade. |
| 500 | `tool_error` | The request reached a dataset that could not answer it. | Retry once; report it if it persists. |
| 500 | `internal_error` | An unexpected failure. | Retry with backoff. |
| 502 | `upstream_unavailable` | A data dependency was briefly unavailable. | Retry with backoff. |
| 503 | `service_unavailable` | Tapetide is briefly unavailable. | Wait `Retry-After` seconds. |

## Charging

Only a `200` costs a request unit. Every error above is free, including `429`.

## Retrying

Retry `429`, `500`, `502` and `503`. Do not retry `400`, `401`, `404` or `405` without changing the request.

```python
import time, requests

def get(session, url, attempts=4):
    for i in range(attempts):
        r = session.get(url, timeout=30)
        if r.status_code not in (429, 500, 502, 503):
            return r
        wait = int(r.headers.get("Retry-After", 2 ** i))
        if r.status_code == 429 and r.json()["error"]["code"] == "quota_exhausted":
            return r  # waiting will not help within this run
        time.sleep(min(wait, 60))
    return r
```
