Kalshi's API gives you past trades, one-minute candles and settled markets, free and without an API key. It does not give you past order books: the order book endpoint shows only the book as it is now.
Checked against Kalshi's API on October 31, 2026.
What Kalshi's API has
| Data | Endpoint | What you get |
|---|---|---|
| Markets | GET /markets, /events, /series | Tickers, rules, open and close times, results. Markets that settled before the cutoff move to GET /. |
| Trades | GET / | Every trade: YES price, contracts, the taker's side, trade id and time. Up to 1,000 a page with a cursor; filter by ticker, min_ts and max_ts. Older trades are in GET /. |
| Candles | GET / | 1-minute, 1-hour or 1-day bars (period_interval 1, 60 or 1440) of the YES bid, YES ask and traded price, with volume and open interest. Settled markets past the cutoff: GET /. |
| Order book | GET / | The book right now: YES bids and NO bids. Nothing from the past. |
| Cutoff | GET / | When markets, trades and orders move from the live endpoints to the historical ones. |
Every path is under https:/. A live endpoint stops returning a record once it moves to the historical ones, so a full history reads both.
Every trade in a market
import time
import requests
API = "https://external-api.kalshi.com/trade-api/v2"
def pages(path, key, **params):
"""Every row of a paged endpoint, following the cursor to the end."""
params["limit"] = 1000
while True:
r = requests.get(f"{API}{path}", params=params, timeout=30)
if r.status_code == 429: # rate limited: wait, then ask again
time.sleep(1)
continue
r.raise_for_status()
body = r.json()
yield from body[key]
params["cursor"] = body.get("cursor")
if not params["cursor"]:
break
ticker = "KXBTC15M-26OCT311945-45"
trades = [
*pages("/historical/trades", "trades", ticker=ticker), # before the cutoff
*pages("/markets/trades", "trades", ticker=ticker), # after it
]One-minute candles for the same market, from 7:30 to 7:45 PM Eastern:
candles = requests.get(
f"{API}/series/KXBTC15M/markets/{ticker}/candlesticks",
params={"start_ts": 1793489400, "end_ts": 1793490300, "period_interval": 1},
timeout=30,
).json()["candlesticks"]
for c in candles:
print(c["end_period_ts"], c["yes_bid"]["close_dollars"], c["yes_ask"]["close_dollars"])What the API leaves out
- Past order books. The depth, spread and size at each price at any past moment are gone once the book changes.
- Top of book between minutes. Candles give the YES bid and ask once a minute, not every change.
Order book history
Kalshi Eye records Kalshi's market feed around the clock: every order book change on every market, every trade and every settlement, 28 billion rows so far. Each UTC day is published the next morning as Parquet files.
book_l2: every book change, to rebuild the whole book at any moment.book_snap_1s: the top ten levels each second.bboandbbo_1m: every top-of-book change, and minute bars.tradesandsettlements.
from kalshieye import Client, replay_book # pip install kalshieye
kx = Client() # reads KALSHIEYE_API_KEY
book = kx.load("book_l2", "2026-10-13", "2026-10-13", tickers=["KXBTC15M-26OCT131200-00"])
for ts, ticker, bids, asks in replay_book(book):
... # the whole book after every changeQuestions
Does Kalshi have historical order book data?
Not through its API. GET /markets/{ticker}/orderbook returns the book as it is now. Past books exist only where someone recorded the feed as it changed.
Do I need an API key for Kalshi's market data?
No. Markets, trades, candles and the current order book answer without one. Placing orders and reading your own fills, orders and positions need a key.
How do I get minute-by-minute Kalshi prices?
Call the candlesticks endpoint with period_interval=1. Each candle has the open, high, low and close of the YES bid, the YES ask and the traded price for that minute.
Next
- Kalshi historical order book data: datasets, plans and coverage.
- Data dictionary: every column of every dataset.
- How to backtest Kalshi markets
- How to get Polymarket historical data