Skip to content

Market order book

Streams the live order book for one market: two sides, bid and offer, each a ladder of price levels carrying size, liquidity and cumulative depth, so you can read how far the market goes without summing levels yourself. It refreshes roughly every 200 ms, not on every individual order; see Server pushes below for the exact shape of a push.

The channels under Channels carry your orders, fills and positions. This one carries the market: the full aggregated book and the market record, one market per topic.

One channel per market. Join market:<market_id> for each market you are watching or trading, and join market_updates alongside it if you also want lightweight change notifications across many markets at once.

The topic accepts either the market UUID or its symbol:

["1","1","market:2bc3d8d8-1f4e-4b8a-9c1d-3e5f7a9b1c2d","phx_join",{}]
["1","1","market:NFLSF2025","phx_join",{}]

Unlike the market_updates channel, a successful join replies with the current state immediately: you do not have to ask for a snapshot first:

["1","1","market:2bc3d8d8-…","phx_reply",
{"status":"ok","response":{
"market_id":"2bc3d8d8-…",
"status":"open",
…,
"ob":{"b":[…],"o":[…]}
}}]

The reply is the full market record, the same shape the markets channel sends, with ob added. The two parts use different units: the market record’s prices are in cents, while ob is in dollars. Read each field’s units from its own page.

A join is refused with a reason you can act on:

reason Meaning
market_not_found No market with that id or symbol
market_not_joinable The market exists but is not in a joinable status

Joinable statuses are pre_open, open, closed and cancelled. scheduled markets are hidden and cannot be joined. resulted and voided are terminal: the socket receives one final market_update and is then dropped by the server, so treat an unexpected close on this channel as “the market ended”, not as a network fault.

The aggregated book, pushed roughly every 200 ms, not on every individual change. Between pushes many updates are coalesced, so treat each push as the current state of the levels it contains rather than as a single event.

["1","1","market:2bc3d8d8-…","order_book_update",
{"ob":{"b":[{"p":0.42,"q":150.0,"l":63.0,"tc":420.0,"tl":176.4}],
"o":[{"p":0.43,"q":90.0,"l":38.7,"tc":310.0,"tl":133.3}]}}]

b is the bid side, o the offer side. Each level:

Field Meaning
p Price, in dollars
q Contracts available at this price
l Liquidity at this price, in dollars
tc Total contracts at this price and better
tl Total liquidity at this price and better, in dollars

These are JSON numbers, not the dollar strings the REST API and the orderbook topic send.

tc and tl are cumulative, so you can read depth-to-price straight off a level without summing the ones above it.

The full Market map, pushed whenever the market changes, status transitions, trading halts, settlement. Same shape the markets channel delivers.

Send request_snapshot and the server immediately re-pushes both the current order_book_update and market_update:

["1","2","market:2bc3d8d8-…","request_snapshot",{}]

Do this after any reconnect. There is currently no sequence number on order_book_update, so a client cannot detect a gap while it believes itself connected. If you suspect you have missed data, call request_snapshot and replace your book with what comes back rather than trying to reconcile.

This channel carries no account-specific data, so it accepts an unsigned socket. The handshake still needs a User-Agent header, or it is refused with 403. Your account channels need a signed socket: if you also want your own orders and fills, sign the handshake as described in WebSocket channels and join both on that one connection.

v1.5.9Changelogllms.txtllms-full.txt