# Errors

> Branch on the status first, then on code. Retry only what the table marks retry: a 429 after Retry-After, a 5xx with backoff. Everything else needs a change on your side.

Source: https://firmtape.com/docs/developers/errors

Branch on the status first, then on `code`. Retry only what the table marks retry: a 429 after `Retry-After`, a 5xx with backoff. Everything else needs a change on your side.

## The shape

- LIVE, ORDERS: `{ "error": { "code": "<code>", "message": "<words>" } }`. The levels API, the Orders API and the POSITIONS API. Extra fields ride beside `code` where a table names them (`retry_after_s`, `oldest`, `pricing`, `live`).
- LAB: `{ "error": "<words>" }`. The LAB API answers with the status and a sentence; extra fields beside it (`buy`, `holder`, `refunded`).
- GEO: `{ "error": "<words>" }`. The Geo API, the same way.

The levels API's `levels` answers 200 for the whole call; a symbol that fails carries its own `error` object (`unknown_symbol`, `no_data`, `no_basis`, `contract_not_live`) and the others still answer.

A platform that cannot read an HTTP status adds `errors=200` to a levels API call: an error then answers 200 with the same body and the real status in `X-FirmTape-Status`.

## Every code

| Status | Code | Retry? | Products | Meaning | What to do |
| --- | --- | --- | --- | --- | --- |
| 400 | `missing_sym` | no | LIVE | No `sym` was given. | Pass `sym`, one symbol or a comma list. |
| 400 | `too_many_symbols` | no | LIVE | More symbols than the call takes (20 a `levels` call, 8 a levels string). | Split the list over two calls. |
| 400 | `bad_book` | no | LIVE | `book` is not `vol` or `oi`. | Send `book=vol` or `book=oi`. |
| 400 | `bad_g` | no | LIVE | `g` is not a whole number from 0 to 30. | Send `g` between 0 and 30. |
| 400 | `bad_day` | no | LIVE, ORDERS | `day` is not `YYYY-MM-DD`. | Send the ET trading day as `YYYY-MM-DD`. |
| 400 | `bad_time` | no | LIVE, ORDERS | `time` (levels API, 09:31 to 15:59) or `t` (POSITIONS) is not an ET minute `HH:MM` of the session. | Send a minute of the session. |
| 400 | `not_finished` | no | LIVE | A past session was asked for with today's date or later. | Leave `day` out for today. |
| 400 | `spx_only` | no | LIVE | The expected move is measured for SPX only. | Send `sym=SPX` or leave it out. |
| 400 | `bad_json` | no | ORDERS | The body is not JSON. | Send `Content-Type: application/json` and a JSON object. |
| 400 | `bad_op` | no | ORDERS | `op` is not `add`, `update` or `remove`. | Send one of the three. |
| 400 | `invalid` | no | ORDERS | The position cannot be checked as sent; `message` says what to fix, in words. | Correct the field the message names. |
| 401 | `no_key` | no | LIVE, ORDERS | No key was sent (Orders: no key, or one that is unknown or revoked). | Send `Authorization: Bearer <key>` with a key of the product's scope. |
| 401 | `bad_key` | no | LIVE | The key is unknown or revoked. | Make a new key on Account, Integrations. |
| 403 | `live_required` | no | LIVE | The key is real; its account holds no active LIVE plan. | Start or renew LIVE; `pricing` names the page. |
| 403 | `history_too_old` | no | LIVE | The day is older than 90 days and the account holds no DATASET licence. | Ask for a day from `oldest` on, or add the DATASET licence. |
| 403 | `plan` | no | ORDERS | The key is real; the account is not on the Orders API or Team plan. | Move to the Orders API or Team plan. |
| 404 | `not_found` | no | LIVE, ORDERS | No such endpoint (levels API), or no position with this id on this account (POSITIONS). | Check the path or the id. |
| 404 | `no_route` | no | ORDERS | No such endpoint under `/orders/api`. | Use `day`, `latest` or `sessions`. |
| 404 | `unknown_symbol` | no | LIVE | FirmTape has no book for that symbol. | Pick one from `GET /api/v1/symbols`. |
| 404 | `no_data` | yes | LIVE | No reading for that symbol or day yet (before the first book of a session, or a day not in the archive). | Ask again after `next_update_s`, or pick another day. |
| 404 | `no_basis` | yes | LIVE | No ES, NQ or RTY basis measured for that day yet; the index levels exist, the offset does not. | Ask the index (SPX, NDX, RUT) or wait for the first basis pass. |
| 404 | `contract_not_live` | no | LIVE | That dated futures contract is not measured that day; `live` lists the ones that are. | Ask the front contract or one named in `live`. |
| 405 | `method_not_allowed` | no | LIVE | The levels API answers GET and HEAD only. | Send a GET. |
| 405 | `method` | no | ORDERS | The POSITIONS API takes GET or POST. | Send a GET or a POST. |
| 429 | `rate_limited` | yes | LIVE | More than 120 requests in a minute on one key. | Wait `Retry-After` seconds; poll with `If-None-Match`, a 304 is free. |
| 429 | `too_many_places` | no | LIVE | A `live` key answered from 3 client addresses this hour; a fourth is refused while the others keep working. | Make one key per machine. |
| 429 | `rate` | yes | ORDERS | The account's calls for the day are spent. | Wait for midnight ET, when the count resets. |
| 500 | `internal` | yes | LIVE | An error on FirmTape's side. | Retry with backoff; write to support if it lasts. |
| 503 | `unavailable` | yes | LIVE | The account store did not answer while the key was checked. | Retry shortly. |
| 401 |  | no | LAB | No key, or an unknown or revoked one. | Send a `lab` key. |
| 402 |  | no | LAB | No credits: `{"error": "no credits", "buy": "lab"}`. Developing an idea also needs a positive balance. | Buy credits on the LAB page. |
| 404 |  | no | LAB | No such idea or job, or not this account's. | Check the id. |
| 409 |  | no | LAB | Another idea holds the conveyor (`holder` names it), or a step came out of order. | Finish or drop the holder; run the steps in order. |
| 422 |  | no | LAB | The rule could not be compiled or the engine refused it (the credit is refunded). | Change the rule. |
| 502 |  | yes | LAB | The engine failed (the credit is refunded). | Retry. |
| 400 |  | no | GEO | A parameter is wrong; no call is counted. | Fix the parameter `error` names. |
| 401 |  | no | GEO | No key, an unknown key or a revoked one. | Send a `geo` key. |
| 403 |  | no | GEO | No active Geo API or Team plan, or a key beyond the plan's key count. | Check the plan on the account page. |
| 429 |  | yes | GEO | The month's calls are used. | Wait for `Retry-After`, the start of the next UTC month. |
