Get started
Paging and completeness
A call that returns data returns all of it, or one page of it with a cursor to the next. A limit sets how much comes back at once, never how much you can reach, and no call thins rows or keeps only the first or last of them. Follow the cursor until it is null and you have everything.
The cursors
| Call | Page size | Next page | Totals |
|---|---|---|---|
| GET /markets/{ticker}/snapshots | limit grid points (1–5000) | next_start_ts → start_ts | complete |
| GET /markets/{ticker}/replay | limit frames (up to 3,600) and every trade in their span | next_offset → offset | frames_total, trades_total |
| GET /files, GET /requests/{id}/files | limit files (up to 1,000) | next_cursor → cursor | total_files, total_bytes |
| GET /markets | limit markets (up to 200) | next_offset → offset | total for a series or event |
| GET /events/{id} | limit markets (up to 400) | next_offset → offset | markets_n |
| POST /events/{id}/bundle | 31 days a zip | next_start_date → start_date | days_total |
| GET /coverage/incidents | limit incidents (up to 500) | next_offset → offset | total |
| GET /series, GET /series/{id}, GET /explore | limit | offset (and event_offset) with has_more | series_total, total |
Exports (POST /exports) hold every row of what they cover: each second a market's book changed, on every published day of the market, event or series-day. GET /exports/{id} says how many rows, markets and days. Search (GET /search) ranks series and shows the best few events and markets; markets_found counts the matches and more_markets is the call that lists them all.
Walk every page
import requests
url = "https://kalshieye.com/api/v1/markets/KXBTC15M-26OCT252000-00/snapshots"
headers = {"Authorization": f"Bearer {KEY}"}
params = {"start_ts": 1792971900, "end_ts": 1792972800, "step_ms": 1000, "limit": 5000}
rows = []
while True:
page = requests.get(url, headers=headers, params=params).json()
rows += page["snapshots"]
if page["next_start_ts"] is None:
break
params["start_ts"] = page["next_start_ts"]When one call can't
Some requests are too big for a single response and say so instead of returning part: a zip over 2 GB or a CSV copy of a file over 2 GiB (413, with how to split it), and sub-second snapshots that would replay more than 500,000 book changes (400, asking for step_ms of 1000 or more). The daily files hold everything: see bulk downloads.