Developers menu

Developers / DATASET / STREAM: the session, every second

Markdown copy

STREAM: the session, every second

The whole terminal stream for a licensee's server: every second of the SPX session, the levels of the three books and the book by strike, as Server-Sent Events. Real time on STREAM and STREAM 250, fifteen minutes late on STREAM, 15 MIN. One licence key opens it and the levels API.

https://stream.firmtape.com/v1/stream
Plan Redistribution licencesKey scope lic

This page serves

Plan: Redistribution licences. Show FirmTape's levels or its stream to other people in your app, site, bot or channel, on printed terms Pricing.

GET https://stream.firmtape.com/v1/stream with Authorization: Bearer lic_..., the licence key issued with the countersigned licence. The answer is text/event-stream and never ends: one second event per second of the session (09:30:00 to 15:59:59 ET), a comment line (: ping) every 15 seconds between and outside sessions. Ask for gzip (Accept-Encoding: gzip); every common HTTP client does.

Your server connects, not your people's browsers. The key is a server's credential: it holds as many connections at once as the licence says (two unless agreed), and your server shows the numbers to the people the licence covers.

Event lineWhat it is
id:the second, ET with its offset (2026-10-23T15:59:59-04:00). Keep the last one you took.
event: secondalways this name, on both the real-time and the delayed stream.
data:one JSON object on one line, below.
retry:once, on connect: how long to wait before reconnecting, in ms (1 to 5 s, spread on purpose).

Reconnecting loses nothing. Send the last id you took as Last-Event-ID (an EventSource client does it for you) and the stream first sends every second after it, from the last thirty minutes, then the live ones, each once and in order. Without it you start at the newest second. A deploy on our side closes the connection; reconnect and resume.

AnswerMeans
200the stream
401no key, a key we do not know, a revoked licence or one past its last day
400a Last-Event-ID that is not a second's id
429the licence's connections are all open; close one (a connection you dropped is freed within seconds)

The second. Everything is FirmTape's own measurement of SPX options; units and books are the data dictionary's. null means not measured at that second, never zero. A field is added to v1, never renamed or removed.

FieldBookWhat it is
v1, the format
t, daythe second (the event's id) and its ET session date
spotnoneSPX solved from put-call parity on the chain, that second
volume.flip, .call_wall, .put_wall, .net_gammavolumethe levels the terminal draws by default: the zero-gamma flip, the peak positive and negative gamma strikes within 2.5% of spot, and the net gamma at spot ($M per index point), 0DTE
open_interest.flip, .call_wall, .put_wall, .net_gammaopen interestthe same on the morning open interest; call_wall_all and put_wall_all over the near expiries
measured.flip, .flip_se, .call_wall, .put_wall, .net_gamma, .charm, .vannameasuredthe dealer book signed print by print: its flip (smoothed, minute cadence) and the flip's standard error, walls, net gamma, charm ($Bn of delta per day) and vanna ($M of delta per vol point)
measured.max_change.<1|5|10|15|30>measured{ strike, change }: the strike whose gamma moved most over that many minutes, and by how much
prints.contracts, .calls, .puts, .calls_near, .puts_nearvolumecontracts printed in that second: 0DTE in all, calls, puts; _near the next three expiries
prints.call_delta, .put_delta, .call_delta_near, .put_delta_nearvolumethe same prints weighted by each option's absolute delta
ladder.minute, ladder.strikesthe book by strike at that second: its minute and the strikes within 2.5% of spot. ladder is null on a second sent again from the backlog after a gap of yours
ladder.volume.*, ladder.open_interest.*, ladder.measured.*per bookper strike, aligned to strikes: gamma, vanna, charm in every book; call_contracts, put_contracts (volume and open interest); gross_gamma (open interest, unsigned); delta, vega (measured). Each book's zero_dte holds the same for today's expiry alone
ladder.iv.calls, ladder.iv.putsnonethe 0DTE smile per strike, vol points, calendar time
{
 "v": 1,
 "t": "2026-10-23T15:59:59-04:00",
 "day": "2026-10-23",
 "spot": 7623.32,
 "volume": {
  "flip": 7631.54,
  "call_wall": 7610,
  "put_wall": 7625,
  "net_gamma": -14119.87
 },
 "open_interest": {
  "flip": 7695.82,
  "call_wall": 7605,
  "put_wall": 7625,
  "call_wall_all": 7605,
  "put_wall_all": 7625,
  "net_gamma": -731.72
 },
 "measured": {
  "flip": 7705.77,
  "flip_se": 1.69,
  "call_wall": 7625,
  "put_wall": 7630,
  "net_gamma": 8.88,
  "charm": -147.25,
  "vanna": -95.72,
  "max_change": {
   "1": {
    "strike": 7625,
    "change": 21.64
   },
   "5": {
    "strike": 7625,
    "change": 63.84
   },
   "10": null,
   "15": null,
   "30": null
  }
 },
 "prints": {
  "contracts": 86,
  "calls": 43,
  "puts": 27,
  "calls_near": 4,
  "puts_near": 12,
  "call_delta": 9.6,
  "put_delta": 19.53,
  "call_delta_near": 0.34,
  "put_delta_near": 5.23
 },
 "ladder": {
  "minute": "15:59",
  "strikes": [
   7620,
   7625
  ],
  "volume": {
   "gamma": [
    1.2,
    -2.4
   ],
   "vanna": [
    0.1,
    -0.3
   ],
   "charm": [
    0.01,
    -0.02
   ],
   "call_contracts": [
    1200,
    800
   ],
   "put_contracts": [
    900,
    2100
   ],
   "zero_dte": {
    "gamma": [
     0.9,
     -2.1
    ],
    "vanna": [
     0.1,
     -0.2
    ],
    "charm": [
     0.01,
     -0.02
    ],
    "call_contracts": [
     1100,
     700
    ],
    "put_contracts": [
     850,
     2000
    ]
   }
  },
  "open_interest": {
   "gamma": [
    3.1,
    -1.2
   ],
   "vanna": [
    0.4,
    -0.1
   ],
   "charm": [
    0.03,
    -0.01
   ],
   "gross_gamma": [
    5.2,
    4.8
   ],
   "call_contracts": [
    4100,
    3900
   ],
   "put_contracts": [
    5200,
    6100
   ],
   "zero_dte": {
    "gamma": [
     1.1,
     -0.9
    ],
    "vanna": [
     0.1,
     -0.1
    ],
    "charm": [
     0.01,
     -0.01
    ],
    "gross_gamma": [
     2.0,
     1.9
    ],
    "call_contracts": [
     1500,
     1300
    ],
    "put_contracts": [
     1800,
     2200
    ]
   }
  },
  "measured": {
   "gamma": [
    0.4,
    -0.6
   ],
   "vanna": [
    0.02,
    -0.03
   ],
   "charm": [
    0.001,
    -0.002
   ],
   "delta": [
    12.5,
    -8.1
   ],
   "vega": [
    0.21,
    -0.18
   ],
   "zero_dte": {
    "gamma": [
     0.3,
     -0.5
    ],
    "vanna": [
     0.02,
     -0.02
    ],
    "charm": [
     0.001,
     -0.001
    ],
    "delta": [
     10.2,
     -7.4
    ],
    "vega": [
     0.15,
     -0.12
    ]
   }
  },
  "iv": {
   "calls": [
    12.5,
    11.9
   ],
   "puts": [
    13.1,
    12.8
   ]
  }
 }
}

Not in it, by design: the vendor's raw quotes and trades, and the tickers' reference prices. The stream is SPX; the tickers' levels are on the levels API with the same key.

import json, requests

KEY = "lic_..."
last = None
while True:
    h = {"Authorization": f"Bearer {KEY}", "Accept": "text/event-stream"}
    if last:
        h["Last-Event-ID"] = last
    try:
        with requests.get("https://stream.firmtape.com/v1/stream", headers=h, stream=True, timeout=(10, 60)) as r:
            r.raise_for_status()
            for line in r.iter_lines(decode_unicode=True):
                if line.startswith("id: "):
                    last = line[4:]
                elif line.startswith("data: "):
                    s = json.loads(line[6:])
                    print(s["t"], s["spot"], s["volume"]["flip"])
    except requests.RequestException as e:
        print("reconnecting:", e)

Measurements of dealer positioning, never a forecast or a trade call. This page is built from FirmTape's public contract, the same files the OpenAPI document is built from; the tests hold them against the route code. Markdown: /docs/developers/dataset/stream.md.