GET /v1/options
A coin's whole Deribit option chain as recorded once a minute, with each option's recorded taker flow, the newest or the newest at a given time, as one binary snapshot.
Returns one snapshot of a coin’s option chain: every listed option with its expiry, strike, open interest, mark volatility, underlying price and recorded flow (net taker buying), as the recorder took it from Deribit. Flowscope computes gamma exposure from it.
The recorder takes a snapshot of each coin in --options-coins (BTC and ETH by default) once a minute; see Storage.
Request
GET /v1/options/<COIN>[?at=<ms>]
GET /v1/options/<COIN>?from=<ms>&to=<ms>&step=<ms>
HEAD /v1/options/<COIN>[?at=<ms>]| Parameter | In | Type | Description |
|---|---|---|---|
COIN | Path | string | BTC or ETH, as configured in --options-coins; case-sensitive |
at | Query | integer, ms | Optional. The newest snapshot taken at or before this time. Without it, or if it does not parse, the newest snapshot. |
With at, the snapshot is looked for in the UTC day of at and the day before. An at at or after the newest snapshot answers the newest.
Over time
With from, to and step (all milliseconds) the answer is the chains over [from, to): the first snapshot of every step on the UTC grid, oldest first, at most 300 of them. to - from may be a day at most and step a minute or more; otherwise 400. The body is a series of records, each length u32 (little-endian) followed by that many bytes of one snapshot in the format below. A range with nothing recorded answers 200 with an empty body. Flowscope’s Gamma Exposure study draws its exposure over time from these.
Response
| Header | Value |
|---|---|
Content-Type | application/octet-stream |
Cache-Control | no-cache |
The body is one snapshot in the format below. Its time (ts) says when it was taken; the recorder keeps answering the last one it has if Deribit does not answer, so check ts if you need a fresh chain.
A BTC snapshot of about 950 options is about 10 to 12 kB.
Snapshot format
Little-endian, no padding. A 16-byte header, a raw DEFLATE stream holding the chain, and a checksum:
"FSO2" | ts i64 | raw length u32 | deflate(index f64 | n u32 |
n × (expiry i64, strike f64, put u8, open interest f32, mark iv f32, underlying f64,
flow f32)) | fnv1a-32 u32The reference implementation is flowscope_engine::options (encode, decode).
Header
| Offset | Size | Type | Field | Meaning |
|---|---|---|---|---|
| 0 | 4 | bytes | magic | ASCII FSO2; the format’s version is part of it. Snapshots recorded before October 2026 are FSO1, see Version 1 |
| 4 | 8 | i64 | ts | When the snapshot was taken, ms since the epoch |
| 12 | 4 | u32 | raw length | Length of the chain after decompression: 12 + 37 × n |
| 16 | total − 20 | bytes | chain | The chain, raw DEFLATE (RFC 1951, no zlib or gzip wrapper) |
Chain
What the DEFLATE stream decompresses to:
| Offset | Size | Type | Field | Meaning |
|---|---|---|---|---|
| 0 | 8 | f64 | index | The coin’s index price when the snapshot was taken (Deribit’s estimated delivery price); 0 if Deribit gave none |
| 8 | 4 | u32 | n | Number of options |
| 12 | 37 × n | options | One record per option, below |
Option record
37 bytes each, in the order Deribit listed them.
| Offset | Size | Type | Field | Meaning |
|---|---|---|---|---|
| 0 | 8 | i64 | expiry | Expiry, ms since the epoch: 08:00 UTC of the day in the option’s name |
| 8 | 8 | f64 | strike | Strike price, USD |
| 16 | 1 | u8 | put | 0 for a call, 1 for a put; readers treat any non-zero value as a put |
| 17 | 4 | f32 | open interest | Open interest in the coin (one Deribit BTC or ETH option is one coin) |
| 21 | 4 | f32 | mark iv | Mark implied volatility, percent a year (41.25 is 41.25 %) |
| 25 | 8 | f64 | underlying | Price of what the option settles on: the future of its expiry |
| 33 | 4 | f32 | flow | Net taker buying recorded, in the coin: the amount of every trade of this option the recorder saw where the taker bought, minus every one where the taker sold, from when recording began. 0 where no trade was seen |
Options without a mark volatility or an underlying price in Deribit’s answer are left out. Options with no open interest are kept, with 0.
The recorder follows Deribit’s option trades (get_last_trades_by_currency, kind=option) once a minute, just before it takes the chain, and adds each trade’s amount to its option’s flow with the sign of the taker’s direction. Block trades count too. The flow carries on across snapshots and is dropped when the option expires. After a restart the recorder goes on from the flow in the newest snapshot on disk (from the UTC day of the restart or the day before) and asks for the trades since then; without one it starts at 0. Trades between the last trade request and a crash can be missed.
Trailer
| Size | Type | Field |
|---|---|---|
| 4 | u32 | FNV-1a 32 of every byte of the snapshot before the trailer, from the magic on (the compressed chain, not the decompressed one) |
Reading rules
A reader rejects a snapshot that:
- is shorter than 20 bytes or does not start with
FSO2orFSO1; - fails the checksum;
- has a raw length below 12 or above 16 MiB;
- does not decompress, or decompresses to anything but
12 + 37 × nbytes (12 + 33 × nforFSO1).
Version 1
FSO1 snapshots, recorded before the flow was, are the same with 33-byte option records: everything up to underlying, no flow. Readers take their flow as 0. Day files may hold both versions; each record carries its own magic.
The name of each option on Deribit is <COIN>-<DMMMYY>-<STRIKE>-<C|P>, for example BTC-27NOV26-73000-P: a put at 73 000 expiring on 27 November 2026 at 08:00 UTC. The record carries the parts, not the name.
Status codes
| Status | Body | When |
|---|---|---|
200 | The snapshot | A snapshot was found |
404 | options of <COIN> are not recorded | The coin is not in --options-coins, or has no options (only BTC and ETH do) |
404 | no snapshot yet | The coin is recorded but has no snapshot: none taken since the recorder started (the first comes within a minute), or none at or before at |
404 | option chains not recorded | --options-coins is empty. Sent with Cache-Control: no-store. |
404 | not found | The path names no coin (/v1/options/) or has more after it |
400 | from < to, a day at most, step a minute or more | An over time request outside those bounds |
Example
The newest BTC chain, and the one at a given time:
curl -fsS -o btc.opt http://127.0.0.1:8787/v1/options/BTC
curl -fsS -o btc-then.opt "http://127.0.0.1:8787/v1/options/BTC?at=1791000000000"
xxd btc.opt | head -100000000: 4653 4f32 0036 ebff a001 0000 e887 0000 FSO2.6..........The magic FSO2, the time (0x01a0ffeb3600 = 1 791 000 000 000 ms), then the raw length (0x87e8 = 34 792 bytes: 12 + 37 × 940, so 940 options). The values after the magic are illustrative; the compressed chain follows from offset 16.
Decoding in Rust
use flowscope_engine::options::{Expiries, Positioning, decode, gex};
let bytes = std::fs::read("btc.opt")?;
let s = decode(&bytes)?;
println!("{} options at {}, index {}", s.quotes.len(), s.ts, s.index);
let g = gex(&s, s.ts, Expiries::All, Positioning::Flow);
println!("call wall {:?} put wall {:?} flip {:?} net ${:.0} per 1 %", g.call_wall, g.put_wall, g.flip, g.total);Decoding in Python
import struct, zlib
b = open("btc.opt", "rb").read()
assert b[:4] in (b"FSO2", b"FSO1")
body, (fnv,) = b[:-4], struct.unpack("<I", b[-4:])
h = 0x811C9DC5
for x in body:
h = ((h ^ x) * 0x01000193) & 0xFFFFFFFF
assert h == fnv, "checksum"
ts, raw_len = struct.unpack_from("<qI", b, 4)
raw = zlib.decompress(body[16:], -15) # raw DEFLATE
assert len(raw) == raw_len
index, n = struct.unpack_from("<dI", raw, 0)
row = 37 if b[:4] == b"FSO2" else 33
for i in range(n):
expiry, strike, put, oi, iv, und = struct.unpack_from("<qdBffd", raw, 12 + row * i)
flow = struct.unpack_from("<f", raw, 12 + row * i + 33)[0] if row == 37 else 0.0How Flowscope clients use it
A client asks its data hub for /v1/options/<COIN> once a minute while a chart shows the Gamma Exposure study, for the coin of the chart’s market. After a failed request it waits a minute longer for each failure in a row, ten minutes at most. It does not use at.
Related
- Gamma exposure describes what the app computes from the snapshots.
- Recorder: storage describes the
.optday files the snapshots are kept in.