kalshi
Kalshi API
Kalshi is a CFTC-regulated prediction market exchange where users trade on the outcomes of real-world events.
Check this skill and the official documentation FREQUENTLY. The canonical machine-readable index of all docs lives at:
Always consult llms.txt to discover every available page before building. It is the single source of truth for endpoint references, SDK docs, WebSocket channels, FIX protocol details, and getting-started guides.
Key Concepts (Glossary)
| Term | Definition |
|---|---|
| Market | A single binary outcome contract (Yes / No) with prices in cents (1–99¢). |
| Event | A collection of related markets representing a real-world occurrence (e.g., an election, a weather reading). |
| Series | A template for recurring events that share the same structure and rules (e.g., "Daily Highest Temp in NYC"). |
| Orderbook | Bids only — in binary markets a YES bid at X¢ is equivalent to a NO ask at (100−X)¢. |
| Fill | A matched trade on one of your orders. |
| Settlement | The final resolution of a market (Yes or No) after determination. |
| Multivariate Event | A combo event created dynamically from a multivariate event collection. |
Full glossary: https://docs.kalshi.com/getting_started/terms.md
Base URLs
| Environment | REST API | WebSocket |
|---|---|---|
| Production | https://api.elections.kalshi.com/trade-api/v2 |
wss://api.elections.kalshi.com/trade-api/ws/v2 |
| Demo | https://demo-api.kalshi.co/trade-api/v2 |
wss://demo-api.kalshi.co/trade-api/ws/v2 |
Note: Despite the
electionssubdomain, the production URL serves ALL Kalshi markets — economics, weather, tech, entertainment, etc.
Demo environment info: https://docs.kalshi.com/getting_started/demo_env.md
Documentation & References
All detailed examples, request/response schemas, and walkthroughs live in the official docs. Always consult these before building:
Authentication
Kalshi uses RSA-PSS signed requests (not Bearer tokens). Every authenticated request requires three headers:
| Header | Value |
|---|---|
KALSHI-ACCESS-KEY |
Your API Key ID |
KALSHI-ACCESS-TIMESTAMP |
Current Unix timestamp in milliseconds |
KALSHI-ACCESS-SIGNATURE |
Base64-encoded RSA-PSS signature of {timestamp}{METHOD}{path} |
Important: When signing, use the path without query parameters. For example, sign /trade-api/v2/portfolio/orders even if the request URL is /trade-api/v2/portfolio/orders?limit=5.
Generating API Keys
- Log in → Account Settings → API Keys section.
- Click "Create New API Key" to generate an RSA key pair.
- Store the private key immediately — it cannot be retrieved again.
Full guide: https://docs.kalshi.com/getting_started/api_keys.md
Python Signing Example
import base64, datetime, requests
from cryptography.hazmat.primitives import serialization, hashes
from cryptography.hazmat.primitives.asymmetric import padding
def load_private_key(file_path):
with open(file_path, "rb") as f:
return serialization.load_pem_private_key(f.read(), password=None)
def sign_pss(private_key, text: str) -> str:
sig = private_key.sign(
text.encode("utf-8"),
padding.PSS(mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.DIGEST_LENGTH),
hashes.SHA256(),
)
return base64.b64encode(sig).decode("utf-8")
def kalshi_headers(key_id, private_key, method, path):
ts = str(int(datetime.datetime.now().timestamp() * 1000))
path_no_qs = path.split("?")[0]
sig = sign_pss(private_key, ts + method + path_no_qs)
return {
"KALSHI-ACCESS-KEY": key_id,
"KALSHI-ACCESS-SIGNATURE": sig,
"KALSHI-ACCESS-TIMESTAMP": ts,
}
JavaScript Signing Example
const crypto = require("crypto");
const fs = require("fs");
function signPss(privateKeyPem, text) {
const sign = crypto.createSign("RSA-SHA256");
sign.update(text);
sign.end();
return sign.sign({
key: privateKeyPem,
padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
saltLength: crypto.constants.RSA_PSS_SALTLEN_DIGEST,
}).toString("base64");
}
function kalshiHeaders(keyId, privateKeyPem, method, path) {
const ts = Date.now().toString();
const pathNoQs = path.split("?")[0];
const sig = signPss(privateKeyPem, ts + method + pathNoQs);
return {
"KALSHI-ACCESS-KEY": keyId,
"KALSHI-ACCESS-SIGNATURE": sig,
"KALSHI-ACCESS-TIMESTAMP": ts,
};
}
SDKs
Kalshi provides official SDKs. Prefer these for production integrations:
| SDK | Install | Docs |
|---|---|---|
| Python (sync) | pip install kalshi_python_sync |
https://docs.kalshi.com/sdks/python/quickstart.md |
| Python (async) | pip install kalshi_python_async |
https://docs.kalshi.com/sdks/python/quickstart.md |
| TypeScript | npm install kalshi-typescript |
https://docs.kalshi.com/sdks/typescript/quickstart.md |
The old
kalshi-pythonpackage is deprecated. Migrate tokalshi_python_syncorkalshi_python_async.
SDK overview: https://docs.kalshi.com/sdks/overview.md
For production applications, consider generating your own client from the OpenAPI spec.
Rate Limits
| Tier | Read | Write |
|---|---|---|
| Basic | 20/s | 10/s |
| Advanced | 30/s | 30/s |
| Premier | 100/s | 100/s |
| Prime | 400/s | 400/s |
Write-limited endpoints: CreateOrder, CancelOrder, AmendOrder, DecreaseOrder, BatchCreateOrders, BatchCancelOrders. In batch APIs each item counts as 1 transaction (except BatchCancelOrders where each cancel = 0.2 transactions).
Full details: https://docs.kalshi.com/getting_started/rate_limits.md
Pagination
The API uses cursor-based pagination. Responses include a cursor field; pass it as ?cursor={value} on the next request. An empty/null cursor means no more pages. Default page size is 100 (max typically 1000).
Full guide: https://docs.kalshi.com/getting_started/pagination.md
REST API Endpoints Overview
Below is a summary organized by domain. For full request/response schemas, see the linked docs or browse https://docs.kalshi.com/llms.txt.
Markets
| Endpoint | Method | Auth | Description |
|---|---|---|---|
/markets |
GET | No | List markets with filters (status, series, timestamps). Docs: Get Markets |
/markets/{ticker} |
GET | No | Get a single market by ticker. Docs: Get Market |
/markets/{ticker}/orderbook |
GET | No | Get current orderbook (yes bids + no bids). Docs: Get Market Orderbook |
/markets/{ticker}/candlesticks |
GET | No | Candlestick data (1m, 1h, 1d). Docs: Get Market Candlesticks |
/markets/candlesticks |
POST | No | Batch candlesticks for up to 100 tickers. Docs: Batch Get Market Candlesticks |
/markets/trades |
GET | No | All public trades. Docs: Get Trades |
/series/{ticker} |
GET | No | Get a series. Docs: Get Series |
/series |
GET | No | List series. Docs: Get Series List |
Events
| Endpoint | Method | Auth | Description |
|---|---|---|---|
/events |
GET | No | List events (excludes multivariate). Docs: Get Events |
/events/{ticker} |
GET | No | Get a single event. Docs: Get Event |
/events/{ticker}/metadata |
GET | No | Event metadata only. Docs: Get Event Metadata |
/events/{ticker}/candlesticks |
GET | No | Aggregated event candlesticks. Docs: Get Event Candlesticks |
/events/{ticker}/forecast/percentile_history |
GET | No | Forecast percentile history. Docs: Get Event Forecast Percentile History |
/events/multivariate |
GET | No | List multivariate events. Docs: Get Multivariate Events |
Orders (Authenticated)
| Endpoint | Method | Auth | Description |
|---|---|---|---|
/portfolio/orders |
POST | Yes | Create an order. Docs: Create Order |
/portfolio/orders |
GET | Yes | List your orders (resting/canceled/executed). Docs: Get Orders |
/portfolio/orders/{order_id} |
GET | Yes | Get a single order. Docs: Get Order |
/portfolio/orders/{order_id} |
DELETE | Yes | Cancel an order. Docs: Cancel Order |
/portfolio/orders/{order_id}/amend |
POST | Yes | Amend price/count. Docs: Amend Order |
/portfolio/orders/{order_id}/decrease |
POST | Yes | Decrease contract count. Docs: Decrease Order |
/portfolio/orders/{order_id}/queue_position |
GET | Yes | Queue position. Docs: Get Order Queue Position |
/portfolio/orders/queue_positions |
GET | Yes | Queue positions for all resting orders. Docs: Get Queue Positions for Orders |
/portfolio/orders/batched |
POST | Yes | Batch create (up to 20). Docs: Batch Create Orders |
/portfolio/orders/batched |
DELETE | Yes | Batch cancel (up to 20). Docs: Batch Cancel Orders |
Portfolio (Authenticated)
| Endpoint | Method | Auth | Description |
|---|---|---|---|
/portfolio/balance |
GET | Yes | Account balance (cents). Docs: Get Balance |
/portfolio/positions |
GET | Yes | Market positions. Docs: Get Positions |
/portfolio/fills |
GET | Yes | Your fills. Docs: Get Fills |
/portfolio/settlements |
GET | Yes | Settlement history. Docs: Get Settlements |
/portfolio/resting_order_value |
GET | Yes | Total resting order value (FCM). Docs: Get Total Resting Order Value |
Subaccounts (Authenticated)
| Endpoint | Method | Auth | Description |
|---|---|---|---|
/portfolio/subaccounts |
POST | Yes | Create subaccount (max 32). Docs: Create Subaccount |
/portfolio/subaccounts/balances |
GET | Yes | All subaccount balances. Docs: Get All Subaccount Balances |
/portfolio/subaccounts/transfers |
GET | Yes | Subaccount transfer history. Docs: Get Subaccount Transfers |
/portfolio/subaccounts/transfer |
POST | Yes | Transfer between subaccounts. Docs: Transfer Between Subaccounts |
Order Groups (Authenticated)
Order groups apply a rolling 15-second contracts limit; when hit, all orders in the group are auto-cancelled.
| Endpoint | Method | Auth | Description |
|---|---|---|---|
/portfolio/order_groups |
POST | Yes | Create group. Docs: Create Order Group |
/portfolio/order_groups |
GET | Yes | List groups. Docs: Get Order Groups |
/portfolio/order_groups/{id} |
GET | Yes | Get group. Docs: Get Order Group |
/portfolio/order_groups/{id} |
DELETE | Yes | Delete group. Docs: Delete Order Group |
/portfolio/order_groups/{id}/reset |
POST | Yes | Reset counter. Docs: Reset Order Group |
/portfolio/order_groups/{id}/trigger |
POST | Yes | Manually trigger. Docs: Trigger Order Group |
/portfolio/order_groups/{id}/limit |
PUT | Yes | Update limit. Docs: Update Order Group Limit |
Communications / RFQ (Authenticated)
| Endpoint | Method | Auth | Description |
|---|---|---|---|
/communications/id |
GET | Yes | Get your comms ID. Docs: Get Communications ID |
/rfqs |
POST | Yes | Create RFQ (max 100 open). Docs: Create RFQ |
/rfqs |
GET | Yes | List RFQs. Docs: Get RFQs |
/rfqs/{id} |
GET | Yes | Get RFQ. Docs: Get RFQ |
/rfqs/{id} |
DELETE | Yes | Delete RFQ. Docs: Delete RFQ |
/quotes |
POST | Yes | Create quote. Docs: Create Quote |
/quotes |
GET | Yes | List quotes. Docs: Get Quotes |
/quotes/{id} |
GET | Yes | Get quote. Docs: Get Quote |
/quotes/{id} |
DELETE | Yes | Delete quote. Docs: Delete Quote |
/quotes/{id}/accept |
POST | Yes | Accept quote. Docs: Accept Quote |
/quotes/{id}/confirm |
POST | Yes | Confirm quote. Docs: Confirm Quote |
Historical Data (Authenticated)
Historical data is for archived markets, orders, and fills that have moved past cutoff timestamps.
| Endpoint | Method | Auth | Description |
|---|---|---|---|
/historical/cutoff_timestamps |
GET | Yes | Cutoff boundaries. Docs: Get Historical Cutoff Timestamps |
/historical/markets |
GET | Yes | Archived markets. Docs: Get Historical Markets |
/historical/markets/{ticker} |
GET | Yes | Single archived market. Docs: Get Historical Market |
/historical/markets/{ticker}/candlesticks |
GET | Yes | Archived candlesticks. Docs: Get Historical Market Candlesticks |
/historical/fills |
GET | Yes | Archived fills. Docs: Get Historical Fills |
/historical/orders |
GET | Yes | Archived orders. Docs: Get Historical Orders |
Historical data guide: https://docs.kalshi.com/getting_started/historical_data.md
Multivariate Collections
| Endpoint | Method | Auth | Description |
|---|---|---|---|
/multivariate/collections |
GET | No | List collections. Docs: Get Multivariate Event Collections |
/multivariate/collections/{ticker} |
GET | No | Get collection. Docs: Get Multivariate Event Collection |
/multivariate/collections/{ticker}/markets |
POST | Yes | Create market in collection. Docs: Create Market In Multivariate Event Collection |
/multivariate/collections/{ticker}/lookup |
GET | No | Lookup tickers. Docs: Lookup Tickers For Market In Multivariate Event Collection |
/multivariate/collections/{ticker}/lookup_history |
GET | No | Recent lookups. Docs: Get Multivariate Event Collection Lookup History |
Milestones & Structured Targets
| Endpoint | Method | Auth | Description |
|---|---|---|---|
/milestones |
GET | No | List milestones. Docs: Get Milestones |
/milestones/{id} |
GET | No | Get milestone. Docs: Get Milestone |
/structured_targets |
GET | No | List structured targets. Docs: Get Structured Targets |
/structured_targets/{id} |
GET | No | Get structured target. Docs: Get Structured Target |
Live Data
| Endpoint | Method | Auth | Description |
|---|---|---|---|
/live_data/{milestone_id} |
GET | No | Live data for milestone. Docs: Get Live Data |
/live_data |
GET | No | Multiple milestones. Docs: Get Multiple Live Data |
Search & Discovery
| Endpoint | Method | Auth | Description |
|---|---|---|---|
/search/sports/filters |
GET | No | Sport filters. Docs: Get Filters for Sports |
/search/tags |
GET | No | Tags by category. Docs: Get Tags for Series Categories |
Exchange & Account
| Endpoint | Method | Auth | Description |
|---|---|---|---|
/exchange/status |
GET | No | Exchange status. Docs: Get Exchange Status |
/exchange/schedule |
GET | No | Exchange schedule. Docs: Get Exchange Schedule |
/exchange/announcements |
GET | No | Announcements. Docs: Get Exchange Announcements |
/exchange/data_timestamp |
GET | Yes | Data freshness timestamp. Docs: Get User Data Timestamp |
/exchange/series_fee_changes |
GET | No | Fee changes. Docs: Get Series Fee Changes |
/account/api_limits |
GET | Yes | Your rate limit tier. Docs: Get Account API Limits |
/account/api_keys |
GET | Yes | List API keys. Docs: Get API Keys |
/account/api_keys |
POST | Yes | Create API key. Docs: Create API Key |
/account/api_keys/generate |
POST | Yes | Generate key pair. Docs: Get API Keys |
/account/api_keys/{id} |
DELETE | Yes | Delete API key. Docs: Delete API Key |
Incentive Programs
| Endpoint | Method | Auth | Description |
|---|---|---|---|
/incentives |
GET | No | List incentives. Docs: Get Incentives |
WebSocket API
A single authenticated WebSocket connection provides real-time streaming. Subscribe to channels by sending JSON commands.
Connection
wss://api.elections.kalshi.com/trade-api/ws/v2 (production)
wss://demo-api.kalshi.co/trade-api/ws/v2 (demo)
Authentication headers (KALSHI-ACCESS-KEY, KALSHI-ACCESS-SIGNATURE, KALSHI-ACCESS-TIMESTAMP) must be included during the handshake. Sign the string {timestamp}GET/trade-api/ws/v2.
Available Channels
| Channel | Auth Required | Description | Docs |
|---|---|---|---|
ticker |
No* | Market price/volume/OI updates | Market Ticker |
trade |
No* | Public trade notifications | Public Trades |
market_lifecycle_v2 |
No* | Market/event state changes | Market & Event Lifecycle |
multivariate |
No* | Multivariate lookup notifications | Multivariate Lookups |
orderbook_delta |
Yes | Real-time orderbook updates | Orderbook Updates |
fill |
Yes | Your fill notifications | User Fills |
order |
Yes | Your order updates | User Orders |
market_positions |
Yes | Your position updates | Market Positions |
communications |
Yes | RFQ/quote notifications | Communications |
order_group_updates |
Yes | Order group lifecycle | Order Group Updates |
*The connection itself always requires authentication, even for public channels.
Subscribe Command
{
"id": 1,
"cmd": "subscribe",
"params": {
"channels": ["ticker", "orderbook_delta"],
"market_tickers": ["KXHIGHNY-24JAN01-T60"]
}
}
Keep-Alive
Kalshi sends Ping frames every 10 seconds with body heartbeat. Clients must respond with Pong frames. Docs: Connection Keep-Alive
Full WebSocket guide: https://docs.kalshi.com/getting_started/quick_start_websockets.md
Orderbook Structure
Kalshi orderbooks return bids only (no asks). In binary markets:
- A YES bid at X¢ = a NO ask at (100−X)¢
- A NO bid at Y¢ = a YES ask at (100−Y)¢
Each entry is [price, quantity] sorted ascending. The best bid is the last element.
Spread calculation:
- Best YES ask =
100 - best_no_bid - Spread = Best YES ask − Best YES bid
Full guide: https://docs.kalshi.com/getting_started/orderbook_responses.md
FIX Protocol
Kalshi also supports the Financial Information eXchange (FIX) protocol for low-latency trading:
| Topic | Docs |
|---|---|
| Overview | https://docs.kalshi.com/fix/index.md |
| Connectivity | https://docs.kalshi.com/fix/connectivity.md |
| Session Management | https://docs.kalshi.com/fix/session-management.md |
| Order Entry | https://docs.kalshi.com/fix/order-entry.md |
| Order Groups | https://docs.kalshi.com/fix/order-groups.md |
| Drop Copy | https://docs.kalshi.com/fix/drop-copy.md |
| Market Settlement | https://docs.kalshi.com/fix/market-settlement.md |
| RFQ Messages | https://docs.kalshi.com/fix/rfq-messages.md |
| Error Handling | https://docs.kalshi.com/fix/error-handling.md |
| Subpenny Pricing | https://docs.kalshi.com/fix/subpenny-pricing.md |
Common Patterns
Fetch All Open Markets for a Series
curl -s "https://api.elections.kalshi.com/trade-api/v2/markets?series_ticker=KXHIGHNY&status=open" | jq '.markets[] | {ticker, title, yes_price, volume}'
Get Orderbook for a Market
curl -s "https://api.elections.kalshi.com/trade-api/v2/markets/KXHIGHNY-24JAN01-T60/orderbook" | jq '.orderbook'
Paginate Through All Results
# First page
curl -s "https://api.elections.kalshi.com/trade-api/v2/markets?limit=100" > page1.json
# Extract cursor for next page
CURSOR=$(jq -r '.cursor' page1.json)
# Next page
curl -s "https://api.elections.kalshi.com/trade-api/v2/markets?limit=100&cursor=$CURSOR" > page2.json
Usage Tips
- Always check
llms.txtfirst: https://docs.kalshi.com/llms.txt has every endpoint and guide. - Use the demo environment for testing — credentials are separate from production.
- Never hardcode API keys — use environment variables or secure key storage.
- Prices are in cents (1–99). Monetary values in WebSocket position channels are in centi-cents (divide by 10,000 for dollars).
- Sign paths without query parameters. Strip everything after
?before signing. - Handle pagination — always check for a non-null
cursorand loop until exhausted. - Respect rate limits — implement exponential backoff on 429 responses.
- Combine REST + WebSocket for the most accurate state: use REST for initial snapshots and WebSocket for real-time deltas.
- Orderbook is bids-only — derive asks via the 100−price complement.
- Historical vs. Live data: Check cutoff timestamps to know whether to query live or historical endpoints.
- For market status values, use:
unopened,open,closed,settled. - Batch operations count against per-second write limits per item.
Error Handling
Standard HTTP error codes apply. The API returns JSON error bodies with descriptive messages. Implement retry with backoff for 429 (rate limited) and 5xx (server errors).
WebSocket Error Codes
| Code | Meaning |
|---|---|
| 1 | Unable to process message |
| 2 | Params required |
| 3 | Channels required |
| 5 | Unknown command |
| 8 | Unknown channel name |
| 9 | Authentication required |
| 14 | Market ticker required |
| 16 | Market not found |
| 17 | Internal error |
| 18 | Command timeout |
Full error code table: https://docs.kalshi.com/getting_started/quick_start_websockets.md
Example Project: Alph Bot
Alph Bot is an open-source automated trading bot that demonstrates a production-quality integration of the Kalshi API alongside Shipp for real-time sports data and Claude AI for probability estimation.
How Alph Bot Uses Kalshi
- Market discovery — Searches for Kalshi event contracts related to a live sports game (e.g., NBA point spreads, totals).
- Orderbook reading — Fetches orderbook data to determine current market-implied probabilities (prices in cents map directly to implied %).
- Edge detection — Compares Kalshi market prices against AI-estimated probabilities powered by Claude + Shipp real-time game data.
- Order execution — Places limit orders when sufficient edge is found, using Kelly Criterion for position sizing.
- Risk management — Enforces circuit breakers (max daily loss), position size limits, single-market exposure caps, and minimum account balance thresholds.
Alph Bot's Kalshi Configuration
From its .env.example:
ALPH_BOT_KALSHI_API_KEY_ID=abc123
ALPH_BOT_KALSHI_PRIVATE_KEY_PATH=./keys/kalshi-private.pem
# Strategy
ALPH_BOT_MIN_EDGE_PCT=5
ALPH_BOT_MIN_CONFIDENCE=medium
ALPH_BOT_KELLY_FRACTION=0.25
# Risk controls
ALPH_BOT_MAX_TOTAL_EXPOSURE_USD=10000
ALPH_BOT_MAX_POSITION_SIZE_USD=1000
ALPH_BOT_MAX_SINGLE_MARKET_PERCENT=20
ALPH_BOT_MAX_DAILY_LOSS_USD=500
ALPH_BOT_MAX_DAILY_TRADES=50
ALPH_BOT_MIN_ACCOUNT_BALANCE_USD=100
Try It
git clone https://gitlab.com/outsharp/shipp/alph-bot.git
cd alph-bot
cp .env.example .env
# Add your Kalshi, Shipp, and Anthropic API keys
yarn migrate
# Find a game to trade on
./index.ts available-games --sport NBA
# Run value betting in demo mode (uses Kalshi demo environment)
./index.ts value-bet -d --game <GAME_ID>
# Run in paper mode (no real orders executed)
./index.ts value-bet --paper --game <GAME_ID>
Warning: Trading on Kalshi involves real money when not in demo/paper mode. Always start with a demo account.
See the Alph Bot README for full setup instructions.
Versioning
The current API version is v2 under /trade-api/v2. Monitor the API Changelog for updates. SDK versions align with the OpenAPI spec and are generally published weekly.