# Get levels

> The volume book's flip, call wall, put wall and net gamma for up to 20 symbols in one call, with the open-interest book alongside under oi.

Source: https://firmtape.com/docs/developers/live/levels

The volume book's flip, call wall, put wall and net gamma for up to 20 symbols in one call, with the open-interest book alongside under `oi`.

`GET /api/v1/levels`

Plan LIVE · Key scope live · Limit 120 a minute per key; a 304 is free · Version 1.0.0 · Since 2026-09-30

**This endpoint serves:** [LIVE: Levels API](https://firmtape.com/docs/live#levels-api), [LIVE: Walls and levels](https://firmtape.com/docs/live#walls-and-levels), [LIVE: Futures views: ES, NQ, RTY, GC, SI, ZB, CL](https://firmtape.com/docs/live#futures-views), [LIVE: Platform integrations](https://firmtape.com/docs/live#integrations). **Plan:** [LIVE](https://firmtape.com/features/live). LIVE: today's session every second, alerts, ASK, the levels API and the platform integrations, and 5 LAB credits a month. Every finished session is free in REPLAY.

## Parameters

| Name | What it does | Default | Error |
| --- | --- | --- | --- |
| `sym` (required) query, string | Comma list, up to 20: `SPX,ES,NQ,QQQ`. MES, MNQ and M2K answer as ES, NQ and RTY; a dated contract (`ESH27`) names that contract. |  | missing_sym, too_many_symbols |
| `include` query, gamma | `gamma` adds each symbol's gamma by strike: one request per chart. |  |  |
| `book` query, vol \| oi | With `include=gamma`: which book's strikes. | vol | bad_book |
| `contract` query, string | Futures: a dated contract in the two weeks before a roll, e.g. `ESH27`. The levels move by that contract's own basis. | the front quarterly | contract_not_live |
| `errors` query, 200 | `200`: an error answers 200 with the same body and the real status in `X-FirmTape-Status`, for clients that cannot read a status. |  |  |
| `key` query, string | Legacy: the key in the address, for platforms that cannot send headers. Prefer the header. |  |  |

## Response

One entry per symbol asked, in order. A symbol that fails carries its own `error`; the rest still answer.

| Field | Meaning | Unit |
| --- | --- | --- |
| `book` ("volume") | Always `volume`. |  |
| `next_update_s` (integer) | The smallest `next_update_s` of the symbols. | seconds |
| `levels` (array) | The readings. |  |
| `levels[].sym` (string) | The symbol answered: SPX, ES, NQ, RTY or a ticker. A micro (MES, MNQ, M2K) answers as its future. |  |
| `levels[].requested` (string) | Present when the symbol asked differs from `sym` (`MES` answered as `ES`). |  |
| `levels[].name` (string) | The symbol's name. |  |
| `levels[].class` (string) | `index`, `future`, `etf` or `stock`. |  |
| `levels[].contract` (string) | Futures: the contract the levels are moved to, e.g. `ESZ26`. |  |
| `levels[].session` (open \| closed \| stale) | `open` while the book is current, `closed` outside market hours, `stale` when market hours are on but the book has not moved for a while. |  |
| `levels[].day` (string) | The ET trading day of the reading, `YYYY-MM-DD`. |  |
| `levels[].book` ("volume") | Always `volume`: every level is the volume book, the one the terminal draws. |  |
| `levels[].price` (number \| null) | The price at the reading. | index points |
| `levels[].price_as_of` (string) | When that price was taken, ISO 8601 in New York time. |  |
| `levels[].flip` (number \| null) | Zero-gamma flip of the volume book. On a finished day, read at the levels minute (`levels_at`). | index points |
| `levels[].call_wall` (number \| null) | Call wall: the strike with the largest call gamma. On a finished day, read at the levels minute. | index points |
| `levels[].put_wall` (number \| null) | Put wall: the strike with the largest put gamma. On a finished day, read at the levels minute. | index points |
| `levels[].net_gamma` (object) | Net dealer gamma of the book: its value, its sign and its unit, named on every answer. |  |
| `levels[].net_gamma.value` (number \| null) | Net dealer gamma, in `unit`. |  |
| `levels[].net_gamma.sign` (positive \| negative \| null) | The sign of `value`. |  |
| `levels[].net_gamma.unit` (usd_m_per_point \| usd_per_1pct) | `usd_m_per_point` (millions of USD per index point: SPX, ES) or `usd_per_1pct` (USD per 1% move: tickers, NQ, RTY). |  |
| `levels[].at_flip` (boolean) | SPX and ES: the price is within 5 points of the flip, where the side is not a reliable reading. |  |
| `levels[].dealer_gamma` (long \| short \| null) | SPX and ES: the side of net gamma; null at the flip. |  |
| `levels[].gamma_pctl` (number \| null) | SPX and ES: the volume book's gamma at spot against the same minute of the last 20 sessions, a decile from -11 to +11. A measurement, with no measured forward edge. |  |
| `levels[].as_of` (string) | When the book was built, ISO 8601 in New York time. |  |
| `levels[].as_of_unix` (integer) | The same instant, Unix seconds. |  |
| `levels[].age_s` (integer) | Seconds since the book was built. | seconds |
| `levels[].cadence` (object) | How often the book and the price are rebuilt. |  |
| `levels[].cadence.book` (string) | `1s` (SPX, ES) or `5m` (tickers, NQ, RTY). |  |
| `levels[].cadence.price` (string) | `1s`. |  |
| `levels[].grade` (string \| null) | Tickers, NQ, RTY: the chain's trust grade (A best). Null for SPX and ES. |  |
| `levels[].flip_status` (string \| null) | Tickers: whether the flip was found inside the strike grid. |  |
| `levels[].oi` (object \| null) | The open-interest book's levels, the same fields; null when the symbol has none. |  |
| `levels[].oi.flip` (number \| null) | Zero-gamma flip of the open-interest book. | index points |
| `levels[].oi.call_wall` (number \| null) | Call wall of the open-interest book. | index points |
| `levels[].oi.put_wall` (number \| null) | Put wall of the open-interest book. | index points |
| `levels[].oi.net_gamma` (object) | Net dealer gamma of the book: its value, its sign and its unit, named on every answer. |  |
| `levels[].oi.flip_status` (string \| null) | Tickers: whether the flip was found inside the strike grid (`ok`) or not. |  |
| `levels[].basis` (object) | ES, NQ, RTY: the day's basis that moved the index levels. |  |
| `levels[].basis.value` (number \| null) | Future minus index. | index points |
| `levels[].basis.index` (string) | The index the book is measured on: SPX, NDX or RUT. |  |
| `levels[].basis.passes` (integer) | Basis passes measured so far today. |  |
| `levels[].basis.rule` (string) | How the basis is made: the running median of the day's passes, rounded to the future's tick. |  |
| `levels[].source` (string) | Futures: which index book and which basis. |  |
| `levels[].levels_at` (15:45 \| 12:45) | A finished day only: the levels minute (ET) `flip`, `call_wall` and `put_wall` were read at. Absent while the session runs. |  |
| `levels[].last_second` (object) | A finished day only: the replaced fields as the session's last second had them. |  |
| `levels[].last_second.flip` (number \| null) | Flip at the last second. | index points |
| `levels[].last_second.call_wall` (number \| null) | Call wall at the last second. | index points |
| `levels[].last_second.put_wall` (number \| null) | Put wall at the last second. | index points |
| `levels[].last_second.at_flip` (boolean) | At the flip, at the last second. |  |
| `levels[].last_second.dealer_gamma` (long \| short \| null) | The side, at the last second. |  |
| `levels[].next_update_s` (integer) | When a new reading is worth asking for: 3 for SPX and ES, up to 300 for a five-minute book, 60 when the session is not open. | seconds |
| `levels[].gamma` (object) | With `include=gamma`: the symbol's gamma by strike (the fields of `GET /api/v1/gamma`), or an `error`. |  |
| `levels[].error` (object) | This symbol failed; the others in the call still answer. |  |
| `levels[].error.code` (string) | `unknown_symbol`, `no_data`, `no_basis` or `contract_not_live`. |  |
| `levels[].error.message` (string) | What happened, in words. |  |
| `levels[].error.live` (array) | With `contract_not_live`: the contracts measured that day. |  |

## Errors

| Status | Code | What to do |
| --- | --- | --- |
| 400 | `missing_sym`, `too_many_symbols`, `bad_book` | A parameter is wrong. Pass `sym`, one symbol or a comma list. |
| 401 | `no_key`, `bad_key` | No key, or an unknown or revoked one. Send `Authorization: Bearer <key>` with a key of the product's scope. |
| 403 | `live_required` | The account holds no LIVE plan. Start or renew LIVE; `pricing` names the page. |
| 429 | `rate_limited`, `too_many_places` | Over the minute's limit, or the key is in use from a fourth address this hour. Wait `Retry-After` seconds; poll with `If-None-Match`, a 304 is free. |

## Caching

Answers carry an `ETag`: send it back as `If-None-Match` and an unchanged reading answers 304 with no body, not counted against the limit.

## Request

```sh
curl \
  -H "Authorization: Bearer $FIRMTAPE_LIVE_KEY" \
  "https://firmtape.com/api/v1/levels?sym=SPX,ES,QQQ"
```

```python
import os, requests

H = {"Authorization": f"Bearer {os.environ['FIRMTAPE_LIVE_KEY']}"}
r = requests.get("https://firmtape.com/api/v1/levels?sym=SPX,ES,QQQ", headers=H)
print(r.status_code, r.text[:500])
```

```js
const r = await fetch("https://firmtape.com/api/v1/levels?sym=SPX,ES,QQQ", {
  method: "GET",
  headers: { Authorization: `Bearer ${process.env.FIRMTAPE_LIVE_KEY}` },
});
console.log(r.status, await r.text());
```

## Example response

```json
{
  "book": "volume",
  "next_update_s": 3,
  "levels": [
    {
      "sym": "ES",
      "name": "E-mini S&P 500",
      "class": "future",
      "contract": "ESZ26",
      "session": "open",
      "day": "2026-09-30",
      "book": "volume",
      "price": 6771.5,
      "price_as_of": "2026-09-30T10:15:03-04:00",
      "flip": 6752.25,
      "call_wall": 6800,
      "put_wall": 6700,
      "net_gamma": {
        "value": -812.4,
        "sign": "negative",
        "unit": "usd_m_per_point"
      },
      "at_flip": false,
      "dealer_gamma": "short",
      "gamma_pctl": -4,
      "as_of": "2026-09-30T10:15:03-04:00",
      "as_of_unix": 1790777703,
      "age_s": 1,
      "cadence": {
        "book": "1s",
        "price": "1s"
      },
      "grade": null,
      "oi": {
        "flip": 6731.4,
        "call_wall": 6825,
        "put_wall": 6700,
        "net_gamma": {
          "value": 1234.5,
          "sign": "positive",
          "unit": "usd_m_per_point"
        }
      },
      "basis": {
        "value": 59.5,
        "index": "SPX",
        "passes": 4,
        "rule": "running median of the day's basis passes up to that time, rounded to 0.25"
      },
      "source": "SPX volume book moved by the ES basis",
      "next_update_s": 3
    }
  ]
}
```

## Changes

- 2026-10-11: A finished day answers with its closing levels, read at 15:45 ET; `levels_at` and `last_second` added.
- 2026-10-11: RTY and M2K.
- 2026-09-30: Added.
