TOTAL VOLUME:
$146.6b
24H VOL:
$161,047,490
24H TRANSACTIONS:
2,781,800,361
OPEN INTEREST:
$1,744,915,607
504,917
Markets across
37,612
events
MATCHED EVENTS:
2,946
PLATFORM COVERAGE:
5
Polymarket:
38%
VS.
Kalshi:
62%
How the Kalshi API works: keyless market data, signed trading, REST and WebSocket feeds, historical candlesticks, and the real rate-limit tiers.
Jared Polites
Oct 18, 2026

TL;DR
https://external-api.kalshi.com/trade-api/v2.KALSHI-ACCESS-* headers.Ever pulled a Kalshi price, expected a number, and got back a string like "0.5800" sitting in a field called yes_bid_dollars? Then discovered the order book has no asks at all? That is the usual first hour with the Kalshi API, and it is not a flaw. It is a clean design that just needs a short map.
Kalshi is a CFTC-regulated exchange, and its API reads like a real exchange API: stable identifiers, signed requests, published limits, and a historical archive. This guide covers what the API is, what you can pull without a key, how authentication works, which endpoints matter, how the WebSocket feed behaves, and how fast you can call everything.
The Kalshi API is the programmatic interface to Kalshi's exchange. It covers the same markets you see in the app, with REST for request and response calls, WebSocket for streaming, and FIX for institutional order entry.
The docs split the surface into two families. The Predictions APIs cover event-contract markets, which is what most people mean by "the Kalshi API". A separate Perps family covers perpetual futures on margin. This guide covers the Predictions side.
| Interface | Where it lives | What it is for |
|---|---|---|
| REST | `https://external-api.kalshi.com/trade-api/v2` | Markets, events, trades, order books, candlesticks, portfolio, orders |
| WebSocket | `external-api-ws.kalshi.com` | Streaming order book, ticker, trade, and account updates |
| FIX | Documented by Kalshi | Order entry for firms that already run FIX infrastructure |
| Demo REST | `https://external-api.demo.kalshi.co/trade-api/v2` | Testing with mock funds |
| Demo WebSocket | `wss://external-api-ws.demo.kalshi.co/trade-api/ws/v2` | Testing the streaming feed |
Kalshi also publishes machine-readable specs: an OpenAPI file for REST and an AsyncAPI file for WebSocket. If you generate client code, generate it from those specs. They update before any guide, this one included.
One legal note belongs here. Using the API means agreeing to Kalshi's Developer Agreement, which is separate from the normal account terms. Read it before you build anything commercial on top of the feed.
No. This is the single most useful fact about the Kalshi API, and many guides bury it.
Kalshi's own quick-start guide for market data says no authentication headers are required for the endpoints it walks through. I checked this directly on 2026-10-17 by calling the production API with no credentials. Markets, recent trades, a market order book, candlesticks, and exchange status all returned normal responses.
That means you can build a read-only dashboard, a price tracker, or a research pipeline without creating an account. You only need a key when you want your own portfolio data or want to place orders. Everything an endpoint returns is prediction market data that you can timestamp and store.
The docs themselves do not spell out which individual read endpoints are keyless, so treat my test as an observed result, not a guarantee. If an endpoint ever returns an authentication_error, the docs say some endpoints do, and you add signing at that point.
Authentication is a signed-request scheme, closer to a brokerage than a hobby API. You hold a private key, Kalshi holds the matching public key, and every private request proves you signed it.
Kalshi keys are asymmetric key pairs, in one of two types.
A PEM file header does not tell you which type you have, so determine it from the parsed key, not the file.
There are two routes. In the web app, open Profile Settings, find the API Keys section, and create a key. Ed25519 is the default there, and you can switch to RSA.
Through the API, POST /trade-api/v2/api_keys registers a public key you generated yourself, and POST /trade-api/v2/api_keys/generate returns a fresh key pair. The generate endpoint defaults to RSA unless you ask for another key_type.
Kalshi shows the private key once and does not store it. If you close the page without saving it, you cannot retrieve it and have to create a new key. Treat the private key like a password.
Every authenticated request carries three headers.
| Header | Value |
|---|---|
| `KALSHI-ACCESS-KEY` | Your Key ID |
| `KALSHI-ACCESS-TIMESTAMP` | The request time in milliseconds |
| `KALSHI-ACCESS-SIGNATURE` | The base64-encoded signature |
The string you sign is the timestamp, then the HTTP method, then the request path, concatenated in that order. The path is the full path starting at /trade-api/v2, and you must strip the query string before signing. A request to /trade-api/v2/portfolio/orders?limit=5 is signed as /trade-api/v2/portfolio/orders.
The single most common signing bug is leaving the query string in. The signature then fails even though everything else is right.
Ed25519 keys sign the string directly. RSA keys use RSA-PSS with SHA-256, MGF1 with SHA-256, and a salt length equal to the digest length. Kalshi picks the verification method from the public key you registered, so sign with the algorithm that matches your key.
Kalshi runs a full demo environment with mock funds. It has its own sign-up at demo.kalshi.co, and Kalshi states that credentials are not shared between demo and production. You need a separate demo account and a separate key.
Kalshi also warns that demo prices and behavior may not reflect real markets. Use it to test your signing, order flow, and error handling, not to judge how a strategy would fill.
Kalshi organizes everything in three levels, and understanding them saves a lot of confusion.
Tickers encode this. A series ticker like KXHIGHNY leads to an event ticker like KXHIGHNY-26OCT18 and then to a market ticker like KXHIGHNY-26OCT18-T61. Learn to read the pattern and you can navigate the API by hand.
For more on how recurring chains behave, see recurring prediction market contracts.
A practical gotcha: Kalshi also lists multivariate "combo" markets. When I called the markets endpoint with only status=open, the first result was a combo, not a simple event market. If you want clean single-outcome contracts, pass mve_filter=exclude. If you only want combos, pass mve_filter=only.
The full reference has well over a hundred endpoints, including FCM, block trades, and margin clearing that most builders never touch. These are the ones that matter for market data and basic trading.
| Job | Endpoint | Auth |
|---|---|---|
| Exchange status | `GET /exchange/status` | None in my test |
| Historical cutoffs | `GET /historical/cutoff` | None in my test |
| List markets | `GET /markets` | None in my test |
| One market | `GET /markets/{ticker}` | None in my test |
| Order book | `GET /markets/{ticker}/orderbook` | None in my test |
| Recent trades | `GET /markets/trades` | None in my test |
| Candlesticks | `GET /series/{series_ticker}/markets/{ticker}/candlesticks` | None in my test |
| One series | `GET /series/{series_ticker}` | None expected |
| One event | `GET /events/{event_ticker}` | None expected |
| Balance and positions | `GET /portfolio/balance`, `GET /portfolio/positions` | Signed |
| Place and manage orders | `POST /portfolio/orders` and V2 order endpoints | Signed |
"None in my test" means the call returned a normal response with no credentials on 2026-10-17. Rows marked "None expected" follow the same pattern but I did not call them directly, so verify before you rely on them.
The reference also covers order groups, subaccounts, RFQ and quote flows for large trades, live data feeds for sports and weather, milestones, and incentive programs. Browse the reference once so you know what exists.
GET /markets is the workhorse. It returns up to 1,000 markets per page, defaulting to 100, and uses cursor pagination.
Useful filters include status, series_ticker, event_ticker, a comma-separated tickers list, and timestamp ranges for creation, close, and settlement. The status filter accepts one value per request: unopened, open, paused, closed, or settled. Leave it empty to get everything.
Some filters are only valid with particular statuses. Close-time filters work with closed or no status, and settled-time filters work with settled or no status. The metadata-updated filters are stricter and cannot be combined with most others.
A market object carries rules, timing, and pricing. These fields matter most.
| Field | What it is | Gotcha |
|---|---|---|
| `ticker`, `event_ticker` | Identifiers | Tickers are your stable keys |
| `market_type` | `binary` or `scalar` | Most are binary |
| `status` | Lifecycle state | Different value set from the `status` filter |
| `yes_bid_dollars`, `yes_ask_dollars` | Best YES bid and ask | Dollar strings, not numbers |
| `no_bid_dollars`, `no_ask_dollars` | Best NO bid and ask | The complement of the YES side |
| `last_price_dollars` | Last traded price | Can be stale on thin markets |
| `yes_bid_size_fp`, `yes_ask_size_fp` | Contracts at the best bid and ask | Fixed-point strings |
| `volume_fp`, `volume_24h_fp` | Total and 24-hour contracts traded | Contracts, not dollars |
| `open_interest_fp` | Open contracts | Not netted |
| `close_time`, `expected_expiration_time` | Timing | Early close is possible on some markets |
| `result` | `yes`, `no`, `scalar`, or empty | Empty until determined |
| `rules_primary`, `rules_secondary` | Plain-language terms | Read these before trusting a price |
The market status field has its own lifecycle vocabulary: initialized, inactive, active, closed, determined, disputed, amended, and finalized. A market is "determined" when the outcome is known and "finalized" when settlement is complete. Do not confuse these with the status query filter, which uses the shorter list above.
Kalshi has also been retiring older fields. For example, liquidity_dollars was removed from market responses on October 1, 2026, with the best-level size fields as the replacement. Check the changelog before you depend on any field for long.
Because Kalshi supports sub-cent price steps on some markets, and whole-cent integers cannot represent them. Prices come back as dollar strings with up to four decimals in most responses, and as many as six in candlesticks. Contract counts come back as fixed-point strings with two decimals.
Parse these as decimals. If you cast them to floats and sum a long ledger, rounding drift will show up. Kalshi's changelog tells readers to use the *_dollars fields because integer-cent fields cannot represent sub-cent prices.
A price of "0.7200" means a 72% implied probability. The contract pays $1 if it resolves yes and $0 otherwise. For a refresher on how odds across a market should add up, see prediction market overround.
The order book endpoint is GET /markets/{ticker}/orderbook. Its response is wrapped in orderbook_fp and contains two arrays, yes_dollars and no_dollars. Each entry is a pair of a price string and a contract-count string, ordered from best to worst.
There are no asks in the response. This surprises everyone once.
In a binary market, a YES bid at one price is mechanically the same trade as a NO ask at the complementary price. So Kalshi returns only the two bid sides and lets you derive the rest.
For example, a YES bid at $0.07 is equivalent to a NO ask at $0.93, for the same number of contracts.
The optional depth parameter limits the number of levels per side. Zero, the default, returns every level. A value from 1 to 100 returns that many.
An empty book is a valid response. When I queried a brand-new combo market, both arrays came back empty. Treat empty arrays as "no resting orders yet", not as an error.
For the structural reasons an order book behaves differently from an automated market maker, see order book versus AMM. To judge how thin a market really is before you trust one price, read the full book, not the top of it.
Kalshi offers three ways to rebuild price history, depending on how granular you need to be.
GET /markets/trades returns executed trades. Filter with ticker, min_ts, and max_ts, and page with the cursor. You can also filter block trades with is_block_trade, which is true for trades matched off the book and false for the rest.
Each trade includes a trade_id, the ticker, count_fp, yes_price_dollars, no_price_dollars, the taker's side, and a created_time. The older taker_side field is deprecated. Use taker_outcome_side, which is yes or no, or taker_book_side, which is bid or ask.
Trades are the right input when you want to reconstruct what actually happened, not just the quote.
Candlesticks live under the series path: GET /series/{series_ticker}/markets/{ticker}/candlesticks. You pass start_ts, end_ts, and period_interval, which accepts 1 for one minute, 60 for one hour, or 1440 for one day.
Each candle carries end_period_ts and three OHLC blocks: price for trades, yes_bid, and yes_ask. It also carries volume_fp and open_interest_fp. The trade-price fields can be null for a period in which nothing traded, so handle nulls.
That separation of trade price from bid and ask is useful. A thin market can show a stale last trade while the bid and ask have moved. Charting the midpoint of the bid and ask often gives a truer picture of consensus than the last price.
If you set include_latest_before_start to true, the response prepends a synthetic candle with null prices and a previous-close value, so your chart has a starting point even when the first period had no trades.
Kalshi splits data between live and historical endpoints using cutoff timestamps. GET /historical/cutoff returns the boundary timestamp for each record type, and it needs no key.
If you backtest across older markets and a query returns nothing from the live endpoints, check the cutoff before assuming the data is missing. For a method that avoids the common traps, see how to backtest a prediction market strategy.
If you only need a snapshot, such as a market's implied probability once an hour, REST is enough. If you need a continuous Kalshi data feed, use the WebSocket. It pushes changes as they happen, so you stop polling and stop burning your rate limit.
The tradeoff is complexity. A WebSocket client has to authenticate, manage subscriptions, detect gaps, and rebuild state, while a REST call just hands you the current state.
The production host is external-api-ws.kalshi.com over wss. Copy the exact URL from Kalshi's AsyncAPI spec, since the demo path is /trade-api/ws/v2 and the production listing may differ.
Authentication is required to open the connection. You send the same API key headers during the WebSocket handshake. This applies even to channels that carry only public data, so a keyless dashboard still needs a key if it streams.
Once connected, you send a JSON command with an integer id, a cmd of subscribe, and params containing a channels array plus an optional market selector such as market_ticker, market_tickers, market_id, or market_ids.
The Trade API channels include:
orderbook_delta for the book snapshot and incremental updates.ticker for price, volume, and open-interest changes.trade for each executed trade.fill, user_orders, market_positions, and order_group_updates for your own account activity.market_lifecycle_v2 and multivariate_market_lifecycle for market and event state changes.communications for RFQ and quote activity.cfbenchmarks_value, cfbenchmarks_value_5hz, and pyth_value for reference-price feeds.For market data work, the three that matter are orderbook_delta, ticker, and trade. Omitting the market selector on ticker or trade streams all markets, which is a lot of traffic. Filter to what you need.
Subscribe to orderbook_delta. You first receive an orderbook_snapshot with the full book, then orderbook_delta messages for each change.
A delta carries the market, a price_dollars string, a delta_fp change in contracts, and the side, which is yes or no. A negative delta_fp reduces size at that level. Apply deltas to your local copy of the book in order.
Every message carries a seq number. Check that each one follows the previous for the same subscription id. If it does not, you have missed something, and your local book can no longer be trusted.
Kalshi's docs do not describe a recovery procedure for a gap. As a practical approach, which is my suggestion and not documented behavior, discard the local book and request a fresh snapshot. The docs say a get_snapshot action returns a snapshot without changing your subscription.
Kalshi sends a WebSocket ping frame every 10 seconds with the body heartbeat. Your client must answer with a pong frame. Most WebSocket libraries do this automatically, but confirm yours does.
The docs do not state a timeout for missed pongs, so do not assume a threshold. Build reconnect logic with backoff, then resubscribe and rebuild state from a fresh snapshot.
Subscriptions also have limits. The docs define errors for too many markets in one subscription, for command rate limits, and for an outbound buffer overflow, but they do not publish the numbers. Treat these as real constraints and handle the error codes.
The ticker channel sends a message whenever a field changes. It includes the last price, best YES bid and ask, best-level sizes, total volume, open interest, and a millisecond timestamp. Dollar values are strings and counts are fixed-point strings, matching REST.
Use ts_ms. The seconds-based ts and the RFC3339 time fields are deprecated.
Kalshi publishes its limits, and they are tiered by trading volume, not by account type or institutional verification. Limits use a token-bucket model, with budgets per second and a bucket that allows short bursts.
| Tier | Read budget per second | Write budget per second |
|---|---|---|
| Basic | 200 | 100 |
| Advanced | 300 | 300 |
| Expert | 600 | 600 |
| Premier | 1,200 | 1,200 |
| Paragon | 2,400 | 2,400 |
| Prime | 4,800 | 4,800 |
| Prestige | 12,000 | 9,600 |
Volume tiers are reviewed daily. Your share is your trailing 30-day volume, counting both maker and taker trades, divided by twice the previous month's total exchange volume. A qualifying review grants the tier for 30 days, and it renews while you keep qualifying.
| Tier | Share to earn | Share to keep |
|---|---|---|
| Expert | 0.075% | 0.05% |
| Premier | 0.125% | 0.10% |
| Paragon | 0.25% | 0.20% |
| Prime | 0.50% | 0.40% |
| Prestige | 1.00% | 0.80% |
If your volume drops below the keep threshold, the tier only lapses when the current grant expires.
Writes cover order placement, amends, cancels, order groups, the RFQ quote flow, and block-trade accepts. REST and FIX draw from the same buckets. Everything else, including every GET, counts as a read.
Most requests cost 10 tokens by default, and some endpoints cost more. Call GET /account/endpoint_costs for the non-default list, and GET /account/limits to see your active grants.
The balance endpoint has its own bucket: once per second on Basic and Advanced, and five per second on higher tiers. Batch requests cost the same as the individual calls, and each order in a batch is billed separately.
You get an HTTP 429 with a short error body and no Retry-After header. Use exponential backoff, since the response will not tell you how long to wait.
Basic and Advanced read buckets hold three seconds of budget, so they absorb short bursts of up to three times the per-second rate. Plan your polling around the steady rate, not the burst.
The practical advice for most builders: start on Basic, build against it, and only upgrade when you measurably hit a limit. Switching a polling loop to the WebSocket usually frees more budget than a tier upgrade does.
Kalshi publishes official SDKs for Python and TypeScript.
| Package | Language | Notes |
|---|---|---|
| `kalshi_python_sync` | Python | Synchronous client |
| `kalshi_python_async` | Python | Asynchronous client |
| `kalshi-typescript` | TypeScript | Install from npm |
| `kalshi-python` | Python | Deprecated, do not use for new work |
Many older tutorials still use the deprecated kalshi-python package or third-party names, so check the package name against Kalshi's SDK page before you install.
Kalshi says SDK releases generally ship on Tuesdays and Wednesdays, usually before the matching API change, and that SDKs may lag the API. For production, the docs recommend generating your own client from the OpenAPI and AsyncAPI specs or calling the API directly. RSA keys work with every SDK version, while Ed25519 keys need version 3.31.0 or later.
Here is the shortest reliable path to a current probability and a history, with no key.
GET /markets with status=open and mve_filter=exclude, optionally filtered by series_ticker, to find the ticker you want.yes_bid_dollars and yes_ask_dollars. The midpoint is a better estimate of consensus than the last price on a thin market.depth to see how much size sits behind those quotes.period_interval=60 for an hourly history.ticker and orderbook_delta channels.Parse every price as a decimal string, and key your database on ticker.
If you would rather watch prices than code against them, see how to track prediction market prices.
The strongest reason is that Kalshi is a CFTC-regulated exchange, and that shapes the whole interface. Authentication, published limits, a documented archive, and FIX support all look like infrastructure built for firms that need to trust the data source.
For anyone building for a US audience, regulated status is the main draw. Prediction market legality is still being worked out between federal and state authorities, and Kalshi has faced challenges from some state regulators. Anyone shipping a consumer product should read the CFTC versus states picture and take their own legal advice, rather than assume one rule applies everywhere.
Kalshi's contract range is also broad, including economics, weather, sports, and politics, so one integration covers many categories. For background on the exchange itself, see what Kalshi is.
Everything above applies to Kalshi only. The moment you want to compare odds across venues, you inherit a normalization problem, because each platform defines its own identifiers, price formats, fee models, and resolution rules.
Polymarket runs a deep global order book and publishes its own public APIs. Limitless, Predict.Fun, and Opinion each have their own contract structures and API conventions, and the onchain platforms can also be read directly from the blockchain. None of the five normalize against each other, so the same real-world event can appear as different markets with different price formats and different resolution criteria.
Matching them correctly is a real engineering task, not a join on a title string. It is part of why prediction market odds disagree across platforms and why spotting a real gap takes care.
This is the gap PredictionHero's aggregation layer is built for. It pulls from Kalshi, Polymarket, Limitless, Predict.Fun, and Opinion and normalizes the output, so you can tell whether a probability is one venue's quirk or a genuine cross-market consensus. For a head-to-head view of the two largest, see Kalshi versus Polymarket. If you only need to compare prediction market data across platforms, an aggregator saves you from maintaining five integrations.
If you are building your own integration anyway, Kalshi is a natural first venue to wire up, because its public read access is open and its documentation is thorough. Design your internal model around outcomes and probabilities, not around any one venue's field names, and read each other platform's documentation before you commit to a shared schema.
*_dollars and *_fp fields, never from integer-cent fields./trade-api/v2.mve_filter so they do not pollute your data.Retry-After header.seq numbers and rebuild from a snapshot on any gap.rules_primary and rules_secondary before you trust a price.Reading public market data is free and needs neither an account nor an API key. The docs list no separate fee for API access. Placing trades through the API requires a funded Kalshi account, and normal trading fees apply, but pulling prices, order books, and history does not cost anything.
Yes. The API returns JSON over REST and WebSocket, so you need to write code in Python, JavaScript, or any language with HTTP and WebSocket libraries. There is no no-code interface inside the API itself. If you only want to watch prices, a tracker or aggregator avoids the coding entirely.
The docs are the reference: endpoint definitions, authentication steps, rate-limit rules, and the OpenAPI and AsyncAPI specs. The data feed is the actual stream of prices, trades, and order book updates you pull by following those docs, either by polling REST or by subscribing over WebSocket.
Yes. The trades endpoint returns executed trades filtered by time, and the candlestick endpoint returns one-minute, one-hour, or one-day candles for a market. Markets that settled before the historical cutoff are served through separate historical endpoints, so check the cutoff timestamps if older queries come back empty.
Open Profile Settings in your Kalshi account, find the API Keys section, and create a key. Ed25519 is the default type. Kalshi shows the private key once and does not store it, so save it immediately. You can also register your own public key through the API.
They are tiered by trading volume. Basic allows 200 reads and 100 writes per second, Advanced allows 300 each, and the top Prestige tier allows 12,000 reads and 9,600 writes per second. Excess requests return an HTTP 429 with no retry header, so back off exponentially.
Yes. It streams order book snapshots and deltas, tickers, trades, and your own fills and orders. The connection requires authenticated API key headers during the handshake, even for public channels. Kalshi sends a ping every 10 seconds and expects a pong in reply.
Yes. Kalshi publishes kalshi_python_sync and kalshi_python_async for Python and kalshi-typescript for TypeScript. The older kalshi-python package is deprecated. Kalshi notes that SDKs can lag the API, so the OpenAPI spec is the source of truth.
Yes. The demo environment uses mock funds and has its own sign-up and credentials, separate from production. Prices and behavior there may not match real markets, so use it for testing your code, not for judging strategy performance.
PredictionHero aggregates publicly available prediction market data for informational purposes only. This is not financial advice. Prediction markets may not be available in all jurisdictions.
Related

A backtest is only as good as the price data underneath it: survivorship bias, thin liquidity, and too few independent events can each quietly inflate your results.
Jared Polites · Sep 8

Tracking a market means watching last price, mid price, volume, and timestamp together, across every platform that lists it. Here's what each of the five major platforms actually shows you.
Jared Polites · Sep 26

Kalshi is a CFTC-regulated exchange where you trade Yes/No contracts on real-world events, funded by bank, card, wire, or crypto, with fees and pricing that work nothing like a sportsbook's.
Jared Polites · Oct 11