Appearance
WebSocket
The indexer serves a WebSocket for real-time market and account data. Values follow the REST conventions: integers, serialized as decimal strings above 2^53. All data is on-chain and public; no authentication is required. A machine-readable AsyncAPI spec is available.
Connecting
Connect to the indexer WebSocket endpoint (base URL is deployment-specific), then send subscribe messages. The server replies with a confirmation and then streams messages for each subscription.
Client messages
method | Fields | Purpose |
|---|---|---|
subscribe | subscription (see below) | Start a stream |
unsubscribe | subscription_id | Stop a stream |
ping | timestamp (uint64, ms) | Liveness / latency |
The subscription object selects a channel and its scope:
| Field | Type | Applies to | Description |
|---|---|---|---|
channel | string | all | One of the channels below. |
market_id | integer | market channels | Market to stream. |
is_perps | boolean | market channels | true for perp markets, false for spot. |
snapshot | boolean | orderbook | Send a full snapshot before deltas. |
interval | string | candle channels | Candle resolution: 1m, 5m, 15m, 1h, 4h, 1d. |
vault_id | integer | vaults | Vault to stream; omit for all vaults. |
address | string (0x-hex) | account channels | Account to stream. |
subaccount_id | integer (uint16) | account channels | Subaccount to stream. |
json
{ "method": "subscribe",
"subscription": { "channel": "orderbook", "market_id": 1, "is_perps": true, "snapshot": true } }json
{ "method": "subscribe",
"subscription": { "channel": "orders", "address": "0x9b08…3e25", "subaccount_id": 0 } }Channels
Public (market):
| Channel | Scope | Payload |
|---|---|---|
orderbook | market_id | Depth snapshot then deltas. |
trades | market_id | Trade prints. |
funding | market_id | Funding-rate updates. |
mark_price | market_id | Mark / oracle price updates. |
candles | market_id + interval | OHLCV candle updates. |
mark_price_candles | market_id + interval | Mark-price candle updates. |
market_stats | market_id, optional | 24h stats, open interest, funding. Omit market_id to stream all markets. |
all_mids | none | Mark prices for every market in one stream. |
bbo | market_id | Best bid/offer; sent only when the top of book changes. |
vaults | vault_id, optional | Vault deposits, withdrawals, and share totals. Omit vault_id for all vaults. |
blp | none | BLP deposits, withdrawals, status, and share totals. |
Account (scoped by address + subaccount_id):
| Channel | Payload |
|---|---|
orders | Order placements, updates, cancellations. |
fills | Fills for the subaccount. |
positions | Position updates. |
balances | Balance updates. |
liquidations | Liquidation and auto-deleveraging events affecting the subaccount. |
funding_payments | Funding payments on the subaccount's positions. |
subaccount | Subaccount snapshot on every change: margin balances and status. |
Server messages
Every message has a type. Control types are subscribed, unsubscribed, pong, and error; the rest match the channel names.
orderbook: bids/asks are [price_ticks, size_base_lots] pairs (strings). snapshot: true marks a full-book message; subsequent messages are deltas (a 0 size removes the level).
json
{ "type": "orderbook", "market_id": 1, "is_perps": true, "snapshot": true,
"bids": [["100000", "5"], ["99900", "12"]],
"asks": [["100100", "3"]],
"sequence": "42", "timestamp_ms": 1717000000000 }trades
json
{ "type": "trades", "market_id": 1, "is_perps": true,
"trades": [ { "price": "100000", "quantity": "5", "taker_is_bid": true,
"timestamp_ms": 1717000000000, "seq": "9876" } ] }funding: funding_rate is in 100ths of a basis point.
json
{ "type": "funding", "market_id": 1, "funding_rate": 50,
"cumulative_funding_index_long": "12345", "cumulative_funding_index_short": "-12300",
"timestamp_ms": 1717000000000 }mark_price
json
{ "type": "mark_price", "market_id": 1, "mark_price": "100050",
"oracle_price": "100040", "timestamp_ms": 1717000000000 }candles / mark_price_candles: one message per candle update; candle mirrors the REST candle resource.
json
{ "type": "candles", "market_id": 1, "interval": "1m",
"candle": { "open_time_ms": 1717000000000, "open": "100000", "high": "100100",
"low": "99900", "close": "100050", "volume_base": "120", "trades": 42 } }market_stats: mirrors the REST market-stats resource.
json
{ "type": "market_stats", "market_id": 1,
"stats": { "last_price": "100050", "volume_24h_quote": "1200000", "open_interest": "5400" },
"timestamp_ms": 1717000000000 }all_mids: mark price per market, keyed by market id.
json
{ "type": "all_mids", "mids": { "1": "100050", "2": "64230" },
"timestamp_ms": 1717000000000 }bbo: sent only when the top of book changes; bid/ask are [price_ticks, size_base_lots].
json
{ "type": "bbo", "market_id": 1, "is_perps": true,
"bid": ["100000", "5"], "ask": ["100100", "3"],
"sequence": "43", "timestamp_ms": 1717000000000 }vaults / blp: one message per operation, with updated share totals.
json
{ "type": "vaults", "vault_id": 3,
"operation": { "kind": "deposit", "address": "0x9b08…3e25",
"amount": "250000", "shares": "1200" },
"total_shares": "51200", "timestamp_ms": 1717000000000 }Account channels (orders, fills, positions, balances, liquidations, funding_payments, subaccount): the payload mirrors the corresponding REST resource (see Accounts, Positions & Orders and Funding & Liquidations), wrapped with the owner:
json
{ "type": "positions", "address": "0x9b08…3e25", "subaccount_id": 0,
"position": { "market_id": 1, "side": "long", "size": "5",
"entry_price": "100000", "margin_mode": "cross" } }subaccount: a snapshot of the subaccount's indexed state, sent on every event that changes it (deposits, withdrawals, transfers, margin-mode changes, isolated-margin adjustments, fills, liquidations). Computed values such as equity and margin requirements are served by REST.
json
{ "type": "subaccount", "address": "0x9b08…3e25", "subaccount_id": 0,
"subaccount": { "cross_margin_balance": "1250000",
"total_isolated_margin": "50000", "status": 0 },
"timestamp_ms": 1717000000000 }Control
json
{ "type": "subscribed", "subscription_id": "sub_1", "channel": "orderbook", "market_id": 1 }
{ "type": "pong", "timestamp": 1717000000000 }
{ "type": "error", "error": "unknown channel" }Operational guidance
Scoping. A subscription only receives messages whose dimensions equal every field it sets. Market channels (orderbook, trades, bbo, candles, market_stats) are scoped by market_id + is_perps; mark_price, funding, and all_mids are perps-only and take market_id alone — setting is_perps on them matches nothing.
Snapshots. Subscribe to orderbook with "snapshot": true to receive the current aggregated book before deltas. Deltas carry absolute per-level sizes (a size of "0" clears the level), plus a monotonic sequence.
Lag handling. Delivery is real-time only — there is no replay. If a connection cannot keep up and the server's per-connection buffer overflows, the server sends one final frame and closes the connection:
json
{ "type": "error", "error": "connection lagged; 42 messages dropped — reconnect and re-subscribe" }Dropped deltas would permanently desync a book, so the close is deliberate. Production consumers should run a reconnect loop: on close, reconnect, re-subscribe (with snapshot: true for orderbook channels), and resume.
Keepalive. Send { "method": "ping", "timestamp": … } periodically; the server echoes a pong with the same timestamp. Use it for liveness and round-trip measurement.
Timestamps. All streamed timestamp_ms values derive from the transaction's sequencer time, so every fill in one transaction shares a timestamp.