Developers menu
Developers / DATASET / STREAM: the session, every second
Markdown copySTREAM: 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.
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 line | What it is |
|---|---|
id: | the second, ET with its offset (2026-10-23T15:59:59-04:00). Keep the last one you took. |
event: second | always 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.
| Answer | Means |
|---|---|
200 | the stream |
401 | no key, a key we do not know, a revoked licence or one past its last day |
400 | a Last-Event-ID that is not a second's id |
429 | the 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.
| Field | Book | What it is |
|---|---|---|
v | 1, the format | |
t, day | the second (the event's id) and its ET session date | |
spot | none | SPX solved from put-call parity on the chain, that second |
volume.flip, .call_wall, .put_wall, .net_gamma | volume | the 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_gamma | open interest | the 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, .vanna | measured | the 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_near | volume | contracts printed in that second: 0DTE in all, calls, puts; _near the next three expiries |
prints.call_delta, .put_delta, .call_delta_near, .put_delta_near | volume | the same prints weighted by each option's absolute delta |
ladder.minute, ladder.strikes | the 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 book | per 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.puts | none | the 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.