# How to stream a prediction market order book

Five live book feeds across four venues, and the parts that decide whether your local book is right — deltas versus replacements, missed messages, reconnects.

*https://predictionmarkets.tools/how-to/stream-an-order-book · next to Prediction Market Data APIs*

**Answer:** Pick the venue first. Polymarket's market channel is keyless and sends a book snapshot then price changes; Kalshi signs even its public handshake and sends deltas; Limitless replaces the whole book each frame; Polymarket US puts its markets stream behind the same identity check as trading. The feed is the easy half. Keeping a local book that still matches the venue is the rest of it.

## The approaches, in order

1. [Polymarket CLOB API](https://predictionmarkets.tools/tools/polymarket-clob-api.md) — The keyless one. A public market channel keyed by outcome token id — a full book snapshot on subscribe, then price changes and last trade prices as they land.
2. [Kalshi API](https://predictionmarkets.tools/tools/kalshi-api.md) — Snapshots and deltas on the exchange's own socket, except that the handshake is signed even for public channels, so there is no keyless live stream here.
3. [pykalshi](https://predictionmarkets.tools/tools/pykalshi.md) — The third-party Kalshi client whose OrderbookManager applies the deltas and hands back the current book — the piece most streaming scripts write badly once.
4. [Limitless API](https://predictionmarkets.tools/tools/limitless-api.md) — Socket.IO on a markets namespace, where each order book frame is the whole book rather than a delta, carrying a version number to order frames by.
5. [Polymarket US API](https://predictionmarkets.tools/tools/polymarket-us-api.md) — The US-accessible stream. Books and trades on the authenticated host, capped at 100 markets a subscription, behind the same identity check as trading.

*Ordered editorially. Paid placement does not affect this order.*

## The short way

Two things decide the shape of the code, and neither of them is the language you write it in:
whether the venue sends you the *changes* to a book or the *whole* book each time, and whether you
are allowed to connect at all without an account.

If you just want a live ladder on screen today, start with
[the Polymarket CLOB API](https://predictionmarkets.tools/tools/polymarket-clob-api), because it is the only one here that takes
no credentials. Open the market channel, send one subscribe frame naming the outcome token ids you
care about, and the server answers with a `book` event — the full bids and asks with a `hash` and a
timestamp — followed by `price_change` as levels move, `last_trade_price` when a trade prints, and
`tick_size_change` when the price crosses into the finer grid near the ends.

```python
import json
from websockets.sync.client import connect

# Outcome token ids come from the catalogue side of Polymarket, not from here.
URL = "wss://ws-subscriptions-clob.polymarket.com/ws/market"

with connect(URL) as ws:
    ws.send(json.dumps({"assets_ids": [TOKEN_ID], "type": "market"}))
    for raw in ws:
        message = json.loads(raw)
        # `book` is the snapshot. `price_change` moved a level. `last_trade_price` was a fill.
```

Two details from Polymarket's own streaming notes will bite the first version of that loop. You are
expected to send a `PING` roughly every ten seconds and the server answers `PONG`; go quiet and the
connection goes. And `price_change` fires when an order is *placed or cancelled*, which is not a
trade — `last_trade_price` is the event that means somebody was filled. The token ids themselves
come from [the Gamma API](https://predictionmarkets.tools/tools/polymarket-gamma-api), which is the catalogue half of the same
integration.

If the reader is in the United States, none of that is the answer, because trading on that platform
is not open to them. The two US-accessible streams both sit behind an account.

## What the options are

[The Kalshi API](https://predictionmarkets.tools/tools/kalshi-api) is the most conventional feed in this category and the one
closest to a futures venue: the WebSocket carries incremental order book updates, market tickers,
public trades and your own fills, and it is the exchange's own record rather than a reconstruction.
The catch is at the handshake rather than in the data. The socket is **not public** — the
connection itself returns 401 without signed headers, even for channels that only carry public
information, so there is no keyless live stream and no way to prototype one. The key is an RSA key
pair generated inside an account that has already passed identity verification.

[pykalshi](https://predictionmarkets.tools/tools/pykalshi) is where the delta arithmetic stops being your problem. It is a
third-party MIT client, one author, five runtime dependencies, and its `OrderbookManager` applies
the deltas and hands back the current book; the typed messages — tickers, order book snapshots and
deltas, trades — arrive as an async iterator you can pattern-match on, with automatic retries and
typed errors around them. That is the piece every market-making script otherwise writes badly once.
It is one person's library, it does not cover the whole API by its own admission, and it learns
about an API change when something breaks.

[The Limitless API](https://predictionmarkets.tools/tools/limitless-api) is the odd one out and usefully so. Real time is Socket.IO
at a `/markets` namespace, and the public market-data events need no signed handshake at all;
`subscribe_market_prices` takes market addresses for its AMM markets and market slugs for its CLOB
markets, and the server emits one `orderbookUpdate` per subscribed slug straight away. What arrives
is a **complete book replacement** rather than a delta — bids highest-first, asks lowest-first, the
whole current state, coalesced so that several changes in quick succession arrive as one message.
Each frame carries a `version` that increases per market, which the documentation suggests using to
drop a frame that arrives out of order just after you resubscribe. The server runs the heartbeat
itself, and the docs ask you not to send PING frames of your own.

[The Polymarket US API](https://predictionmarkets.tools/tools/polymarket-us-api) is the US-regulated counterpart, and its split
between hosts is the whole story: the public gateway serves the book over REST with no credentials,
while the markets stream lives on the authenticated host at `/v1/ws/markets` and takes the same
API-key headers as every write. Subscriptions are typed — a full order book with market statistics,
a lite variant carrying price data only, and a separate trade subscription — and each one is capped
at 100 markets, with more subscriptions rather than a higher cap as the documented way past it. The
server sends a `{"heartbeat": {}}` message periodically, and the guidance for everything else is
exponential backoff.

```json
{
  "subscribe": {
    "requestId": "book-1",
    "subscriptionType": "SUBSCRIPTION_TYPE_MARKET_DATA",
    "marketSlugs": ["your-market-slug"]
  }
}
```

The credential that opens that socket is created from a developer portal on an account opened in the
venue's iOS app, after identity verification — so the stream is gated by the same thing the orders
are. [Placing an order from code](https://predictionmarkets.tools/how-to/place-an-order-from-code) is the other half of that work.

## Where this breaks

**Snapshot-plus-delta only works if you can tell that a message is missing, and mostly you cannot.**
Polymarket's `book` event carries a `hash`, which answers "does the book I am holding match the one
you just described" — it does not tell you *which* message you dropped, and there is no sequence
number on the market channel to count. The cost of that at scale is recorded on our
[PolyOrderbooks](https://predictionmarkets.tools/tools/polyorderbooks) card: the published dataset's README says the delta feed
runs at roughly 24,000 events a second with no sequence numbers, so dropped messages cannot be
detected from the stream at all, and the collector re-reads the full book over REST every 60 seconds
to bound the drift. That REST re-read *is* the resynchronisation procedure, and it is one you have
to write. Limitless sidesteps the question by never sending a delta — but its `version` restarts
after a backend failover, so a jump in it is an ordering hint and not a gap alarm. Kalshi is the one
that sends real deltas off a snapshot, which is precisely why [pykalshi](https://predictionmarkets.tools/tools/pykalshi) exists.

**One connection does not carry the catalogue.** Polymarket US is explicit: 100 markets per
subscription, open another subscription if you need more. Limitless publishes no per-connection cap
and instead has a trap the documentation states plainly — emitting `subscribe_market_prices` again
**replaces** the previous subscription on that connection rather than adding to it, so the obvious
loop that subscribes one market at a time ends up watching only the last one, and a call that means
to follow both AMM and CLOB markets has to pass both parameters together. On the rented feeds the
limit is simply a line on the price list: [DepthFeed](https://predictionmarkets.tools/tools/depthfeed) runs from one live
subscription on its free plan to five on one connection, 25 on two, and 100 with wildcards on five,
and [Predexon](https://predictionmarkets.tools/tools/predexon) caps its mid tier at 10 subscriptions of 10 items across three
channels.

**A reconnect is a hole in your data, and nothing here replays it.** None of these venues documents
a message backlog or a catch-up window. What a reconnect gets you is a fresh snapshot, which repairs
your *book* and does nothing for your *record*: every trade that printed while you were away is
missing from your file unless you go back to REST and fetch it. Watch the heartbeat rather than the
socket state — Polymarket US tells you to reconnect when heartbeats stop, Limitless runs the
heartbeat server-side, and the Polymarket market channel expects the ping from you — and treat the
gap as data loss to be backfilled, not as a blip.

**A venue with no central limit order book has no book to stream.** Everything above runs a
[central limit order book](https://predictionmarkets.tools/glossary/central-limit-order-book); much of this sector does not.
[The Manifold API](https://predictionmarkets.tools/tools/manifold-api) has a WebSocket with a per-market topic, but the venue is a
CPMM [automated market maker](https://predictionmarkets.tools/glossary/automated-market-maker) with resting limit orders sitting on
top of the curve, so what that topic carries is updates to those resting orders — the price a
position actually costs you there is the curve's impact, not a level you can read off a ladder.
[The Futuur API](https://predictionmarkets.tools/tools/futuur-api) fails the other way: it does publish an order book per question
per currency mode, but it has no WebSocket, no streaming endpoint and no push channel of any kind,
so "live" there means polling a cache that is a few seconds stale. Decide which of the three you are
pointed at before writing a book-shaped consumer;
[where a prediction market's liquidity comes from](https://predictionmarkets.tools/guides/where-liquidity-comes-from) is the
background on why a venue picks one.

**A streamed top of book is not a fill.** It is what was resting a moment ago, on a venue that
decides what you get after you send. Limitless rejects a signed order whose receive window has
passed with HTTP 425. Polymarket documents matching-engine maintenance windows and a post-only mode
after a restart, which a naive bot reads as rejections. Polymarket US delivers the per-entry outcome
of a batch on the private order stream rather than in the HTTP response, and its `canceledOrderIds`
is an echo of the request rather than a confirmation — so a 200 tells you nothing about what
happened. And Kalshi answers 429 with no `Retry-After` and no rate-limit headers, so whatever
polling runs alongside your stream needs a bucket model you wrote yourself. Size the position on the
depth you can still see after the round trip, not on the frame that prompted it.

## If you outgrow this

If the question is more venues than you want connections for,
[tools with a streaming feed](https://predictionmarkets.tools/collections/streaming-feeds) is the listing and
[tools that cover more than one venue](https://predictionmarkets.tools/collections/cross-venue) is the overlap.
[Predictefy](https://predictionmarkets.tools/tools/predictefy) puts sixteen venues behind one verb family and sends **full book
snapshots rather than deltas**, metered at two credits per connection-minute with two streaming
connections on its free plan; Predexon sells tick-level history beside its socket.
[Adjacent versus DepthFeed versus Predexon](https://predictionmarkets.tools/compare/cross-venue-prediction-market-data) is the head
to head, and the thing each of them flattens to fit several venues in one schema is usually the part
of the book you were streaming for. [FinFeedAPI](https://predictionmarkets.tools/tools/finfeedapi) is worth knowing about for the
opposite reason: no WebSocket on its prediction-market product at all, but raw limit-order events —
adds, updates, deletes — pulled from flat files for replay.

And if what you actually needed was the book from *before* you connected, no venue will sell it to
you: Polymarket's own documentation says the depth that produced a price is not retained, and
Limitless says past activity has to be reassembled from price history, feed events and your own
fills. [DepthFeed](https://predictionmarkets.tools/tools/depthfeed) and [PolyOrderbooks](https://predictionmarkets.tools/tools/polyorderbooks) record it
independently, both across a crypto slice rather than the whole catalogue, and the shape of each
archive follows the shape of the feed behind it — DepthFeed captures Polymarket off the CLOB
WebSocket and Kalshi by paced full-depth REST polling, which is why its own measured Kalshi latency
is about a second and its Polymarket latency is about ten milliseconds.

## FAQ

### Which of these can I stream without an account?

One, cleanly. Polymarket's market channel takes no credentials at all, and Limitless lists its market-data events as public with no signed handshake needed. Kalshi's WebSocket returns 401 without signed headers even on channels carrying only public data, and the Polymarket US markets stream lives on the authenticated host behind an identity check. Reading a book is still not permission to trade against it.

### Do any of these give me a sequence number I can detect a gap with?

Not in the form most people expect. Limitless puts a version on each order book frame, which increases per frame and restarts after a backend failover — an ordering hint rather than a gap detector. Polymarket's book event carries a hash, which tells you whether your book matches, not which message you dropped. Polymarket US documents no sequence field and no replay.

### How many markets can one connection carry?

Polymarket US caps a subscription at 100 markets and tells you to open more subscriptions rather than raising it. Limitless publishes no per-connection cap but replaces a subscription when you emit it again, so a loop that subscribes market by market ends up watching only the last one. On the rented cross-venue feeds the cap is a line on the price list.

### Can I get the book from before I connected?

Not from the venues. Polymarket's own documentation says the depth behind a price is not retained, and Limitless says past activity has to be reassembled from price history, feed events and your own fills. Recorded ladders are a product somebody sells, and the two in this catalogue both cover a crypto slice rather than the whole catalogue.

## Sources

1. [Real-time data streaming](https://github.com/Polymarket/agent-skills/blob/main/websocket.md) — Polymarket, read 2026-09-21
2. [Markets WebSocket](https://docs.polymarket.us/api-reference/websocket/markets) — Polymarket US, read 2026-09-21
3. [WebSocket overview](https://docs.limitless.exchange/developers/websocket/overview) — Limitless Exchange, read 2026-09-21
4. [Market data over WebSocket](https://docs.limitless.exchange/developers/websocket/market-data) — Limitless Exchange, read 2026-09-21

*Last updated 2026-09-21. A reference page, corrected in place — not a dated post.*
