This API is in private preview. Target September 2026; timing is subject to change.
l2BookDiff delivers incremental price-level changes so clients can maintain a
local order book without receiving a complete snapshot on every update. This is
ideal for latency-sensitive consumers needing continuous book state.
For the corresponding gRPC diff stream, see StreamL2BookDiff. l2Book provides standalone snapshots for displays; it is not a bootstrap source for a diff-maintained book.
{
"method": "subscribe",
"subscription": {
"type": "l2BookDiff",
"coins": ["BTC", "ETH"],
"nSigFigs": 5,
"mantissa": 2,
"nLevels": 20
}
}Use marketTypes instead of coins to subscribe to all markets of selected types:
{
"method": "subscribe",
"subscription": { "type": "l2BookDiff", "marketTypes": ["spot"] }
}| Parameter | Type | Required | Source | Description |
|---|---|---|---|---|
coins | string[] | No | Alchemy extension | Markets to subscribe to. Omit to subscribe to every market of the types in marketTypes, which defaults to perpetuals. The native API takes a single coin per subscription; the plural array is an Alchemy extension. |
marketTypes | string[] | No | Alchemy extension | Restricts delivery by market type. Accepted values: perp, spot, outcome, or *. Defaults to ["perp"]. Rejected if combined with coins. |
nSigFigs | number | No | Foundation native | Aggregate price levels to N significant figures. Accepted values: 2, 3, 4, 5. |
mantissa | number | No | Foundation native | Snap prices to a mantissa step. Accepted values: 2 or 5. Valid only when nSigFigs is 5. |
nLevels | number | No | Alchemy extension | Levels per side: 1, 10, 20 (default), or 50. |
The default never grows. New market types must be added to marketTypes explicitly, or pass ["*"] to opt in to future types automatically.
Aggregation rounds bids down and asks up. With nSigFigs: 5 and mantissa: 2, a bid of 70325 becomes 70324 and an ask of 70325 becomes 70326.
{
"channel": "subscriptionResponse",
"data": { "subscriptionId": "sub_01", "type": "l2BookDiff" }
}{
"channel": "l2BookDiff",
"subscriptionId": "sub_01",
"blockHeight": 123456,
"blockTime": 1780000000000,
"cursor": "<opaque cursor>",
"data": {
"height": 123456,
"time": 1780000000000,
"isSnapshot": false,
"diffs": [
{
"coin": "BTC",
"seq": 42,
"prev_seq": 41,
"levels": [[{ "px": "100000.0", "sz": "1.50", "n": 4 }], []]
}
]
}
}| Field | Type | Description |
|---|---|---|
height | integer | Block height. |
time | integer | Block timestamp in milliseconds. |
isSnapshot | boolean | true when the message contains a complete replacement snapshot; absent or false for an incremental update. |
diffs | array | One entry per market that changed in this block. |
diffs[].coin | string | Market symbol. |
diffs[].seq | integer | Per-market sequence number, incrementing by one per diff for that market. |
diffs[].prev_seq | integer | The preceding per-market sequence number, for gap detection. |
diffs[].levels | array | Two-element tuple of [bids, asks], containing changed levels only. |
diffs[].levels[][].px | string | Price, as a decimal string. |
diffs[].levels[][].sz | string | New total size at this price level. "0" means the level was removed. |
diffs[].levels[][].n | integer | Number of orders at this level. 0 when the level was removed. |
- A level with
szof"0"has been removed. Delete that price from your book. - Any other level is an upsert: set that price to the given
szandn. - Sizes are absolute — the level's new total, not a delta.
- Levels absent from a diff are unchanged.
- The snapshot and all subsequent diffs must use identical
nSigFigs,mantissa, andnLevels. Applying diffs from one aggregation setting to a snapshot taken at another produces an invalid book.
The first message for each subscribed market carries the current aggregated book with isSnapshot: true. Install it as local state. Normal subsequent messages are diffs; a later isSnapshot: true message replaces state for recovery.
Apply subsequent diffs in height order. For each market, the first diff must carry prev_seq equal to the snapshot's seq; continue comparing prev_seq to your stored position after every diff. A later message with isSnapshot: true uses the same replacement mechanism for recovery: discard local state for the affected market and adopt it.
For a snapshot shared by many consumers, or to let a slow client digest a large book while buffering live updates, use the REST snapshot read instead. l2Book carries no per-market sequence, so it is not a bootstrap source for a diff-maintained book.
| l2Book | l2BookDiff | bbo | l4BookUpdates | |
|---|---|---|---|---|
| Delivers | Complete aggregated snapshot | Changed aggregated levels | Best bid and ask only | Changed individual orders |
| Client state | None required | Maintains a local book | None required | Maintains a local book |
| Detail | Aggregated levels | Aggregated levels | Top of book | Individual orders, with queue position |
| Use case | Displays, periodic reads | Efficient live book | Price and spread tracking | Market making, queue analysis |
{
"method": "unsubscribe",
"subscription": { "type": "l2BookDiff", "coins": ["BTC", "ETH"] }
}- Persist
cursorand each market'sseqafter applying a message. - On reconnect, supply
cursorin the subscription object. - Check
prev_seqagainst your position for that market on every diff. - A message with
isSnapshot: truereplaces your local state. Any message without it is a diff that must chain onto your previous state. - If continuity is broken, a fresh message with
isSnapshot: trueis pushed to you.isSnapshotis the only continuity signal you need to handle — receiving it means discard local state and adopt the supplied snapshot.