Kalshi's API shows a market's order book only as it is now. To see the book at a past moment, you need a recording of every change. Kalshi Eye records every order book change on every Kalshi market, 32 billion rows so far, published each morning as daily Parquet files.
Checked against the files on November 2, 2026.
How Kalshi's order book works
A Kalshi book has two sides, and both are bids: bids to buy YES and bids to buy NO. A NO bid at 31¢ is an offer to sell YES at 69¢, so the best YES ask is 1 minus the best NO bid. Here is the top of the book for SENATETX-26-D (Democrats win the Texas Senate race) at the end of November 1, 2026:
| YES bid | Contracts | NO bid | Contracts | Same as a YES ask at |
|---|---|---|---|---|
| 0.68 | 578,389 | 0.31 | 901,998 | 0.69 |
| 0.67 | 59,459 | 0.30 | 903,035 | 0.70 |
- Prices are dollars from 0 to 1; a contract pays $1 if its side wins. Most markets move in 1¢ steps. Markets with the
tapered_deci_centstructure, such as the 15-minute crypto markets, move in 0.1¢ steps below 10¢ and above 90¢. Each market's steps are in themarketsfile (tick_structure,price_ranges). - Sizes are contracts resting at that price.
- Updates arrive as a snapshot of the whole book, then deltas: a signed change in size at one price on one side, with a sequence number.
What Kalshi's API gives you
GET / returns the YES and NO bids as they are now. The WebSocket's orderbook_delta channel sends a snapshot and then every change, but only from the moment you connect. Candles give the best bid and ask once a minute. Nothing in the API returns a past book.
Four ways to read the history
| Dataset | What it holds | Plan | Use it for |
|---|---|---|---|
book_l2 | Every change to the YES and NO bid books, every price level. Each day opens with a snapshot of every book. | Full book | The whole book at any moment: depth, queue position, how the book reacts to trades. |
book_snap_1s | The top ten YES bid and ask levels at the end of every second in which the book changed. | Research | Spread and depth over time without replaying anything. |
bbo | The best YES bid and ask with their sizes, one row each time any of the four changes. | Research | What you could have bought or sold at, at any moment. |
bbo_1m | Minute bars of the YES mid, with the closing bid and ask, trade count and volume. | Research | Charts, long date ranges and minute-level backtests. |
Every file is one series for one UTC day. In book_l2, snapshot rows set a level and delta rows add to it, so each day's file replays on its own.
Rebuild the book at any moment
The Python client replays a day of book changes and hands you the book after each one:
from kalshieye import Client, replay_book # pip install kalshieye
kx = Client() # reads KALSHIEYE_API_KEY
book = kx.load("book_l2", "2026-11-01", "2026-11-01", series="SENATETX")
for ts, ticker, bids, asks in replay_book(book, ticker="SENATETX-26-D"):
... # bids and asks: the top ten (price, size) levels after every changeThe same thing without the client, on a downloaded file:
import duckdb
# Rows are already in time order for each ticker: read them as the file has them.
rows = duckdb.sql("""
SELECT epoch_ns(ts_recv) AS ts, is_snapshot, side, price, size
FROM 'kalshieye-data/book_l2/date=2026-11-01/series=SENATETX/*.parquet'
WHERE ticker = 'SENATETX-26-D'
""").fetchall()
book = {"yes": {}, "no": {}} # side -> {price: contracts}
snapshot_at = None
for ts, is_snapshot, side, price, size in rows:
if is_snapshot:
if ts != snapshot_at: # the first row of a new snapshot: start over
book = {"yes": {}, "no": {}}
snapshot_at = ts
if side: # a snapshot with no side: the book was empty
book[side][price] = size
else: # a delta: add the signed change, drop a level at or below zero
level = book[side].get(price, 0) + size
if level > 0:
book[side][price] = level
else:
book[side].pop(price, None)
best_bid = max(book["yes"], default=None)
best_ask = round(1 - max(book["no"]), 4) if book["no"] else None # YES ask = 1 - best NO bidSpread and depth without replaying
book_snap_1s already holds the top ten levels each second, so most spread and depth questions are one query:
SET TimeZone = 'UTC';
-- Average spread and the contracts bid in the top five levels, hour by hour.
SELECT date_trunc('hour', ts::TIMESTAMPTZ) AS hour,
round(avg(ask_px_0 - bid_px_0), 4) AS avg_spread,
round(avg(bid_sz_0 + bid_sz_1 + bid_sz_2 + bid_sz_3 + bid_sz_4)) AS bid_depth_5
FROM 'kalshieye-data/book_snap_1s/date=2026-11-01/series=KXBTC15M/*.parquet'
WHERE bid_px_0 IS NOT NULL AND ask_px_0 IS NOT NULL
GROUP BY hour
ORDER BY hour;Questions
Does Kalshi have historical order book data?
Not through its API. GET /markets/{ticker}/orderbook returns the book as it is now, and the WebSocket sends changes only while you are connected. Past books exist only where someone recorded the feed as it changed.
Why does a Kalshi order book have no asks?
Every order is a bid on YES or a bid on NO. Selling YES at 69¢ is the same trade as buying NO at 31¢, so Kalshi keeps only bids. The YES ask is 1 minus the best NO bid.
How deep is the recorded book?
book_l2 has every price level on both sides. book_snap_1s keeps the top ten YES bids and asks, which covers most questions about spread and depth.
How big are the files?
It depends on how busy the market is. A day of book_l2 for KXBTC15M, Kalshi's 15-minute bitcoin markets, is about 260 MB; a day for a Senate race is under 1 MB.
Next
- Kalshi historical order book data: datasets, plans and coverage.
- Data dictionary: every column of every dataset.
- Kalshi 15-minute crypto markets
- How to backtest Kalshi markets