Skip to content

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

methodFieldsPurpose
subscribesubscription (see below)Start a stream
unsubscribesubscription_idStop a stream
pingtimestamp (uint64, ms)Liveness / latency

The subscription object selects a channel and its scope:

FieldTypeApplies toDescription
channelstringallOne of the channels below.
market_idintegermarket channelsMarket to stream.
is_perpsbooleanmarket channelstrue for perp markets, false for spot.
snapshotbooleanorderbookSend a full snapshot before deltas.
intervalstringcandle channelsCandle resolution: 1m, 5m, 15m, 1h, 4h, 1d.
vault_idintegervaultsVault to stream; omit for all vaults.
addressstring (0x-hex)account channelsAccount to stream.
subaccount_idinteger (uint16)account channelsSubaccount 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):

ChannelScopePayload
orderbookmarket_idDepth snapshot then deltas.
tradesmarket_idTrade prints.
fundingmarket_idFunding-rate updates.
mark_pricemarket_idMark / oracle price updates.
candlesmarket_id + intervalOHLCV candle updates.
mark_price_candlesmarket_id + intervalMark-price candle updates.
market_statsmarket_id, optional24h stats, open interest, funding. Omit market_id to stream all markets.
all_midsnoneMark prices for every market in one stream.
bbomarket_idBest bid/offer; sent only when the top of book changes.
vaultsvault_id, optionalVault deposits, withdrawals, and share totals. Omit vault_id for all vaults.
blpnoneBLP deposits, withdrawals, status, and share totals.

Account (scoped by address + subaccount_id):

ChannelPayload
ordersOrder placements, updates, cancellations.
fillsFills for the subaccount.
positionsPosition updates.
balancesBalance updates.
liquidationsLiquidation and auto-deleveraging events affecting the subaccount.
funding_paymentsFunding payments on the subaccount's positions.
subaccountSubaccount 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.