Skip to main content
Routeur records the order books of Kalshi and Polymarket: every level change as it arrives on the venue’s own feed, a full copy of each book at intervals, and the top of the book for every second in which it changed. Three endpoints read that record: the book now or at a past moment, the top of book over time, and the health of each venue’s feed.

What a book is here

A prediction market has two outcomes, Yes and No, and a contract on either pays $1 (Kalshi) or 1 USDC (Polymarket) if it wins. A bid to buy Yes at 41¢ is the same order as an offer to sell No at 59¢, and the venues match them that way. So a book is described completely by its resting bids per side: the Yes bids and the No bids. A Yes ask at price p is a No bid at 1 − p, and vice versa. Everything Routeur stores is bids; asks are derived.
  • On Kalshi, the feed publishes exactly this: yes and no bid ladders.
  • On Polymarket, each outcome token has its own book, and the exchange mirrors every bid on one token as an ask on the other. Routeur subscribes to both tokens and records each token’s bids (Yes-token bids as Yes bids, No-token bids as No bids), so every order is stored once. When only one of a market’s tokens is known, that token’s asks stand in for the other side’s bids.
Prices are dollars per contract, exact to six decimals. Quantities are contracts on Kalshi and outcome shares on Polymarket. USD amounts are price × quantity.

Which markets are recorded how

The collector sorts listed markets into two tiers and re-reads the sorting every 5 minutes: A polled book is only as fresh as its last poll. With over 100,000 polled markets per venue, a cycle takes hours; the polled issue on a book says so, and as_of says when it was read.

What is kept, and for how long

The export job runs at 02:30 UTC, writes each day past retention as Zstandard-compressed Parquet under books/{dataset}/venue={venue}/date={YYYY-MM-DD}/, with a manifest listing every object’s rows, bytes and SHA-256, and drops the hot partition only after both venues exported cleanly. BigQuery external tables books.snapshots and books.top read the objects in place, partitioned by venue and date; a partition filter is required. The archive is not served through the API.

Three clocks

Three timestamps appear on books, and they are never mixed: Feed latency, receipt minus venue_ts, is reported per venue as latency_ms_p50 and latency_ms_p99 in GET /v1/books/health. It includes the venue’s own publishing delay and the network path. When the observed p50 was 19 ms on Kalshi and 50 ms on Polymarket, that was the whole delay from the venue’s stamp to Routeur’s receipt.

The book now, or at a moment

The book is rebuilt from the latest snapshot taken at or before the moment, plus every level event received since. Without at, source is live; with it, rebuilt.
The Yes asks are the No bids at the complementary price: 38,282 contracts bid for No at 45¢ are 38,282 contracts offered as Yes at 55¢. The liquidity fields of the market follow (trimmed above); see Liquidity.

The top of book

At a past moment

at accepts up to 90 days back, but the rebuild is exact only while the level events are hot (7 days). Between 7 and 30 days back the book is the latest snapshot at or before the moment with no events on top, so events_applied is 0 and as_of is the snapshot’s time; for a full market that is at most 15 minutes before the moment asked. Further back than the snapshots reach, the answer is 404 book_not_found.

The top of book over time

One point per step in which the top changed, each the last state in its step, oldest first. The series is recorded per second of change and kept hot for 30 days.
at is the start of the step; changes counts the seconds within it in which the top changed. A step with no change has no point, so a quiet market produces a short series: carry the previous point forward when you chart it.
At most 5,000 points per request, and the window may not be longer than 5,000 steps: a day at 1s needs 18 requests of 5,000 seconds each, and a window longer than that is rejected as invalid_parameter on from. At 1m a window of up to 3 days 11 hours fits in one request.

Feed health

One entry per venue, from the collector’s own report, which it rewrites every 15 seconds:

Books on the live stream

The books topic on /v1/stream pushes a book event when a market’s top of book changes (best prices or the size at them), at most once a second per market. It carries the top in Yes terms with the 5-cent depths and the imbalance, the venue’s seq and venue_ts, observed_at (receipt) and recorded_at. Deeper changes that leave the top alone are not streamed; read them from the book. Polled markets appear only when a poll shows their top changed, so once a cycle at most.

Caveats

  • Polled books are old. A rest market is read once per cycle, 5 levels per side. Check as_of and the polled issue before treating the book as current.
  • Polymarket has no sequence numbers. A lost message cannot be detected as it happens; it shows up as a correction when the venue re-sends the book, which it does after each trade. Between a lost message and the next re-send, the book held can be wrong at that level.
  • Kalshi snapshots carry no venue_ts. A book rebuilt from a snapshot with no events since has venue_ts absent.
  • Depth windows include the boundary level. *_depth_1c_usd on a 1¢-tick market covers the best two levels, not one.
  • Quantities are rounded to six decimals. Polymarket shares can carry more.
  • Raw events are not exported. Until the data-rights review clears redistribution, level events are queryable only for 7 days through at= rebuilds; the snapshot and top-of-book series are the durable record.
  • Liquidity: the book_* fields carried by every market, and the grades that decide the full tier.
  • Live stream: the books topic.
  • Leads: depth, which walks both books of a lead with real fees and minimum sizes.
  • Quotes and trades: the crawl’s asks and the public fills, the other two price records.