KalshiAPI is a simple market data API for Kalshi markets. You make a request over HTTP, and you get back JSON. That is the whole idea. If you can call a URL, you can use this API.
The base URL for every request is:
https://kalshiapi.com
With it you can list markets, read full details for a single market, pull hourly price candles, pull individual trades, and download bulk historical data for offline analysis. There is also an MCP server so AI and vibe-coding tools can read the data directly.
Send your API key in the X-API-Key request header. Every endpoint accepts it, and most endpoints require it.
The one exception is /v1/stats, which is public and needs no key. Use it to confirm things are working before you add your key.
Use the public demo key to wire KalshiAPI into your project and get real, live data immediately, no signup. Hit copy on the example below to grab the working key. It's rate-limited per IP and capped to a few rows per call, so it's perfect for building the integration and seeing it work, then get your own free key (1,000 calls/mo) or upgrade for full results.
Grab a free key on the home page, or self-serve from a script or coding agent:
curl -s https://kalshiapi.com/v1/signup \
-H "Content-Type: application/json" \
-d '{"email": "you@example.com", "password": "your-password"}'
The response includes your api_key right away (usable immediately at a reduced rate). We email a verification link; clicking it unlocks the full free tier. password is optional. There's a one-paste setup prompt for coding agents on the home page.
curl https://kalshiapi.com/v1/markets?limit=5 \
-H "X-API-Key: YOUR_KEY"The free key works against sample data. Get one on the homepage. Keep your key private; treat it like a password.
The free tier is built for trying things out and small projects. Paid tiers raise the limits and unlock full data. See the pricing section for current plans.
| Tier | Per minute | Per month | Data |
|---|---|---|---|
| Free | 60 requests | 1,000 requests | Sample data |
| Starter | 600 requests | 100,000 requests | Full data |
| Pro | 1,200 requests | 1,000,000 requests | Full data + live + bulk |
If you go over a limit you get a 429 response. Slow down and retry, or move to a higher tier. Check your live usage anytime with GET /v1/me (returns your tier, monthly quota and calls remaining). An unverified email is capped at 15 requests/minute until you click the verification link.
KalshiAPI is also available on RapidAPI. If you subscribe there, RapidAPI handles your key and billing, just call the endpoints through the RapidAPI host with your RapidAPI key, no separate KalshiAPI key needed.
List and search markets. This is the endpoint you will use most.
GET /v1/markets
| Param | Type | Description |
|---|---|---|
category | string | Filter by category, for example Crypto, Sports, Financials, Climate and Weather, Commodities, Economics, Elections. |
status | string | Filter by market status. |
series | string | Filter by series_ticker. |
event | string | Filter by event_ticker. |
q | string | Text search over the market title. |
resolved | boolean | true or false. |
limit | integer | Results per page. Default 100, max 1000. |
offset | integer | Number of results to skip. Default 0. |
curl "https://kalshiapi.com/v1/markets?category=Crypto&limit=100" \
-H "X-API-Key: YOUR_KEY"{
"total": 1342,
"limit": 100,
"offset": 0,
"markets": [
{
"ticker": "KXBTCD-25DEC31",
"event_ticker": "KXBTCD-25DEC31",
"series_ticker": "KXBTCD",
"category": "Crypto",
"title": "Bitcoin price on Dec 31, 2025",
"yes_sub_title": "Above $100,000",
"status": "active",
"result": "",
"open_time": "2025-01-02T15:00:00Z",
"close_time": "2025-12-31T22:00:00Z",
"volume_fp": 184200,
"open_interest_fp": 52100,
"liquidity_dollars": 14380.55
}
]
}
| Field | Description |
|---|---|
ticker | Unique market ticker, for example KXBTCD-25DEC31. |
event_ticker | Ticker of the event the market belongs to. |
series_ticker | Ticker of the series the market belongs to. |
category | Market category. |
title | Human readable market question. |
yes_sub_title | Short label describing the YES outcome. |
status | Current market status. |
result | Settlement result, empty until resolved. |
open_time | When the market opened. |
close_time | When the market closes. |
volume_fp | Traded volume. |
open_interest_fp | Open interest. |
liquidity_dollars | Resting liquidity in dollars. |
limit fixed and add limit to offset on each request until you have seen total markets.Get the full metadata for one market by its ticker. This returns more than the list view, including rules, strikes, settlement details, and outcome.
GET /v1/markets/{ticker}
curl "https://kalshiapi.com/v1/markets/KXBTCD-25DEC31" \
-H "X-API-Key: YOUR_KEY"The response includes every field from the list view plus the market's full rules, strike information, settlement details, and outcome.
Hourly price candles for a single market. Each candle covers one hour and carries open, high, low, close, plus bid and ask detail and volume.
GET /v1/markets/{ticker}/candles
| Param | Type | Description |
|---|---|---|
start | integer | Start time in epoch seconds. |
end | integer | End time in epoch seconds. |
limit | integer | Maximum number of candles to return. |
format | string | json or parquet. Default json. |
curl "https://kalshiapi.com/v1/markets/KXBTCD-25DEC31/candles?start=1735689600&end=1735776000" \
-H "X-API-Key: YOUR_KEY"{
"ticker": "KXBTCD-25DEC31",
"kind": "candles",
"rows": 24,
"data": [
{
"ts": 1735689600,
"price_open": 61, "price_high": 64, "price_low": 60,
"price_close": 63, "price_mean": 62, "price_previous": 61,
"yes_bid_open": 60, "yes_bid_high": 63, "yes_bid_low": 59, "yes_bid_close": 62,
"yes_ask_open": 62, "yes_ask_high": 65, "yes_ask_low": 61, "yes_ask_close": 64,
"volume": 1820, "open_interest": 50120
}
]
}
| Field | Description |
|---|---|
ts | Candle start time in epoch seconds. |
price_open, price_high, price_low, price_close | Trade price open, high, low, and close for the hour. |
price_mean | Mean trade price for the hour. |
price_previous | Close from the prior candle. |
yes_bid_open/high/low/close | YES bid price over the hour. |
yes_ask_open/high/low/close | YES ask price over the hour. |
volume | Contracts traded during the hour. |
open_interest | Open interest at the candle. |
Individual executed trades for a single market. Use these when you want tick-level detail instead of hourly aggregates.
GET /v1/markets/{ticker}/trades
Same parameters as candles: start and end in epoch seconds, limit, and format (json or parquet, default json).
curl "https://kalshiapi.com/v1/markets/AMAZONFTC-29DEC31/trades?limit=200" \
-H "X-API-Key: YOUR_KEY"| Field | Description |
|---|---|
trade_id | Unique trade identifier. |
created_time | When the trade executed. |
yes_price_dollars | YES price in dollars. |
no_price_dollars | NO price in dollars. |
count_fp | Number of contracts traded. |
taker_side | Side the taker was on. |
taker_outcome_side | Outcome side the taker took. |
is_block_trade | Whether the trade was a block trade. |
ticker | Market ticker for the trade. |
Per-team record across every settled game-winner market on Kalshi. Each team gets a real win/loss record. For pro keys, each team also carries the average YES price the market was charging before the game (avg_implied_pct) and gap_pct — the team's real win rate minus that average price. These are descriptive historical figures; we show the numbers, we don't predict future results.
GET /v1/teams
| Param | Description |
|---|---|
league | Filter to one league, e.g. MLB, English Premier League. |
min_games | Minimum settled games (default 1). Use 30+ for stable calibration. |
sort | games, win_pct, wins, or gap_pct (pro). Default games. |
order | asc or desc (default). |
limit / offset | Pagination. limit 1–1000 (default 100). |
curl "https://kalshiapi.com/v1/teams?league=MLB&min_games=30&sort=gap_pct" \
-H "X-API-Key: YOUR_KEY"{
"total": 31,
"limit": 100,
"offset": 0,
"teams": [
{
"team": "Chicago WS",
"league": "MLB",
"games": 58,
"wins": 34,
"losses": 24,
"win_pct": 58.6,
"avg_implied_pct": 45.9,
"gap_pct": 12.7,
"priced_games": 31
}
]
}
| Field | Description |
|---|---|
team | Team name as Kalshi labels it (the YES side of the matchup). |
league | League the team plays in. |
games / wins / losses | Settled game-winner markets and the record. |
win_pct | Real win rate, percent. |
avg_implied_pct (pro) | Average pre-game YES price the market charged, percent. |
gap_pct (pro) | win_pct minus avg_implied_pct: the historical difference between the team's win rate and the price. Descriptive, not a prediction. |
priced_games (pro) | Games with candle pricing used for calibration. |
Single team: GET /v1/teams/{team} returns one team's row (URL-encode the name, e.g. /v1/teams/Chicago%20WS).
Need the whole history at once? Bulk download gives you full datasets as Parquet files, packaged into a single .zip (unzip on any OS, no tools needed). This is the fastest way to load everything into pandas, Polars, or DuckDB for backtesting and research. Bulk is a paid feature, also available as a one-time purchase on the pricing section.
| Dataset | Contents |
|---|---|
metadata | All market metadata. |
candles | All hourly candles. |
trades | All individual trades. |
full | Everything above in one archive. |
Snapshots are refreshed monthly.
See which datasets exist along with their current snapshot size and freshness.
GET /v1/bulk/catalog
Order a dataset. Large datasets build in the background, so the order starts in a building state and finishes ready.
POST /v1/bulk/order?dataset=full
The response includes the order details:
{
"id": "ord_8f21",
"dataset": "full",
"status": "building"
}
Check the order status. When it is ready the response includes a download_url.
GET /v1/bulk/order/{id}
Download the .zip once the order is ready (the link is private to your purchase and expires).
GET /v1/bulk/download/{id}?token=...
# 1. order
curl -X POST "https://kalshiapi.com/v1/bulk/order?dataset=full" \
-H "X-API-Key: YOUR_KEY"
# 2. poll (repeat until status is ready)
curl "https://kalshiapi.com/v1/bulk/order/ord_8f21" \
-H "X-API-Key: YOUR_KEY"
# 3. download once ready
curl -L "https://kalshiapi.com/v1/bulk/download/ord_8f21?token=TOKEN" \
-H "X-API-Key: YOUR_KEY" -o kalshi-full.tarThe MCP server is the easy way to use this data with AI and vibe-coding tools. It lets assistants like Claude Desktop and Cursor read the data directly while you build, no glue code required. Want a walkthrough? Read the blog post.
The remote endpoint uses streamable HTTP:
https://kalshiapi.com/mcp
It works with no key against sample data, so you can paste the URL into your client and try it instantly. To raise your limits and reach full data, pass your key in the X-API-Key header in your client config.
Add this to your Claude Desktop or Cursor MCP configuration:
{
"mcpServers": {
"kalshiapi": {
"url": "https://kalshiapi.com/mcp",
"headers": { "X-API-Key": "YOUR_KEY" }
}
}
}
Drop the headers block to run keyless on sample data.
| Tool | What it does |
|---|---|
dataset_stats | Dataset coverage and freshness. |
category_yes_rates | YES resolution rates by category. |
list_markets | List and search markets. |
get_market | Full detail for one market. |
get_candles | Hourly candles for a market. |
get_trades | Individual trades for a market. |
For real-time data, open a WebSocket and subscribe to the markets you care about. You'll get a message every time one of your subscribed markets updates, the same firehose that feeds the dataset. Built for live trading bots and dashboards.
Connect to the endpoint and pass your key as a query parameter (browsers can't set headers on a WebSocket):
wss://kalshiapi.com/v1/stream?api_key=YOUR_KEY
On connect you get a welcome message with your tier and ticker limit. Then send a subscribe action with up to 1,000 tickers. The server pushes an update message (with the full market row) whenever a subscribed market changes.
| You send | Server replies / pushes |
|---|---|
{"action":"subscribe","tickers":["KXBTC...","KXNBA..."]} | {"type":"subscribed","count":2,"rejected":0} |
{"action":"unsubscribe","tickers":["KXBTC..."]} | {"type":"unsubscribed","count":1} |
{"action":"ping"} | {"type":"pong"} |
| — | {"type":"update","market":{ …live market row… }} |
const ws = new WebSocket("wss://kalshiapi.com/v1/stream?api_key=YOUR_KEY");
ws.onopen = () => ws.send(JSON.stringify({ action: "subscribe", tickers: ["KXBTCD-25DEC31"] }));
ws.onmessage = (e) => {
const msg = JSON.parse(e.data);
if (msg.type === "update") console.log(msg.market);
};
The API speaks JSON by default. On the candles and trades endpoints you can pass format=parquet to get Apache Parquet instead. Bulk downloads are always Parquet, packaged in a .zip.
Parquet is a columnar format that loads fast and keeps types intact. Read it straight into pandas:
import pandas as pd
df = pd.read_parquet("candles.parquet")
Polars and DuckDB read the same files with pl.read_parquet(...) and SELECT * FROM 'candles.parquet'.
Errors use standard HTTP status codes. Check the code first, then read the JSON body for detail.
| Status | Meaning |
|---|---|
400 | Bad request. A parameter is missing or malformed. |
401 | Invalid or missing API key. Check your X-API-Key header. |
404 | Not found. The ticker or resource does not exist. |
429 | Rate limit exceeded. Slow down or move to a higher tier. |
401 on every call? Confirm the header name is exactly X-API-Key and that your key has no extra spaces. Still stuck? Contact support.