This guide takes you from a stream key to a live, gap-free orderbook in one
connection. The examples are Node.js with the ws
library, but the protocol is plain JSON over a standard WebSocket, so you can
use any WebSocket client.
Prerequisites
1. Connect and authenticate
Connect to:
Authentication happens during the WebSocket upgrade — there is no separate
login message. Supply your key one of three ways, in order of preference:
Prefer the subprotocol or header. The ?api_key= query parameter is supported
as a fallback for clients that can’t set headers, but query strings are
commonly written to logs and proxies along the way — avoid it when you can.
If the key is missing or invalid, the upgrade is rejected with HTTP 401
before the connection opens. A 503 means the gateway is at capacity —
reconnect, and you’ll land on fresh capacity. A 429 means your account is at
its connection limit — close a
connection you no longer need instead of retrying.
2. Receive welcome
Immediately after the upgrade succeeds, the server sends a welcome frame:
Keep the connectionId — quote it in any support request so we can find your
session in our logs. The limits tell you how many subscriptions this
connection accepts and how long it will live before the server closes it for a
scheduled reconnect.
3. Subscribe
Send a subscribe frame listing the markets you want. The only required
fields are type and markets; channels defaults to ["book"].
For each market in the batch you get back either a subscribed ack
or an error (for example, UNKNOWN_MARKET) — one reply per market, so a
single bad entry never sinks the rest of the batch.
Cache the mapping from subscriptionId to whatever local state you keep for
that market — every subsequent frame for this subscription is keyed by that id,
and it’s also the handle you pass to unsubscribe.
snapshotPending: true tells you a snapshot is on its way.
4. Apply the stream
A book subscription delivers a snapshot first, then delta frames:
That’s the whole opening sequence. snapshot replaces your book; delta
mutates it. Because of the sequencing contract,
your apply loop needs no gap-handling logic of its own — if we ever could not
send a contiguous delta, we send a fresh snapshot instead.
Optional: the trade tape
To also receive each fill on a market as it executes, subscribe with the
trade channel (each (market, channel) pair is its own subscription, with
its own subscriptionId):
Then add a case to the handler:
The tape needs no state on your side: there is no snapshot
(snapshotPending: false on the ack), and frames flow from the moment you
subscribe — earlier fills are not replayed. See the
trade frame reference for the full semantics.
The trade channel is available on Polymarket; Limitless publishes no public
trade feed, so a trade subscribe there is rejected with UNSUPPORTED_CHANNEL.
5. Keep the connection healthy
- The server sends WebSocket pings every 30 seconds; your client library
answers them automatically. You can also send an application-level
ping and get a pong back.
- Watch for
goodbye
frames and socket closes, and reconnect. Connections are closed at the
connectionTtlSeconds lifetime from welcome, so schedule a proactive
reconnect before then.
- On a
status frame with state: "resyncing", drop that subscription’s local
book and trust the next snapshot. See
reconnect & resync handling.
Next
The WebSocket API reference documents the full wire contract, one page per
concern: Authentication,
Connection lifecycle,
Subscriptions & markets,
the Orderbook and Trades channels,
Errors & status, and the full
Message catalog.