Kalshi & Polymarket Election Odds History Scraper
The whole odds history of settled Kalshi and Polymarket election markets: daily or hourly candles with bid/ask, volume and open interest, every candidate and the result. 2024, 2026 and live 2028 races.
Verification
Not yet in the weekly verification report.
Output fields
Authoritative field set from the dataset schema.
| Field | Type |
|---|---|
row_type | string|null |
venue | string|null |
race_id | string|null |
market_id | string|null |
series | string|null |
title | string|null |
subtitle | string|null |
cycle | string|null |
status | string|null |
tags | array|null |
winner | string|null |
candidates | integer|null |
settlement_sources | array|null |
candidate | string|null |
result | string|null |
last_price | number|null |
settlement_value | number|null |
settled_time | string|null |
open_time | string|null |
close_time | string|null |
period | string|null |
period_end | string|null |
open | number|null |
high | number|null |
low | number|null |
close | number|null |
mean | number|null |
yes_bid_open | number|null |
yes_bid_high | number|null |
yes_bid_low | number|null |
yes_bid_close | number|null |
yes_ask_open | number|null |
yes_ask_high | number|null |
yes_ask_low | number|null |
yes_ask_close | number|null |
volume | number|null |
open_interest | number|null |
trade_id | string|null |
time | string|null |
yes_price | number|null |
no_price | number|null |
size | number|null |
taker_side | string|null |
notice | string|null |
notice_type | string|null |
source_url | string|null |
Sample row
{
"source_url": "https://api.elections.kalshi.com/trade-api/v2/historical/markets/SENATEAZ-24-R/candlesticks",
"row_type": "candle",
"venue": "kalshi",
"race_id": "SENATEAZ-24",
"market_id": "SENATEAZ-24-R",
"candidate": "Kari Lake",
"period": "daily",
"period_end": "2024-10-08T04:00:00Z",
"open": 0.14,
"high": 0.16,
"low": 0.14,
"close": 0.15,
"mean": 0.1503,
"yes_bid_open": 0.1,
"yes_bid_high": 0.15,
"yes_bid_low": 0.1,
"yes_bid_close": 0.13,
"yes_ask_open": 1,
"yes_ask_high": 1,
"yes_ask_low": 0.14,
"yes_ask_close": 0.15,
"volume": 1447,
"open_interest": 967
} Inputs
| Field | Type | Req | Default | Description |
|---|---|---|---|---|
queries | array | no | ["arizona senate"] | Races to pull, as words in the race's title: 'arizona senate', 'oregon governor', 'presidential election winner'. Every word must appear. Kalshi is matched on its election series titles, Polymarket through its own search. Polymarket is only read when you give a keyword. |
query | string | no | — | One keyword. Kept alongside 'Keywords' for saved tasks and API callers. |
seriesTickers | array | no | [] | Exact Kalshi series, e.g. SENATEAZ, GOVPARTYOR, HOUSEPA7, SENATENM. One series holds every cycle of that race (2024, 2026, 2028...). |
seriesTicker | string | no | — | One series ticker. Kept alongside 'Kalshi series tickers' for saved tasks and API callers. |
tags | array | no | [] | Every Kalshi election series carrying any of these tags: Senate, House, Governor, Primaries, US Elections, Referendums, Local, 2028, International. Broad: combine with 'Cycle' and 'Maximum races per venue'. |
venues | string | no | "both" | Kalshi, Polymarket or both. Both returns the same race from each venue as separate rows with a 'venue' field. |
cycle | string | no | "any" | Election year. Kalshi's year comes from the race ticker (SENATEAZ-24), Polymarket's from the market's end date. |
status | string | no | "settled" | Settled races (the default) carry the result and their whole history. Open races give the history so far. |
granularity | string | no | "daily" | One row per day or per hour of each candidate's odds. Hourly is 24 times the rows (and the cost). |
from | string | no | — | Only candles and trades from this date (YYYY-MM-DD). Empty = from the day the market opened. |
to | string | no | — | Only candles and trades up to this date (YYYY-MM-DD). Empty = until the market settled, or today if it is open. |
includeTrades | boolean | no | false | Every Kalshi trade: time, yes/no price, size and taker side. A busy race has tens of thousands; set dates or 'Maximum rows' to bound it. |
maxRaces | integer | no | 10 | Stop after this many races from each venue. |
maxResults | integer | no | 5000 | A cap on the whole RUN for candles plus trades: your spend control. Race and candidate rows are not counted. When it cuts the run short, a free 'truncated' row says so. |
proxyConfiguration | object | no | {"useApifyProxy":false} | Optional and off by default: both venues are public, keyless APIs. |
Pricing
Pay per result (candle) — no charge for empty runs.
Example: 952 candles at $0.0003/result ≈ $0.32.
FAQ
How far back does the history go?
To the day the market opened, unless you set 'From date'. Kalshi's Oregon 2026 governor market ran from 2025-01-26 to 2026-11-04, and the daily candles cover all of it. Kalshi's 2024 Senate and governor markets opened in October 2024, so their history starts there. Polymarket's 2024 presidential market starts in January 2024.
Why do some Kalshi candles have no open, high, low or close?
No trade happened in that period. The bid, ask and open interest are still there, so the candle is kept. Candles from before the market had any price or any holder are dropped and not charged: Oregon's market opened with an empty book for weeks.
What does Polymarket give compared to Kalshi?
Polymarket publishes a price per point in time, so its candles carry 'close' only: no open/high/low, no bid/ask, no open interest, no trade tape here. Its race and candidate rows carry the result and volume like Kalshi's.
How are races found?
Kalshi: by series ticker, by tag (Senate, House, Governor, Primaries, 2028...) or by keyword against its 2,465 election series titles. Polymarket: through its own search, keeping only titles that contain every word of your keyword. Polymarket is read only when you give a keyword, because it has no series tickers or tags in Kalshi's sense.
Are the same races on both venues joined into one row?
No. Each venue's race comes back as its own rows with 'venue' set, because the two venues word and split races differently (Polymarket also lists margins and primaries as separate events). Match them on the title and cycle in your own sheet.
What happens if Kalshi renames its fields again?
The run returns a free row with notice_type 'source_changed' instead of race rows full of empty prices.
Do I need an account or a proxy?
No. Both venues are public, keyless APIs. The proxy input is off by default.
Guides & use cases
Related actors