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.
Joining
Section titled “Joining”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.
Server pushes
Section titled “Server pushes”order_book_update
Section titled “order_book_update”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.
market_update
Section titled “market_update”The full Market map, pushed whenever the market changes, status
transitions, trading halts, settlement. Same shape the
markets channel delivers.
Re-syncing after a reconnect
Section titled “Re-syncing after a reconnect”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.
Authentication
Section titled “Authentication”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.

