Order book, ticker and trades
Three topics carry market data, the same for every participant, in the same wire format as the account channels and the REST API.
| Topic | Carries | Filters |
|---|---|---|
orderbook |
aggregated book levels | market_ids (required) |
ticker |
per-market price summary | sports, competitions |
trades |
executed trades, anonymous | market_ids, event_ids |
Each is a single topic covering every market, narrowed by the join payload. Watching ten markets is one join, not ten.
Field names
Section titled “Field names”Money is a dollar string ("0.3900"), quantities are quantity strings
("100.00"), and counts are plain integers. Field names are snake_case
throughout, and the offer side is called offer, not ask, the same names the
REST market payload uses.
Filters
Section titled “Filters”Every filter follows one contract:
- omitted,
null,[], or a list with no usable entry means no filter; - unusable entries are dropped rather than rejecting the join;
- the join reply echoes what was actually applied, so compare it against what you sent to catch a typo;
- naming two filters narrows: a message must match both;
select_filters(orselect_market_idsonorderbook) changes them without rejoining.
orderbook is the one exception: it requires at least one valid market_id.
orderbook
Section titled “orderbook”["1","1","orderbook","phx_join",{"market_ids":["<uuid>","<uuid>"]}]An absent or unusable list is an error, not “no filter”:
{"status":"error","response":{"reason":"market_ids_required"}}Pushes "book" on the matching engine’s publish cadence, one message per market:
{ "market_id": "...", "bids": [{"price":"0.3900","quantity":"100.00","liquidity":"39.0000", "total_quantity":"100.00","total_liquidity":"39.0000"}], "offers": [], "timestamp": "2026-09-02T21:16:39.717812Z", "timestamp_us": 1788383799717812}Levels are best-first. liquidity is that level alone; total_quantity and
total_liquidity are cumulative through it.
Change markets without rejoining:
["1","2","orderbook","select_market_ids",{"market_ids":["<uuid>"]}]The non-empty requirement still applies; an unusable list leaves the current selection in place and replies with an error.
ticker
Section titled “ticker”["1","1","ticker","phx_join",{}]["1","1","ticker","phx_join",{"sports":["Football"],"competitions":["NFL"]}]Pushes "ticker" for each market whose price, book top, volume or open interest
moved:
{ "market_id": "...", "market_symbol": "STXNFL-...", "event_id": "...", "event_symbol": "STXNFL-...", "sport": "Football", "competition": "NFL", "last_traded_price": "0.3900", "last_traded_quantity": "100.00", "best_bid": "0.3800", "best_bid_quantity": "250.00", "best_offer": "0.4000", "best_offer_quantity": "175.00", "bid_depth": 4, "offer_depth": 6, "open_interest": "1200.00", "total_volume": "48000.00", "timestamp": "...", "timestamp_us": 1788383799717812}bid_depth and offer_depth count price levels, so they are integers rather than
quantity strings. Any field can be null on a market that has not traded or has an
empty side of the book. event_symbol is null while the event list is still
warming.
Filter values are matched exactly as the market carries them, so "Football" and
"football" are different.
trades
Section titled “trades”["1","1","trades","phx_join",{}]["1","1","trades","phx_join",{"market_ids":["<uuid>"],"event_ids":["<uuid>"]}]Pushes "trade", one per execution:
{ "market_id": "...", "market_symbol": "STXNFL-...", "event_id": "...", "event_symbol": "STXNFL-...", "price": "0.3200", "quantity": "100.00", "action": "buy", "timestamp": "...", "timestamp_us": 1788383799717812}action is the taker’s side: "buy" when the incoming order bought from the
book, "sell" when it sold into it.
This feed is anonymous: it carries no account, user or order identifier for any market participant.

