Kalshi API: A Complete Developer Guide
How Kalshi's API works in practice — the ticker hierarchy, RSA request signing, and the 2026 field migrations that are still breaking integrations months later.
Read this part before you write any code
Kalshi's API has moved more than any other prediction market API this year, and the changes have been breaking rather than additive.
The integer cent price fields were removed in the Q1 2026 migration. The legacy order endpoint was retired in June. Public GitHub issues from as recently as this month show pollers whose Kalshi branch silently stopped returning anything, because the fields they read no longer exist and every market quietly hit a continue.
None of that makes it a bad API. It is a regulated exchange growing quickly, and growing APIs change. But it does mean two things for you. Pin a date against anything you read about Kalshi, including this page. And check the official documentation before you build, because a guide written eight months ago will confidently tell you to read fields that were deleted in March.
Everything below was checked in September 2026.
Market data needs no credentials at all
The best thing about building on Kalshi is also the least advertised: the public market data endpoints require no API key, no signature and no account.
Production lives at external-api.kalshi.com/trade-api/v2. There is a demo environment at external-api.demo.kalshi.co/trade-api/v2, which is genuinely useful — you can develop against it without touching real money, and it is the right place to test anything order-shaped.
So if you are building a dashboard, a scanner or a research tool, you can be pulling live Kalshi prices within a minute of reading this. Authentication only becomes your problem when you want portfolio or trading endpoints, and that is a much bigger job — covered further down.
# Public. No key, no signature, no account.
curl "https://external-api.kalshi.com/trade-api/v2/markets?limit=5&status=open"
# Everything under one event
curl "https://external-api.kalshi.com/trade-api/v2/events/SOME-EVENT-TICKER"
# Same call against the demo environment
curl "https://external-api.demo.kalshi.co/trade-api/v2/markets?limit=5"Series, events and markets are three different things
Kalshi organises everything into a three-level hierarchy, and all three levels are addressed by ticker. Getting the levels confused is the most common early mistake.
A series is the recurring template — the concept of "monthly CPI release", not any particular month.
An event is one instance of that series. September's CPI release is an event.
A market is a single tradeable contract inside that event. "CPI comes in above 3.0%" is a market, and one event usually holds several of them at different thresholds.
This matters because a price always belongs to a market, never to an event. If you ask for an event and try to read a price off it, you will get nothing useful — you need to walk down to the markets underneath it. Store all three tickers rather than flattening them, because you will want the hierarchy back later for grouping and filtering.
The price fields that broke everyone's integration
This is the one to know about.
Kalshi used to return prices as integer cents — yes_bid, yes_ask and last_price, as whole numbers from 1 to 99, where 62 meant a 62% implied probability. Plenty of code and plenty of tutorials still assume that shape.
Those fields are gone. The replacements carry a _dollars suffix — yes_bid_dollars, no_bid_dollars, yes_ask_dollars, no_ask_dollars, last_price_dollars and friends — and they are dollar-denominated strings rather than integers. A price of 62 cents now arrives as the string "0.6200".
Three things follow. Your parsing changes, because these are strings and need converting rather than reading as numbers. Your maths changes, because you are working in dollars rather than cents and an old ÷100 will now be wrong by two orders of magnitude. And most painfully, code written against the old fields does not throw — it reads undefined, skips the market, and looks like Kalshi simply has no data. That failure is silent, which is why some integrations ran broken for months before anyone noticed.
If you inherited a Kalshi integration written before 2026, check this first. It is the single most likely thing wrong with it.
// Gone — removed in the Q1 2026 migration
// market.yes_bid => 62 (integer cents)
// market.last_price => 61
// Current — dollar-denominated strings
const yesBid = parseFloat(market.yes_bid_dollars); // "0.6200" -> 0.62
const yesAsk = parseFloat(market.yes_ask_dollars);
const last = parseFloat(market.last_price_dollars);
// The probability is now the price itself, not price / 100.
const impliedProbability = yesBid; // 0.62 === 62%Authentication, when you actually need it
Portfolio and trading endpoints need signed requests, and Kalshi's scheme is more involved than the bearer token most APIs use. It is RSA request signing, and there is no way to shortcut it.
You generate an RSA key pair, upload the public half in the Kalshi dashboard and keep the private half somewhere sensible. Every authenticated request then carries three headers: KALSHI-ACCESS-KEY, KALSHI-ACCESS-TIMESTAMP and KALSHI-ACCESS-SIGNATURE.
The signature is built over a message that is simply the timestamp, the HTTP method and the request path concatenated together. Two details in that sentence cause most of the failures.
The timestamp is in milliseconds. Seconds will fail validation, and the error will not tell you that is why.
The path includes the /trade-api/v2 prefix and excludes the query string. Sign the path you called without its parameters, not the full URL.
Use RSA-PSS with SHA-256 for both the hash and MGF1, and a salt length equal to the digest length — 32 bytes. Then base64 the result.
import base64, time
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding
timestamp = str(int(time.time() * 1000)) # milliseconds, not seconds
method = "GET"
path = "/trade-api/v2/portfolio/balance" # prefix in, query string out
message = (timestamp + method + path).encode()
signature = private_key.sign(
message,
padding.PSS(
mgf=padding.MGF1(hashes.SHA256()),
salt_length=32, # equal to the digest length
),
hashes.SHA256(),
)
headers = {
"KALSHI-ACCESS-KEY": ACCESS_KEY_ID,
"KALSHI-ACCESS-TIMESTAMP": timestamp,
"KALSHI-ACCESS-SIGNATURE": base64.b64encode(signature).decode(),
}Order books, and what they actually tell you
Kalshi gives you a real order book per market, which is more than most prediction markets expose. Ask for a market's orderbook and you get the resting bids on both the yes and the no side, as price and quantity pairs.
The thing that confuses people coming from traditional markets is that yes and no are separate ladders rather than two sides of one book. A bid on no is economically an offer on yes, so if you want a conventional single book you have to fold one side into the other yourself.
This matters more than it sounds. A market showing an attractive top-of-book price might have very little sitting behind it, and depth is the difference between a price you can see and a price you can get. If you are building anything that sizes a position rather than just displays a number, read the book rather than the quote.
curl "https://external-api.kalshi.com/trade-api/v2/markets/SOME-MARKET-TICKER/orderbook"
# Returns resting bids on both sides as price/quantity pairs.
# A bid on 'no' is economically an offer on 'yes' — fold them
# together yourself if you want one conventional book.What changed in 2026, and what it cost people
A short timeline, because the pattern matters more than any individual change.
In November 2025 the dollar-denominated quote fields appeared alongside the integer ones. Additive, harmless, easy to miss.
In the Q1 2026 migration the integer cent fields were removed. Everything reading yes_bid started silently returning nothing.
In May, fields on the fill schema were deprecated. In June, between the 18th and the 25th, the legacy order endpoint was retired entirely and replaced by a new one with a different model — side became bid or ask, and the old yes/no combined with buy/sell arrangement disappeared. Anything placing orders had to be rewritten, not adjusted.
And in September, months after the first of those changes, developers were still filing issues about pollers that had quietly stopped returning Kalshi data.
The lesson is not that Kalshi is unreliable. It is that a direct integration with any single venue is a subscription to that venue's release schedule, and the bill arrives at unpredictable times. Budget for it.
Kalshi or Polymarket, if you only want one
Both are worth building on and they are good at different things.
Kalshi is a CFTC-regulated US exchange. That regulatory standing is the whole proposition for some users, and it comes with a real order book, proper market structure and a serious trader base on economics and politics. The cost is a heavier integration — RSA signing rather than a token, and an API that has moved repeatedly this year.
Polymarket is easier to start with. Read access needs no credentials at all, coverage is wider, and volume on political and macro markets is higher. Its own quirks are different ones — several separate API surfaces and an identifier chain that catches everybody once.
If you want regulated US markets and economic indicators, Kalshi. If you want breadth and the fastest possible start, Polymarket. If you want to compare them against each other, that is a different problem.
The point where one venue stops being enough
Everything above is a perfectly good afternoon, and for a Kalshi-only tool it is the whole job. Go direct. It is free and you keep full access to everything the exchange exposes, including the order book depth a normalized layer will not give you.
The arithmetic changes when you add the second platform, and not for the reason people expect. Writing a second integration is tedious but finite. What is not finite is keeping the mapping between them correct — deciding that a Kalshi market and a Polymarket market are the same question, and keeping that true as both venues rename, split and retire markets. Add the release-schedule problem on top, now doubled, and it becomes a standing commitment rather than a task.
That is the specific thing a normalized layer removes. One schema, one set of identifiers, the same event matched across venues before it reaches you, and someone else absorbing the next field rename. You give up depth and platform-specific features to get it, which is a real trade rather than a free upgrade.
So the honest version: if Kalshi alone answers your question, this guide is all you need. If you are comparing prices across venues or building consensus probabilities, the maintenance grows faster than the platform count, and that is the point where it stops being worth owning yourself.
Related reading
Watching more than one venue?
Kalshi, Polymarket and Novig through one schema, with the same event matched across all three — and the next field rename absorbed on our side.
Endpoints, authentication details and the 2026 migration timeline were checked against Kalshi's documentation and public developer reports in September 2026. Kalshi's API has changed repeatedly this year — verify against the official docs before building.