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"]]
}
}
| Field | Type | Description |
|---|---|---|
symbol | string | Market identifier, e.g. DFUSDT. |
last_update_id | integer | Monotonic 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": []
}
}
| Field | Type | Description |
|---|---|---|
symbol | string | Market identifier. |
update_id_first | integer | First engine sequence number covered by this diff. |
update_id_last | integer | Last 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
- Subscribe → receive
spot_depth_snapshotwithlast_update_id = N. - Start buffering incoming
spot_depth_diffframes. - Discard any diff where
update_id_last <= N. - Apply the first diff where
update_id_first == N + 1(orupdate_id_first <= N + 1 <= update_id_last). - Apply all subsequent diffs in arrival order.
- If
update_id_firstof an incoming diff is greater thanlast_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;
}