# Errors

> What an error looks like, what each code means and when to retry.

> Documentation index for agents: https://kinodata.space/en/llms.txt — the whole documentation in one file: https://kinodata.space/en/llms-full.txt

When a request fails, the API responds with a 4xx or 5xx status and an `error` object. The code says what happened; the fields next to it say which parameter is at fault and which values are allowed. Here the catalog type has a typo, `tmbd` instead of `tmdb`:

```bash
curl -H "X-API-Key: $KINODATA_KEY" \
  "https://api.kinodata.space/v1.3/ids/tmbd/27205"
```

Response `400`:

```json
{
  "error": {
    "code": "invalid_request",
    "message": "unknown type \"tmbd\"",
    "field": "type",
    "allowed": [
      "imdb",
      "kp",
      "tmdb",
      "tmdb_tv",
      "kinorium"
    ],
    "hint": "did you mean \"tmdb\"?"
  }
}
```

## Error fields

| field | always present | what it is |
|---|---|---|
| `code` | yes | A machine-readable code. Branch on it. |
| `message` | yes | A human-readable explanation in English. The wording may change, so don't parse it. |
| `field` | no | The parameter that caused the rejection. |
| `allowed` | no | The values that parameter accepts. |
| `hint` | no | A suggestion when the value looks like an allowed one (`tmbd` → `tmdb`) or is part of one (`sort=kp` → `rating_kp` or `votes_kp`). |

Branch on `code`, not on the status: one status can have several causes.

## Codes

### invalid_request

- **Status:** 400.
- **When:** an unknown parameter or value, an ID that doesn't match the catalog format, a repeated parameter, a `limit` that isn't a whole number from 1.
- **What to do:** read `field`, `allowed` and `hint`, then fix the request.

### unauthorized

- **Status:** 401.
- **When:** the `X-API-Key` header is missing, or the key is unknown or revoked.
- **What to do:** check that the header is named `X-API-Key` and that the whole key was copied.

### forbidden

- **Status:** 403.
- **When:** requests from your address are blocked because it was guessing keys or otherwise broke the rules. `message` is `this address is blocked`. A ban for guessing keys lifts by itself after a day.
- **What to do:** if your address was blocked by mistake, for example because an office or a carrier shares it, [contact us](https://kinodata.space/en/contacts).

### not_found

- **Status:** 404.
- **When:** no title or person with this ID, no collection with this `slug`, or no such path.
- **What to do:** check the type and the ID. `tmdb` and `tmdb_tv` are easy to mix up, and their IDs are different.

### rate_limited

- **Status:** 429.
- **When:** the per-second rate limit or the daily quota is used up. `message` says which: `rate limit exceeded` for the second, `quota limit exceeded` for the day.
- **What to do:** wait the number of seconds in the `Retry-After` header. The daily quota resets at midnight UTC.
- **How to stay under it:** every response shows what is left. `X-RateLimit-Remaining` is the requests left in the current second, `X-Quota-Remaining` is the requests left today, and `X-Quota-Reset` is when the quota resets (Unix time). Keys without limits don't get these headers.

### internal

- **Status:** 500.
- **When:** something failed on our side.
- **What to do:** retry after a pause. If it keeps happening, contact us and include the `X-Request-Id`.

### source_unavailable

- **Status:** 502.
- **When:** a catalog we were filling data from did not respond.
- **What to do:** retry later.

## Should you retry

| code | retry |
|---|---|
| `rate_limited` | yes, after `Retry-After` seconds |
| `internal`, `source_unavailable` | yes, with a growing pause: 1, 2, 4 seconds |
| everything else | no: the request won't fix itself |

Every request in the API is a `GET`, so a retry changes nothing and is safe.

## If you're stuck

Every response carries an `X-Request-Id` header. Include it when you contact us and we'll find your request.

## Next

- [Keys and limits](https://kinodata.space/en/docs/keys.md): how to stay clear of `429`.
- [Lists and pagination](https://kinodata.space/en/docs/lists.md): the rules for `limit`, `offset` and `cursor`.
