Stream a live order book
REST gives you a book at a moment. The WebSocket gives you the book as it changes, and it does so with one upstream venue subscription shared across every client watching that market — so scaling consumers does not burn a venue’s rate limits.
The protocol reference is Streaming. This page is the working loop.
Connect and subscribe
Section titled “Connect and subscribe”Connect to /v1/stream on the WebSocket origin shown in your dashboard, authenticating with the
same pk_live_… key as REST.
const ws = new WebSocket(`${WS_ORIGIN}/v1/stream`, { headers: { Authorization: `Bearer ${process.env.PREDICTEFY_API_KEY}` },});
ws.on('open', () => { ws.send( JSON.stringify({ op: 'subscribe', channel: 'orderbook', venue: 'polymarket', marketId: OUTCOME_ID, // venue-native — see below }), );});Handle the frames
Section titled “Handle the frames”ws.on('message', (raw) => { const frame = JSON.parse(raw);
switch (frame.type) { case 'subscribed': case 'unsubscribed': return;
// Both carry the FULL book, never deltas. Replace state; do not merge. case 'snapshot': case 'update': book.set(frame.marketId, frame.data); return;
case 'trade': onTrade(frame.venue, frame.marketId, frame.data); return;
case 'error': // Non-fatal. The socket stays open — do NOT reconnect on these. console.warn(`${frame.code}: ${frame.message}`, frame.venue, frame.marketId); return; }});Two properties decide the shape of this loop:
snapshot and update both carry the full book. snapshot is the first frame after
subscribing and after backpressure coalescing; update marks live ticks. Neither is a delta, so
replace your local state rather than merging into it. Merging works until the first coalesced
snapshot, then drifts.
Protocol errors are non-fatal. BAD_MESSAGE, NOT_SUPPORTED, NOT_SUBSCRIBED and
SUBSCRIPTION_LIMIT arrive as type: "error" frames and the socket stays open. Treating
them as disconnects produces a reconnect loop against a working connection.
Book frames carry bids and asks as { price, size }, with prices as probabilities in
[0, 1].
What you cannot subscribe to
Section titled “What you cannot subscribe to”NOT_SUPPORTED is a real answer about the world, not a failure:
- Venues without a public stream answer it rather than serving a synthetic one.
- Trades stream only where the venue has a native fills channel. Order-book support does not imply trade support.
subscribeAllneeds a venue-wide firehose upstream; venues without one answerNOT_SUPPORTED.
Do not fall back to another venue when you get this. The honest answer is that this venue does not offer that stream — see Capability-honest data.
Subscription limits
Section titled “Subscription limits”Active logical subscriptions are capped by plan: 2 on Free, 20 on Builder, 100 on Pro, 500 on Scale. A lower service safety cap can also apply.
Exceeding either returns a non-fatal SUBSCRIPTION_LIMIT — the socket survives and the
subscription simply does not exist. Track your own count; a subscribe that “succeeded” because
the socket stayed open is not the same as a subscribe that took effect. Wait for the
subscribed ack.
Falling back to REST
Section titled “Falling back to REST”For a point-in-time book, or for any venue without a stream, fetchOrderBook takes an
outcomeId and a limit of up to 1000 depth levels. It also reads the archive: at for the
nearest stored snapshot to a time, since/until for a range, and side to return only bids
or only asks.
Related
Section titled “Related”- Streaming — the full protocol: auth, close codes, feed tickers
- Identifiers — venue-native versus unified ids
- Venue coverage — which venues have a real book at all