Endpoint
A single WebSocket connection can subscribe to any number of markets. The server pushes one frame per trade print, in real time. There is no snapshot on subscribe — see Backfill.
Authentication
Browsers can’t set custom headers on the WebSocket handshake, so the
signed-request flow moves to query params. The signature covers
WS\nPATH\nSORTED_QUERY (excluding key_id/ts/sig)\nTIMESTAMP. See
Authentication for the full recipe.
The official SDKs sign the handshake for you.
For first-party web clients you can authenticate with a Supabase JWT instead:
?access_token=<jwt>. Invalid or missing auth closes the connection with code 4401.
Backfill
The WebSocket stream is live-only — it does not emit historical trades on subscribe. To populate a recent trades view on page load, call:
This REST endpoint accepts one or more river_ids (repeat the parameter), queries each source exchange sequentially, and returns the most recent trades for every market in descending time order. The response is { "results": [...] } with one block per requested river_id in input order. Per-market failures show up as not_found rows with a message. Fire it in parallel with the WebSocket subscribe action and deduplicate overlap by exchange_trade_id.
Protocol
All frames are JSON. Client frames are text; server frames are bytes (orjson-serialized UTF-8).
Client → server
subscribe / unsubscribe accept one or many river_ids. Duplicate subscribes on the same connection are no-ops.
Server → client
trade is sent once per print. Append-only — frames are never coalesced.
reconnect is sent during graceful pod shutdown. Reconnect with a small jitter.
Keepalive
Liveness is handled at the WebSocket protocol layer (RFC 6455 PING/PONG control frames). Standard clients (Python websockets, browser WebSocket) reply to server PINGs automatically — no application-level heartbeat is needed.
Tradeprint payload
| Field | Type | Notes |
|---|
exchange_timestamp | string (ISO-8601) | null | Exchange-reported trade time. |
price | number | Trade price, normalised to 0–1 in YES terms. |
qty | number | Trade size in contracts. |
aggressor_buy_flag | boolean | null | true if the taker bought (hit an ask). false if the taker sold. null if unknown. |
exchange_trade_id | string | null | Exchange-assigned trade id. Use it to deduplicate REST backfill against the live WS stream. |
Rate limits and pacing
Trades are append-only events — they are not rate-capped or coalesced. Every print on the upstream exchange is forwarded. If a slow client cannot drain fast enough, the server closes the connection with code 1011; reconnect and re-backfill.
Minimal client
Using the SDK (handles REST + WS signing automatically):
Close codes
| Code | Meaning |
|---|
1000 | Normal closure |
1011 | Server-side overflow (client couldn’t keep up with the send rate) |
4401 | Missing or invalid authentication |
4503 | Server draining for deploy — reconnect with jitter |