Skip to main content

Depth

Channel Name

spot:depth:{symbol}

Authentication: not required (public channel).

Description

Provides a full order-book snapshot on subscribe, followed by incremental diffs on every book mutation (order placement, cancellation, or fill). Only changed price levels are included in each diff — unaffected levels are never repeated.

Subscribe

Send:

{ "type": "subscribe", "channel": "spot:depth:DFUSDT" }

The server responds with a subscribed ack and then immediately sends the initial snapshot:

{ "type": "subscribed", "channel": "spot:depth:DFUSDT" }

Followed by:

{
"type": "spot_depth_snapshot",
"channel": "spot:depth:DFUSDT",
"data": {
"symbol": "DFUSDT",
"last_update_id": 12345,
"bids": [
["0.5000", "100"],
["0.4999", "200"],
["0.4998", "500"]
],
"asks": [
["0.5001", "150"],
["0.5002", "180"],
["0.5003", "300"]
]
}
}

Push Format

spot_depth_snapshot

Sent once, immediately after the subscribed ack. Contains up to 1 000 levels on each side.

{
"type": "spot_depth_snapshot",
"channel": "spot:depth:DFUSDT",
"data": {
"symbol": "DFUSDT",
"last_update_id": 12345,
"bids": [["0.5000", "100"], ["0.4999", "200"]],
"asks": [["0.5001", "150"], ["0.5002", "180"]]
}
}
FieldTypeDescription
symbolstringMarket identifier, e.g. DFUSDT.
last_update_idintegerMonotonic engine counter at snapshot time. Use this to align incoming diffs — see Sequencing.
bids[price, qty][]All resting bid levels, best price first. Both values are strings.
asks[price, qty][]All resting ask levels, best price first. Both values are strings.

spot_depth_diff

Pushed on every book mutation. Only includes changed price levels.

{
"type": "spot_depth_diff",
"channel": "spot:depth:DFUSDT",
"data": {
"symbol": "DFUSDT",
"update_id_first": 12346,
"update_id_last": 12346,
"bids": [["0.5000", "70"]],
"asks": []
}
}
FieldTypeDescription
symbolstringMarket identifier.
update_id_firstintegerFirst engine sequence number covered by this diff.
update_id_lastintegerLast engine sequence number covered by this diff.
bids[price, qty][]Changed bid levels. qty == "0" means the level is now empty — remove it from your local book.
asks[price, qty][]Changed ask levels. Same removal rule applies.

Example: a fill consumes one ask level and partially reduces another.

{
"type": "spot_depth_diff",
"channel": "spot:depth:DFUSDT",
"data": {
"symbol": "DFUSDT",
"update_id_first": 12347,
"update_id_last": 12347,
"bids": [],
"asks": [
["0.5001", "0"],
["0.5002", "80"]
]
}
}

"0.5001" is removed (qty = "0"); "0.5002" is updated to 80.

Sequencing

  1. Subscribe → receive spot_depth_snapshot with last_update_id = N.
  2. Start buffering incoming spot_depth_diff frames.
  3. Discard any diff where update_id_last <= N.
  4. Apply the first diff where update_id_first == N + 1 (or update_id_first <= N + 1 <= update_id_last).
  5. Apply all subsequent diffs in arrival order.
  6. If update_id_first of an incoming diff is greater than last_update_id_applied + 1, frames were dropped — re-subscribe to get a fresh snapshot.

For more detail see Snapshot ↔ Live Sequencing.

Update Cadence

A spot_depth_diff is pushed on every engine event that mutates the book:

  • Limit order placement (adds a level or increases qty at an existing level).
  • Order cancellation (reduces or removes a level).
  • Fill (reduces the resting side; may remove the level entirely).

There is no fixed timer — updates are event-driven and arrive within single-digit milliseconds of the engine event.

Code Example

const ws = new WebSocket('wss://api-sepolia.p99.world/ws');

let localBook = { bids: new Map(), asks: new Map() };
let snapshotId = null;
let pendingDiffs = [];

ws.onopen = () => {
ws.send(JSON.stringify({ type: 'subscribe', channel: 'spot:depth:DFUSDT' }));
setInterval(() => ws.send(JSON.stringify({ type: 'ping' })), 30000);
};

ws.onmessage = (event) => {
const msg = JSON.parse(event.data);

if (msg.type === 'spot_depth_snapshot') {
// Seed local book
localBook.bids = new Map(msg.data.bids.map(([p, q]) => [p, q]));
localBook.asks = new Map(msg.data.asks.map(([p, q]) => [p, q]));
snapshotId = msg.data.last_update_id;

// Apply any buffered diffs
for (const diff of pendingDiffs) applyDiff(diff);
pendingDiffs = [];
}

if (msg.type === 'spot_depth_diff') {
if (snapshotId === null) {
pendingDiffs.push(msg.data);
return;
}
if (msg.data.update_id_last <= snapshotId) return; // stale, discard
applyDiff(msg.data);
}
};

function applyDiff(diff) {
for (const [price, qty] of diff.bids) {
if (qty === '0') localBook.bids.delete(price);
else localBook.bids.set(price, qty);
}
for (const [price, qty] of diff.asks) {
if (qty === '0') localBook.asks.delete(price);
else localBook.asks.set(price, qty);
}
snapshotId = diff.update_id_last;
}