Skip to content

Market channel

/ws/market streams the live book and tape. Use REST market data for the initial snapshot, then keep it current here. Market data is public: no Authenticate frame is required on this channel.

Six independent streams, each with its own subscribe verb and its own set of venues. A stream only accepts venues it can actually serve, and a subscribe it cannot honour is refused rather than accepted and left silently blank.

Stream Subscribe with Pushes Venues
Depth (aggregated levels) Subscribe-Depth Depth-Update hyperliquid, aster, lighter, polymarket, kalshi, massive, b3, t4
Order book (per order, L3) Subscribe-Depth-L3 Order-Book-Update lighter, b3
Tape (trade prints) Subscribe-Tape Tape-Update hyperliquid, aster, massive, b3, t4
Volume at price Subscribe-VAP VAP-Update hyperliquid, aster, massive, b3, t4
Inside quote Subscribe-BBO BBO-Update hyperliquid, aster, lighter, polymarket, massive, b3, t4
Candles Subscribe-Candles Candle-Update hyperliquid, aster, lighter, polymarket, kalshi, massive, b3, t4

massive is the venue key for listed futures; the same key is what GET /api/v1/symbols/{venue} enumerates them under.

Each has a matching Unsubscribe-*; Unsubscribe-All drops the lot. Ping answers Pong, and every subscribe frame answers Result.

Four different refusals, and they mean different things:

Result error Means What to do
invalid_frame The venue is not in this stream’s set at all, or a required field is missing. detail names it. Fix the frame; retrying will not help.
venue_disabled The venue is real for this stream but switched off by an admin. Not yours to fix — a different venue, or ask the operator.
bbo_disabled The inside-quote feed is off platform-wide. Use Subscribe-Depth and read the top of book.
subscribe_failed The venue accepted, the symbol did not. detail carries the venue’s own complaint. Check the symbol against GET /api/v1/symbols/{venue}.

venue and symbol are separate fields — there is no venue:symbol string on the wire — and symbol is the venue-suffixed ticker, not the bare asset:

{ "type": "Subscribe-Depth", "venue": "hyperliquid", "symbol": "BTC.HL" }

The suffix is not redundant with venue: it is the platform-canonical symbol, the same string GET /api/v1/symbols/{venue} returns and the same one the REST market-data routes take. Sending the bare asset is refused — subscribe_failed, "invalid HL symbol for depth subscribe: BTC" — because the venue never sees a symbol it recognises.

Subscribe-Candles and Unsubscribe-Candles additionally require interval, written as a count plus a unit — s, m, h, d, w, or M (1m, 15m, 4h, 1d, 1w). Custom resolutions are accepted and folded server-side from a finer base interval where the venue has no native bar. Each interval is its own subscription: unsubscribing 5m leaves 1h running.

Prices and sizes arrive as decimal strings, and every frame echoes venue and symbol so one socket can multiplex the whole set.

High-rate venues can print far faster than any UI can render. The tape is batched server-side into one frame per flush window: a single Tape-Update carries a trades array holding every print that landed in the window, in wire order. Iterate it; do not assume one frame equals one trade.

This matters most on futures venues, where an active session can produce hundreds of prints per second on one symbol.

Like the other channels, ?encoding=msgpack on the upgrade URL switches server→client frames to MessagePack. On a busy depth subscription this is the single cheapest performance win available to a client.

  1. Subscribe first.
  2. Then fetch the REST snapshot.
  3. Apply buffered updates on top.

Doing it the other way round leaves a hole between the snapshot and your first frame.