What the Kalshi API covers
Kalshi runs one Trade API with three ways in. REST covers market data, your portfolio and order entry. A WebSocket feed streams book changes, fills and positions. A FIX interface covers order entry, market data and drop copy. Production and demo are separate environments with these base URLs. A key made in one fails in the other.
Production REST https://external-api.kalshi.com/trade-api/v2
Production WebSocket wss://external-api-ws.kalshi.com/trade-api/ws/v2
Demo REST https://external-api.demo.kalshi.co/trade-api/v2
Demo WebSocket wss://external-api-ws.demo.kalshi.co/trade-api/ws/v2
The older hosts, api.elections.kalshi.com and demo-api.kalshi.co, still work. Kalshi recommends the external-api hosts for API traders. Despite its name, the elections host serves every Kalshi market.
Calling the API counts as accepting Kalshi's Developer Agreement. It limits API use to a member's own trading. It bars collecting or storing API data except to support that trading, and bars sharing it with third parties without Kalshi's written permission. It also requires bounded retries with exponential backoff.
Reading market data without a key
Series, events, markets, trades and order books are public REST endpoints. They need no account and no headers. This request lists the open markets in the series Kalshi's docs use as their example, the daily high temperature in New York:
curl "https://external-api.kalshi.com/trade-api/v2/markets?series_ticker=KXHIGHNY&status=open"
Fields ending in _dollars are prices as fixed-point strings, so 61c arrives as "0.6100". Fields ending in _fp are contract counts with two decimals, such as "25.00". Kalshi removed the old integer-cent fields in stages between January and April 2026. Older code that reads yes_bid as cents is broken. A decimal type keeps these strings exact. Each market also carries its settlement terms in rules_primary and rules_secondary, which the settlement guide explains. The glossary defines fields such as volume and open interest.
List endpoints page with a cursor. Pass each response's cursor into the next request until none comes back.
The order book returns bids only
GET /markets/{ticker}/orderbook returns two arrays of bids, yes_dollars and no_dollars. Each level is a price and size pair sorted from low to high, so the best bid is the last element. Asks are implied by the other side. A NO bid at 37c is an offer to sell YES at 63c.
Suppose the best YES level is ["0.6100", "250.00"] and the best NO level is ["0.3700", "400.00"]. The YES ask is $1 minus 37c, or 63c, with 400 contracts behind it. The spread is 2c. Buying YES now costs 63c and selling YES now gets 61c. The odds converter turns a price like 63c into implied probability and sportsbook odds. The guide to the Kalshi order book covers depth and spreads.
Creating a Kalshi API key
Keys are made in the API Keys section of your Kalshi account settings. Since October 1, 2026, the web app generates Ed25519 keys by default, with RSA (2048-bit) as the option. Kalshi's docs recommend Ed25519, which has a lower signing cost and 64-byte signatures. RSA remains for clients that only support RSA-PSS, including Kalshi's own SDKs before version 3.31.0.
You get a key ID and a private key in PEM format. Kalshi does not store the private key and shows it only once, so a lost key means a new key. You can also generate the pair yourself and paste only the public key. Through the API, POST /api_keys/generate still makes an RSA key unless you pass "key_type": "ed25519".
Scopes decide what a key can do
A key created through the API with no scopes list gets read and write: full access, transfers included.
| Scope | What it grants |
|---|---|
read | Every read endpoint |
write | Every write endpoint, orders and transfers included. Requires read on the same key. |
write::trade | Orders, order groups and RFQ quotes, without transfers |
write::transfer | Transfer endpoints only |
read::portfolio_balance | Balance reads only |
A trading bot needs read and write::trade. Leaving out write keeps transfers off that key. A key can also be locked to one subaccount, numbered 0 to 63. It then reads and trades only that subaccount. GET /api_keys returns each key's scopes and lock, so you can confirm what a key can do before the first order.
How Kalshi API authentication works
Every private request carries three headers. KALSHI-ACCESS-KEY holds the key ID. KALSHI-ACCESS-TIMESTAMP holds the current time in milliseconds. KALSHI-ACCESS-SIGNATURE holds a base64 signature of one string:
The path starts at /trade-api/v2 and stops before any ?. A request to /trade-api/v2/portfolio/orders?limit=5 signs /trade-api/v2/portfolio/orders. The host and the body stay out of the message. A balance check at timestamp 1760900000000 signs 1760900000000GET/trade-api/v2/portfolio/balance.
Ed25519 keys sign those bytes directly. RSA keys use RSA-PSS with SHA-256, MGF1 with SHA-256 and a salt as long as the digest. This Python client handles both types and needs the requests and cryptography packages. Its signatures verify against the matching public key for both an Ed25519 and an RSA key.
import base64, time, uuid
import requests
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
BASE = "https://external-api.demo.kalshi.co/trade-api/v2"
KEY_ID = "your-key-id"
with open("kalshi.key", "rb") as f:
KEY = serialization.load_pem_private_key(f.read(), password=None)
def sign(ts, method, path):
msg = (ts + method + path.split("?")[0]).encode()
if isinstance(KEY, Ed25519PrivateKey):
sig = KEY.sign(msg)
else:
sig = KEY.sign(msg,
padding.PSS(mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.DIGEST_LENGTH),
hashes.SHA256())
return base64.b64encode(sig).decode()
def call(method, endpoint, body=None):
path = "/trade-api/v2" + endpoint
ts = str(int(time.time() * 1000))
headers = {
"KALSHI-ACCESS-KEY": KEY_ID,
"KALSHI-ACCESS-TIMESTAMP": ts,
"KALSHI-ACCESS-SIGNATURE": sign(ts, method, path),
}
return requests.request(method, BASE + endpoint,
headers=headers, json=body, timeout=10)
for k in call("GET", "/api_keys").json()["api_keys"]:
print(k["api_key_id"], k["scopes"])
The code checks the parsed key because a PKCS#8 RSA key also begins with BEGIN PRIVATE KEY. Kalshi's troubleshooting table lists the usual failures: a wrong key ID or key file, the wrong signing algorithm, a timestamp in seconds, a query string in the signed path, and /trade-api/v2 added to the URL twice. Keep the clock synced, since the timestamp is part of what you sign.
Kalshi also publishes SDKs: kalshi_python_sync, kalshi_python_async and kalshi-typescript. Ed25519 keys need version 3.31.0 or later. The old kalshi-python package is deprecated.
How to place an order with the Kalshi API
Orders go to POST /portfolio/events/orders, which Kalshi calls Create Order (V2). It requires ticker, side, count, price, time_in_force and self_trade_prevention_type. client_order_id is optional, and Kalshi strongly recommends it.
V2 quotes everything on the YES side. bid buys YES and ask sells YES. Selling YES is the same exposure as buying NO at $1 minus the price, so a NO trade is an ask priced on the YES scale. With the book above, rows that trade now use immediate_or_cancel. Rows that wait use good_till_canceled:
| You want to | side | price | Result |
|---|---|---|---|
| Buy YES now | bid | 0.6300 | Takes the 63c ask |
| Buy YES and wait | bid | 0.6100 | Rests at the 61c bid |
| Buy NO now | ask | 0.6100 | Hits the 61c YES bid, so NO costs 39c |
| Buy NO and wait | ask | 0.6300 | Rests with the 37c NO bids |
| Sell YES you hold now | ask | 0.6100 | Receives 61c |
| Sell NO you hold now | bid | 0.6300 | Receives 37c |
There is no market order. Kalshi retired that type in September 2025, and V2 has no type field. To trade at once, send immediate_or_cancel at the worst price you will accept. Kalshi cancels whatever cannot fill at that price or better. The guide to Kalshi limit orders covers the choices.
This order rests one YES contract at 1c in demo, using the call helper above, then cancels it. Nothing fills.
order = {
"ticker": "TICKER-FROM-GET-MARKETS",
"side": "bid", # bid buys YES, ask sells YES
"count": "1",
"price": "0.0100", # always the YES price, in dollars
"time_in_force": "good_till_canceled",
"post_only": True,
"self_trade_prevention_type": "taker_at_cross",
"client_order_id": str(uuid.uuid4()),
}
r = call("POST", "/portfolio/events/orders", order)
oid = r.json()["order_id"]
call("DELETE", f"/portfolio/events/orders/{oid}?market_ticker={order['ticker']}")
A 201 response returns order_id, fill_count and remaining_count, plus average_fill_price and average_fee_paid when something filled. Passing market_ticker on a cancel lets Kalshi route it to the right exchange shard. Without it, the cancel goes to shard 0. Kalshi deprecated the older /portfolio/orders write endpoints in June 2026. GET /portfolio/orders still lists your orders.
Order options and their rules
| Field | Rule |
|---|---|
time_in_force | Required. good_till_canceled, immediate_or_cancel or fill_or_kill. fill_or_kill fills the whole count at once or nothing. |
expiration_time | Unix seconds. Pair it with good_till_canceled for an order that expires at that time. It cannot be combined with immediate_or_cancel. |
post_only | An order that would cross is canceled instead of filled, so it can only rest as a maker. |
reduce_only | Caps the size at your current position. Rejected unless the order is immediate_or_cancel. |
self_trade_prevention_type | Required. taker_at_cross cancels the incoming order if it would hit your own resting order. maker cancels the resting one and keeps matching. |
cancel_order_on_pause | Cancels the resting order if exchange trading pauses. |
order_group_id | Joins an order group. When fills in a rolling 15-second window pass the group's limit, Kalshi cancels its resting orders and blocks new ones until a reset. |
subaccount | 0 is the primary account. |
Fees depend on the series. GET /series/{ticker} returns fee_type. With quadratic, only takers pay. With quadratic_with_maker_fees, resting orders also pay when they fill. Two other values, quadratic_with_combo_maker_fees and flat, cover combos and series on Kalshi's specific fee table. The Kalshi fees guide has the formula. The fee calculator prices any fill.
Prices, ticks and fractional contracts
Each market publishes its price grid in price_ranges, a list of bands with start, end and step in dollars. Kalshi calls this field the source of truth. Kalshi accepts a price on the grid and rejects a price off it. linear_cent markets step by 1c. tapered_deci_cent markets step by $0.001 below 10c and above 90c. Combo markets go down to $0.0001 at the extremes. Whole-cent prices are valid in every structure.
On a tapered_deci_cent market, "0.0450" is valid, "0.4550" is rejected and "0.4600" is valid. A check against the bands takes a few lines:
from decimal import Decimal
def on_grid(price, price_ranges):
p = Decimal(price)
for band in price_ranges:
start, end, step = (Decimal(band[k]) for k in ("start", "end", "step"))
if start <= p <= end and (p - start) % step == 0:
return True
return False
Counts take up to two decimals, with a minimum of 0.01 contracts. Fills can arrive fractional even if you never send a fractional order.
Kalshi API rate limits
Kalshi meters private requests in tokens. Most cost 10. Your tier sets the refill rate of two buckets, in tokens per second. Write covers orders, amends, cancels, order groups, the RFQ quote flow and block trade accepts. Read covers the rest. REST and FIX draw from the same buckets.
| Tier | Read | Write | Orders a second |
|---|---|---|---|
| Basic | 200 | 100 | 10 |
| Advanced | 300 | 300 | 30 |
| Expert | 600 | 600 | 60 |
| Premier | 1,200 | 1,200 | 120 |
| Paragon | 2,400 | 2,400 | 240 |
| Prime | 4,800 | 4,800 | 480 |
| Prestige | 12,000 | 9,600 | 960 |
On Basic that is 20 ordinary reads and 10 orders a second. The Basic Write bucket holds one second of budget, so it never banks a burst. Write buckets above Basic hold three seconds, and so do the Read buckets on Basic and Advanced. They allow a burst of three times the rate after a quiet spell.
Every account starts on Basic. Advanced comes from an upgrade endpoint. Kalshi grants Expert and above daily, based on your share of exchange volume. GET /account/limits shows your tier. GET /account/endpoint_costs lists endpoints that cost more or less than 10. Batches bill every item, so 25 orders in one batch cost 250 tokens. GET /portfolio/balance has had its own bucket since October 15, 2026.
An empty bucket returns 429 with no Retry-After header and no penalty. Kalshi asks clients to back off exponentially.
WebSocket basics
The WebSocket needs a key even for public data. Sign the handshake with GET and the path /trade-api/ws/v2, send the same three headers, then subscribe:
{"id": 1, "cmd": "subscribe",
"params": {"channels": ["orderbook_delta"],
"market_tickers": ["TICKER-FROM-GET-MARKETS"],
"use_yes_price": true}}
Useful channels include orderbook_delta, ticker, trade, fill, user_orders and market_positions. The book channel sends a snapshot and then deltas, each with a seq number. A gap in the sequence means you missed an update. NO levels arrive on the NO price scale unless you set use_yes_price to true, which matches V2 pricing. Kalshi plans to make that the default.
Habits that keep API trading safe
Six habits guard against the common expensive errors, such as double orders, stale prices, unit bugs and cancels stuck behind a 429.
- A fresh client_order_id on every new order. Kalshi's docs say a second submission with the same ID is rejected with a 409. If a response is lost, the ID is how you find the order.
- No retry under a new ID. A timeout does not say whether the order reached the book. Resend with the same client_order_id, or list your orders on that ticker and match the ID first. A retry under a fresh ID can place the order twice.
- Prices from the book.
last_price_dollarsis the last trade, which can be hours old. A buy fills against the ask and its size, so the ask is the price that belongs in any expected value math for a buy. - Dollar strings checked against the grid. Mixing up cents and dollars is the classic bug in code ported from the integer-cent API.
- Headroom in the Write bucket. Cancels share it with new orders, so a client that spends it all on new orders can find a cancel stuck behind a 429.
- Demo first. Kalshi warns that demo prices and behavior may differ from production, so demo tests the plumbing.
GET /api_keys also returns api_key_region_expiration_ts: the time your location attestation for API key requests lapses. Kalshi's changelog says that after that time, keys cannot trade Sports, Elections and Entertainment markets.
Questions traders ask
Is the Kalshi API free?
Kalshi's documentation lists no charge for API access. Every account starts on the Basic rate limit tier. Advanced comes from an upgrade endpoint, and higher tiers come from trading volume. What costs money is trading. Each fill pays the series' normal exchange fee, which depends on its fee type and on whether your order took or made liquidity. The fee calculator prices a fill.
Do I need an API key to get Kalshi market data?
No key is needed for REST market data. Series, events, markets, trades and order books are public endpoints that take no headers. The WebSocket is different. Kalshi authenticates every WebSocket connection, even one that only carries public channels such as ticker and trade.
Does Kalshi have a sandbox or demo API?
Yes. The demo environment at demo.kalshi.co runs on mock funds, and its REST base is external-api.demo.kalshi.co/trade-api/v2. Demo and production credentials are separate, so a demo key works only in demo. Kalshi warns that demo prices and behavior may not match real markets.
Can you place a market order through the Kalshi API?
No. Kalshi retired the market order type in September 2025, and the V2 order endpoint requires a price on every order. To trade immediately, send an immediate_or_cancel order at the worst price you will accept. Kalshi cancels anything that cannot fill at that price or better.
What are the Kalshi API rate limits?
Basic accounts get 200 read tokens and 100 write tokens per second. Most requests cost 10 tokens, so that is about 20 reads and 10 orders a second. Higher tiers run up to Prestige at 12,000 read and 9,600 write tokens. A 429 response means the bucket is empty.
Is there an official Kalshi Python SDK?
Yes. Kalshi publishes kalshi_python_sync and kalshi_python_async on PyPI, plus kalshi-typescript on npm. Ed25519 keys need version 3.31.0 or later. The old kalshi-python package is deprecated. Kalshi says the SDKs can lag the API and treats its OpenAPI specification as the source of truth.
Sources
- Kalshi API documentation, API Environments and Endpoints (accessed Oct 19, 2026)
- Kalshi API documentation, Quick Start: Market Data (accessed Oct 19, 2026)
- Kalshi API documentation, Understanding Pagination (accessed Oct 19, 2026)
- Kalshi API documentation, Orderbook Responses (accessed Oct 19, 2026)
- Kalshi API documentation, API Keys (accessed Oct 19, 2026)
- Kalshi API documentation, Quick Start: Authenticated Requests (accessed Oct 19, 2026)
- Kalshi API documentation, Get API Keys (scopes and subaccount) (accessed Oct 19, 2026)
- Kalshi API documentation, Generate API Key (key_type and default scopes) (accessed Oct 19, 2026)
- Kalshi API documentation, Create Order (V2) (accessed Oct 19, 2026)
- Kalshi API documentation, Cancel Order (V2) (accessed Oct 19, 2026)
- Kalshi API documentation, Get Orders (accessed Oct 19, 2026)
- Kalshi API documentation, Quick Start: Create your first order (client_order_id and errors) (accessed Oct 19, 2026)
- Kalshi API documentation, Order direction (outcome_side and book_side) (accessed Oct 19, 2026)
- Kalshi API documentation, Order Groups (accessed Oct 19, 2026)
- Kalshi API documentation, FIX Order Entry (post-only and time in force) (accessed Oct 19, 2026)
- Kalshi API documentation, Fixed-Point Representation (accessed Oct 19, 2026)
- Kalshi API documentation, Get Market (price_ranges) (accessed Oct 19, 2026)
- Kalshi API documentation, Get Series (fee_type) (accessed Oct 19, 2026)
- Kalshi API documentation, Rate Limits and Tiers (accessed Oct 19, 2026)
- Kalshi API documentation, Quick Start: WebSockets (accessed Oct 19, 2026)
- Kalshi API documentation, WebSocket Orderbook Updates (accessed Oct 19, 2026)
- Kalshi API documentation, Test In The Demo Environment (accessed Oct 19, 2026)
- Kalshi API documentation, Kalshi SDKs (accessed Oct 19, 2026)
- Kalshi API changelog, entries of Sep 25, 2025 (market order type), Jan 6, Mar 8 and Mar 25, 2026 (integer-cent fields), Apr 22, 2026 (V2 order endpoints), Jun 18, 2026 (legacy order endpoints deprecated), Jun 30, 2026 (write::trade), Jul 2, 2026 (subaccount keys), Aug 16, 2026 (location attestation), Sep 24 and Oct 1, 2026 (Ed25519 keys) and Oct 15, 2026 (balance bucket) (accessed Oct 19, 2026)
- Kalshi Developer Agreement, v1.1 (accessed Oct 19, 2026)