Skip to content
Alchemy Logo

l2BookDiff stream

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"] }
}

ParameterTypeRequiredSourceDescription
coinsstring[]NoAlchemy extensionMarkets 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.
marketTypesstring[]NoAlchemy extensionRestricts delivery by market type. Accepted values: perp, spot, outcome, or *. Defaults to ["perp"]. Rejected if combined with coins.
nSigFigsnumberNoFoundation nativeAggregate price levels to N significant figures. Accepted values: 2, 3, 4, 5.
mantissanumberNoFoundation nativeSnap prices to a mantissa step. Accepted values: 2 or 5. Valid only when nSigFigs is 5.
nLevelsnumberNoAlchemy extensionLevels 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 }], []]
      }
    ]
  }
}

FieldTypeDescription
heightintegerBlock height.
timeintegerBlock timestamp in milliseconds.
isSnapshotbooleantrue when the message contains a complete replacement snapshot; absent or false for an incremental update.
diffsarrayOne entry per market that changed in this block.
diffs[].coinstringMarket symbol.
diffs[].seqintegerPer-market sequence number, incrementing by one per diff for that market.
diffs[].prev_seqintegerThe preceding per-market sequence number, for gap detection.
diffs[].levelsarrayTwo-element tuple of [bids, asks], containing changed levels only.
diffs[].levels[][].pxstringPrice, as a decimal string.
diffs[].levels[][].szstringNew total size at this price level. "0" means the level was removed.
diffs[].levels[][].nintegerNumber of orders at this level. 0 when the level was removed.

  • A level with sz of "0" has been removed. Delete that price from your book.
  • Any other level is an upsert: set that price to the given sz and n.
  • 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, and nLevels. 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.

l2Bookl2BookDiffbbol4BookUpdates
DeliversComplete aggregated snapshotChanged aggregated levelsBest bid and ask onlyChanged individual orders
Client stateNone requiredMaintains a local bookNone requiredMaintains a local book
DetailAggregated levelsAggregated levelsTop of bookIndividual orders, with queue position
Use caseDisplays, periodic readsEfficient live bookPrice and spread trackingMarket making, queue analysis

{
  "method": "unsubscribe",
  "subscription": { "type": "l2BookDiff", "coins": ["BTC", "ETH"] }
}

  • Persist cursor and each market's seq after applying a message.
  • On reconnect, supply cursor in the subscription object.
  • Check prev_seq against your position for that market on every diff.
  • A message with isSnapshot: true replaces 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: true is pushed to you. isSnapshot is the only continuity signal you need to handle — receiving it means discard local state and adopt the supplied snapshot.
Was this page helpful?