Kalshi

7 tables

Kalshi's prediction-market exchange — binary YES/NO contracts on NBA games (winner, spread, and total markets) from the 2025-26 season onward, with event-driven price candles, the public trade tape, and on-exchange settlement results.

Machine-readable spec: /api/openapi/public.json (OpenAPI 3.1)

Main· 3 tables

serieskalshi.series
Families of related Kalshi binary markets organized around a common theme — all NBA game-winner markets, all NBA spread markets, all NFL totals markets, election markets, weather markets. Tracks series-level metadata and sync state.
Fields9
FieldTypeReferencesDescription
idkeybigintPrimary Key
categorystringe.g. "Sports > Basketball > NBA"
e.g. Sports > Basketball > NBA
contract_typestringnullable"gw", "spread", "ou"
Values:gwspreadou
fetched_attimestamptz
e.g. 2026-06-14T18:01:14.930Z
frequencystringnullable"daily", "weekly"
e.g. — (all-null in sample)
is_activeboolean
e.g. true
league_codestringnullablee.g. "nba", "nfl", null for non-sport
e.g. nba
series_tickerstringe.g. "KXNBAGAME"
Values:KXNBAGAMEKXNBASPREADKXNBATOTAL
titlestring
Values:NBA Game WinnerNBA SpreadNBA Total
GET/api/v1/kalshi/series
Parameters
limitqueryintegeroptionaldefault 50
Page size. Defaults to 50; max 200.
from_idquerybigintoptional
Return rows with id strictly greater than this value; page using the previous response's `next_from_id`. Omit on the first page.
Restores the documented defaults (trial keys only accept the unchanged defaults).
Request
curl -sS \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  'https://api.stat-api.com/api/v1/kalshi/series?limit=3'
Responses
200series rows matching the declared filter set, wrapped in { series, limit, next_from_id }. next_from_id is the last row's id on a full page — pass it back via ?from_id= to walk the full result; null marks the terminal page.application/jsonshow example ▸
400No declared filter set was satisfied — response body matches MissingRequiredFiltersError; pick one combo from the accepted-sets hint and resend.application/json
GET/api/v1/kalshi/series/{id}
Parameters
idpathbigintrequired
Primary key (id) of the sery row.
Restores the documented defaults (trial keys only accept the unchanged defaults).
Request
curl -sS \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  'https://api.stat-api.com/api/v1/kalshi/series/{pk_value}'
Responses
200Single sery row.application/jsonshow example ▸
404Row not found.
eventskalshi.events
A real-world event that anchors a set of Kalshi markets — for sports, a specific game or matchup; for politics, an election or vote; for finance, a price level on a target date. E.g., MEM@POR game-winner event has 2 markets (MEM wins, POR wins).
Fields15
FieldTypeReferencesDescription
idkeybigintPrimary Key
competition_idbigintnullablePolymorphic cross-schema ref — {league}.games (team sports) or golf.tournaments (golf), resolved during sync by league_code. Dereference via main.competitions. NULL until linked.
league_game_idstringnullableNBA CDN game ID for fallback cross-ref
e.g. — (all-null in sample)
series_idbigint
e.g. 1
away_teamstringnullableTricode: "MEM"
e.g. SAS
categorystring
e.g. Sports > Basketball > NBA
event_tickerstringe.g. "KXNBAGAME-26FEB06MEMPOR"
e.g. KXNBAGAME-26FEB12DALLAL
fetched_attimestamptz
e.g. 2026-03-30T01:56:47.997Z
game_datedatenullableGame date for sport events
e.g. 2026-04-10T04:00:00.000Z
home_teamstringnullableTricode: "POR"
e.g. SAS
league_codestringnullableDenormalized for filtering
e.g. nba
market_countintegernullable
e.g. 11
settled_attimestamptznullable
e.g. — (all-null in sample)
statusstring"open", "closed", "settled"
e.g. closed
titlestring
e.g. Toronto wins by over 8.5 Points?
GET/api/v1/kalshi/events
Requires one of: series_id or league_code or competition_id — requests satisfying none of these return 400.
Parameters
series_idquerybigintoptional
Filter to events within a single Kalshi series (e.g., one row of `series` ⇒ all KXNBAGAME events).
competition_idquerybigintoptional
Filter to events linked to a specific competition (cross-schema game or golf tournament; resolved during sync via league_code).
league_codequerystringoptional
Filter to all events for a league (e.g. league_code=nba) — the league-level entry point, mirroring polymarket.events.
limitqueryintegeroptionaldefault 50
Page size. Defaults to 50; max 200.
from_idquerybigintoptional
Return rows with id strictly greater than this value; page using the previous response's `next_from_id`. Omit on the first page.
Restores the documented defaults (trial keys only accept the unchanged defaults).
Request
curl -sS \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  'https://api.stat-api.com/api/v1/kalshi/events?series_id=1'
Responses
200events rows matching the declared filter set, wrapped in { events, limit, next_from_id }. next_from_id is the last row's id on a full page — pass it back via ?from_id= to walk the full result; null marks the terminal page.application/jsonshow example ▸
400No declared filter set was satisfied — response body matches MissingRequiredFiltersError; pick one combo from the accepted-sets hint and resend.application/json
GET/api/v1/kalshi/events/{id}
Parameters
idpathbigintrequired
Primary key (id) of the event row.
Restores the documented defaults (trial keys only accept the unchanged defaults).
Request
curl -sS \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  'https://api.stat-api.com/api/v1/kalshi/events/{pk_value}'
Responses
200Single event row.application/jsonshow example ▸
404Row not found.
marketskalshi.markets
An individual binary contract on Kalshi — a YES/NO question with a defined resolution date and rule, priced between 0 and 100 cents. Stores latest known state (overwritten on sync).
Fields23
FieldTypeReferencesDescription
idkeybigintPrimary Key
competition_idbigintnullablePolymorphic cross-schema ref (denormalized from event) — {league}.games or golf.tournaments, by league_code. Dereference via main.competitions. NULL until linked.
event_idbigint
e.g. 66
series_idbigintDenormalized from event for direct queries
e.g. 2
close_timetimestamptznullable
e.g. 2026-02-27T03:00:00.000Z
contract_typestring"gw", "spread", "ou"
Values:spreadgwou
expiration_timetimestamptznullable
e.g. 2026-02-26T00:30:00.000Z
fetched_attimestamptz
e.g. 2026-02-23T05:31:45.400Z
last_priceintegernullableCents (1-99)
e.g. 1
linedecimalnullableSpread line for spread markets (e.g., -3.5). UNRELIABLE for ou (totals) markets — mostly null, and populated values hold the playoff series game number, not the total; parse the numeric ticker suffix (e.g. …-238 ⇒ 238) for the O/U line.
e.g. 1.5000
liquidityinteger
e.g. 0
open_interestinteger
e.g. 1304
open_timetimestamptznullable
e.g. 2026-02-07T02:07:00.000Z
resultstringnullable"yes", "no", "scalar", null if undetermined
Values:noyesscalar
settled_attimestamptznullableSettlement timestamp from Kalshi settlement_ts
e.g. 2026-01-20T02:37:00.941Z
settlement_valueintegernullableSettlement payout for YES side in cents (0-100 for binary, 1-99 possible for scalar)
e.g. 0
statusstring"initialized", "inactive", "active", "closed", "determined", "disputed", "amended", "finalized"
e.g. finalized
subtitlestringnullable
e.g. — (all-null in sample)
tickerstringe.g. "KXNBAGAME-26FEB06MEMPOR-POR"
e.g. KXNBAGAME-26FEB12DALLAL-LAL
titlestring
e.g. Los Angeles C at Houston: Total Points
volumeintegerLifetime volume
e.g. 2226
yes_askintegernullableCents. 1-99 while an order book exists; 100 when there is no resting ask (the common state on settled markets).
e.g. 100
yes_bidintegernullableCents. 1-99 while an order book exists; 0 when there is no resting bid (the common state on settled markets).
e.g. 0
GET/api/v1/kalshi/markets
Requires one of: event_id or competition_id — requests satisfying none of these return 400.
Parameters
event_idquerybigintoptional
Filter to all markets that belong to a single event (e.g., MEM@POR game-winner event ⇒ two markets, MEM wins and POR wins).
series_idquerybigintoptional
Filter to all markets in a single series (denormalized for direct queries).
competition_idquerybigintoptional
Filter to markets linked to a specific competition — all of its markets in one call.
limitqueryintegeroptionaldefault 50
Page size. Defaults to 50; max 200.
from_idquerybigintoptional
Return rows with id strictly greater than this value; page using the previous response's `next_from_id`. Omit on the first page.
Restores the documented defaults (trial keys only accept the unchanged defaults).
Request
curl -sS \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  'https://api.stat-api.com/api/v1/kalshi/markets?event_id=1'
Responses
200markets rows matching the declared filter set, wrapped in { markets, limit, next_from_id }. next_from_id is the last row's id on a full page — pass it back via ?from_id= to walk the full result; null marks the terminal page.application/jsonshow example ▸
400No declared filter set was satisfied — response body matches MissingRequiredFiltersError; pick one combo from the accepted-sets hint and resend.application/json
GET/api/v1/kalshi/markets/{id}
Parameters
idpathbigintrequired
Primary key (id) of the market row.
Restores the documented defaults (trial keys only accept the unchanged defaults).
Request
curl -sS \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  'https://api.stat-api.com/api/v1/kalshi/markets/{pk_value}'
Responses
200Single market row.application/jsonshow example ▸
404Row not found.

Markets· 4 tables

market_statuseskalshi.market_statuses
The trading-status period log for each Kalshi market — one row per state change with start/end timestamps, recording when the market was open, closed, settled, etc. Sparse coverage — captured 2025-10 through 2026-02 for ~1,500 markets; not currently updating.
Fields6
FieldTypeReferencesDescription
idkeybigintPrimary Key
detailsjsonbnullable
end_tstimestamptz
start_tstimestamptz
statusstring"open", "closed", "settled", etc.
e.g. — (all-null in sample)
tickerstring
e.g. — (all-null in sample)
GET/api/v1/kalshi/market_statuses
Requires one of: market_id — requests satisfying none of these return 400.
Parameters
market_idquerybigintoptional
Filter to status periods for a single Kalshi market by numeric id (resolves to ticker via JOIN to kalshi.markets).
limitqueryintegeroptionaldefault 50
Page size. Defaults to 50; max 200.
from_idquerybigintoptional
Return rows with id strictly greater than this value; page using the previous response's `next_from_id`. Omit on the first page.
Restores the documented defaults (trial keys only accept the unchanged defaults).
Request
curl -sS \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  'https://api.stat-api.com/api/v1/kalshi/market_statuses?market_id=1'
Responses
200market_statuses rows matching the declared filter set, wrapped in { market_statuses, limit, next_from_id }. next_from_id is the last row's id on a full page — pass it back via ?from_id= to walk the full result; null marks the terminal page.application/jsonshow example ▸
400No declared filter set was satisfied — response body matches MissingRequiredFiltersError; pick one combo from the accepted-sets hint and resend.application/json
GET/api/v1/kalshi/market_statuses/{id}
Parameters
idpathbigintrequired
Primary key (id) of the market_statuse row.
Restores the documented defaults (trial keys only accept the unchanged defaults).
Request
curl -sS \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  'https://api.stat-api.com/api/v1/kalshi/market_statuses/{pk_value}'
Responses
200Single market_statuse row.application/jsonshow example ▸
404Row not found.
candleskalshi.candles
OHLC candlestick price summaries for Kalshi markets — the open, high, low, and close prices over standard time bins (1 minute, 5 minute, 1 hour, 1 day), with traded volume per bin. Flattened columns (not JSON) for SQL queryability.
Fields15
FieldTypeReferencesDescription
idkeybigintPrimary Key
market_idbigint
e.g. 8
end_period_tsintegerUnix timestamp of period end
e.g. 1770781714
open_interestintegerOI at period end
e.g. 0
period_intervalintegerSeconds (1, 60, 3600, 86400)
e.g. 1
tickerstringDenormalized
Values:KXNBAGAME-26FEB11MEMDEN…KXNBAGAME-26FEB11OKCPHX…
volumeintegerPeriod volume
e.g. 1
yes_ask_closeintegernullable
e.g. 86
yes_ask_highintegernullable
e.g. 86
yes_ask_lowintegernullable
e.g. 30
yes_ask_openintegernullable
e.g. 86
yes_bid_closeintegernullable
e.g. 86
yes_bid_highintegernullable
e.g. 86
yes_bid_lowintegernullable
e.g. 30
yes_bid_openintegernullableCents
e.g. 86
GET/api/v1/kalshi/candles
Requires one of: market_id — requests satisfying none of these return 400.
Parameters
market_idquerybigintoptional
Filter to candles for a single Kalshi market.
period_intervalqueryintegeroptional
Bin width in seconds. Currently every row is period_interval=1 (event-driven captures at up-to-1s resolution, irregular spacing) — resample client-side for fixed OHLC bins.
limitqueryintegeroptionaldefault 50
Page size. Defaults to 50; max 200.
from_idquerybigintoptional
Return rows with id strictly greater than this value; page using the previous response's `next_from_id`. Omit on the first page.
Restores the documented defaults (trial keys only accept the unchanged defaults).
Request
curl -sS \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  'https://api.stat-api.com/api/v1/kalshi/candles?market_id=1'
Responses
200candles rows matching the declared filter set, wrapped in { candles, limit, next_from_id }. next_from_id is the last row's id on a full page — pass it back via ?from_id= to walk the full result; null marks the terminal page.application/jsonshow example ▸
400No declared filter set was satisfied — response body matches MissingRequiredFiltersError; pick one combo from the accepted-sets hint and resend.application/json
GET/api/v1/kalshi/candles/{id}
Parameters
idpathbigintrequired
Primary key (id) of the candle row.
Restores the documented defaults (trial keys only accept the unchanged defaults).
Request
curl -sS \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  'https://api.stat-api.com/api/v1/kalshi/candles/{pk_value}'
Responses
200Single candle row.application/jsonshow example ▸
404Row not found.
market_snapshotskalshi.market_snapshots
Time-series price captures for Kalshi markets — the YES bid/ask and NO bid/ask, last trade price, and order-book depth recorded as prices move throughout the trading session. Same pattern as nba.game_line.
Fields11
FieldTypeReferencesDescription
idkeybigintPrimary Key
market_idbigint
e.g. — (all-null in sample)
captured_attimestamptz
e.g. — (all-null in sample)
is_openingbooleanFirst snapshot for this market
e.g. — (all-null in sample)
last_priceintegernullable
e.g. — (all-null in sample)
liquidityinteger
e.g. — (all-null in sample)
open_interestinteger
e.g. — (all-null in sample)
tickerstringDenormalized for efficient queries
e.g. — (all-null in sample)
volumeintegerCumulative at capture time
e.g. — (all-null in sample)
yes_askintegernullable
e.g. — (all-null in sample)
yes_bidintegernullable
e.g. — (all-null in sample)
⚠️ This endpoint is documented but not yet live. Calls return 503 until data is wired in.
GET/api/v1/kalshi/market_snapshots
Requires one of: market_id — requests satisfying none of these return 400.
Parameters
market_idquerybigintoptional
Filter to snapshots for a single Kalshi market.
limitqueryintegeroptionaldefault 50
Page size. Defaults to 50; max 200.
from_idquerybigintoptional
Return rows with id strictly greater than this value; page using the previous response's `next_from_id`. Omit on the first page.
Restores the documented defaults (trial keys only accept the unchanged defaults).
Request
curl -sS \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  'https://api.stat-api.com/api/v1/kalshi/market_snapshots?market_id=1'
Responses
200market_snapshots rows matching the declared filter set, wrapped in { market_snapshots, limit, next_from_id }. next_from_id is the last row's id on a full page — pass it back via ?from_id= to walk the full result; null marks the terminal page.application/jsonshow example ▸
400No declared filter set was satisfied — response body matches MissingRequiredFiltersError; pick one combo from the accepted-sets hint and resend.application/json
503Coming soon — handler returns 503 until data is wired in.
GET/api/v1/kalshi/market_snapshots/{id}
Parameters
idpathbigintrequired
Primary key (id) of the market_snapshot row.
Restores the documented defaults (trial keys only accept the unchanged defaults).
Request
curl -sS \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  'https://api.stat-api.com/api/v1/kalshi/market_snapshots/{pk_value}'
Responses
200Single market_snapshot row.application/jsonshow example ▸
404Row not found.
503Coming soon — handler returns 503 until data is wired in.
public_tradeskalshi.public_trades
Every executed trade on Kalshi — price, size, side (YES or NO), and timestamp, comprising the public market tape. Represents completed market trades across all users.
Fields14
FieldTypeReferencesDescription
idkeybigintPrimary Key
market_idbigint
e.g. 900
trade_idstring
e.g. befe59d2-106d-6057-517e-eeddd083eba6
countinteger
e.g. 1
count_fpstringnullable
e.g. 1.00
created_timetimestamptznullable
e.g. 2026-02-13T05:23:27.794Z
no_priceintegernullable
e.g. 1
no_price_dollarsstringnullable
e.g. 0.0100
pricefloatnullable
e.g. 0.99
raw_payloadjsonb
e.g. {"count":18,"count_fp":"18.00","created_time":"2026-02-13T0…
taker_sidestringnullable"yes" | "no"
Values:noyes
tickerstring
e.g. KXNBATOTAL-26FEB12DALLAL-229
yes_priceintegernullable
e.g. 99
yes_price_dollarsstringnullable
e.g. 0.9900
GET/api/v1/kalshi/public_trades
Requires one of: market_id — requests satisfying none of these return 400.
Parameters
market_idquerybigintoptional
Filter to trades on a single Kalshi market.
limitqueryintegeroptionaldefault 50
Page size. Defaults to 50; max 200.
from_idquerybigintoptional
Return rows with id strictly greater than this value; page using the previous response's `next_from_id`. Omit on the first page.
Restores the documented defaults (trial keys only accept the unchanged defaults).
Request
curl -sS \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  'https://api.stat-api.com/api/v1/kalshi/public_trades?market_id=1'
Responses
200public_trades rows matching the declared filter set, wrapped in { public_trades, limit, next_from_id }. next_from_id is the last row's id on a full page — pass it back via ?from_id= to walk the full result; null marks the terminal page.application/jsonshow example ▸
400No declared filter set was satisfied — response body matches MissingRequiredFiltersError; pick one combo from the accepted-sets hint and resend.application/json
GET/api/v1/kalshi/public_trades/{id}
Parameters
idpathbigintrequired
Primary key (id) of the public_trade row.
Restores the documented defaults (trial keys only accept the unchanged defaults).
Request
curl -sS \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  'https://api.stat-api.com/api/v1/kalshi/public_trades/{pk_value}'
Responses
200Single public_trade row.application/jsonshow example ▸
404Row not found.