Developer and API

Kalshi's API is free to read and will not talk to your browser

Kalshi's API needs no key to read and answers in under 300ms. Send an Origin header and it returns 403. Browser JS cannot call it.

Last checked 20 August 2026 · 5 sources

Kalshi's public API needs no key to read from. On 20 August 2026 the markets endpoint answered in 178ms and the events endpoint in 273ms, both unauthenticated. But send a browser Origin header with the identical request and it returns HTTP 403 Forbidden, in 173ms, with no body.

That single behaviour decides your architecture. You cannot call Kalshi's API from client-side JavaScript. Not with a fetch from your page, not from a front-end framework, not from a browser extension talking to the API directly. Anything that reads Kalshi has to run server side.

We hit this building our own scanner, and it is why the live feed on this site is a JSON file published from a machine rather than a fetch made by your browser.

The 403, exactly as we reproduced it

Same URL, same second, one header different.

RequestResultTime
GET /trade-api/v2/markets?limit=1&status=openHTTP 200, JSON body178ms
The same, plus Origin: https://predictionedge.proHTTP 403 Forbidden, empty body173ms

There is no CORS negotiation to win here and no header you can add on your side to fix it. The presence of Origin is what the request is being judged on, and Origin is set by the browser, not by you. A browser cannot be persuaded not to send it.

We reproduced this on two separate days with two different origin values, so it is not a transient block on one domain.

What this means in practice: if you want Kalshi prices on a web page, you need a server, a scheduled job, or a serverless function in between. It reads Kalshi, it writes something your page can fetch. There is no way around it that runs purely in the browser.

The three endpoints worth knowing

Base is https://api.elections.kalshi.com/trade-api/v2.

EndpointWhat it is forCache-Control
/events?with_nested_markets=truethe catalogue, one call per event with its markets insidepublic, max-age=15
/marketsflat market list, no event contextpublic, max-age=15
/exchange/statusis the exchange open, is trading activepublic, max-age=1

The events endpoint with with_nested_markets=true is the efficient one. One request gives you an event and every market under it, which saves a round trip per event. Our collector uses it with limit=200.

Fifteen seconds of cache is genuinely short, and much shorter than Polymarket's Gamma at 300 seconds. Per-minute polling sits comfortably inside what Kalshi is already caching for, and there is no benefit to going faster than 15 seconds because you will be served the same copy.

Paging is a cursor, and it is not optional

Kalshi returns a cursor field alongside your results. You pass it back as ?cursor= on the next request to get the following page. When the cursor comes back empty, you have everything.

This is different from Polymarket's Gamma, which pages by numeric offset. Code written for one will not page the other. Our collector pulled 7,936 tradeable markets on 20 August 2026 this way.

There is no total count in the response. You do not know how many pages remain until the cursor runs dry, so write your loop with a hard page cap or a malformed response will spin it forever.

The field names are unusual, and the suffixes matter

Kalshi's market objects use explicit unit suffixes, which is more disciplined than most APIs and catches people who assume cents:

  • no_ask_dollars, no_bid_dollars, and the yes equivalents. Dollars, so

0.94 means 94 cents, not 94 dollars and not 94 cents expressed as 94.

  • liquidity_dollars, what the book can absorb.
  • notional_value_dollars, the contract's face value.
  • open_interest_fp, open interest.
  • can_close_early, whether the market can settle before its stated close.
  • is_provisional, which flags a market whose terms are not final.

On the event object, two fields are worth more than they look:

  • settlement_sources, the documents Kalshi will read to decide the outcome.

This is the resolution source, and it is the single most useful field on the API for anyone who has been burned by a carveout.

  • mutually_exclusive, whether the markets under an event are exclusive. This

changes the arithmetic completely if you are pricing a basket.

Fees: the taker rate is published, the maker rate is not

Kalshi's taker fee is documented and settled:

fee = roundup(0.07 x contracts x P x (1 - P))

The round-up is to the whole cent and, importantly, the contract count sits inside the ceiling, so the rounding applies to the whole trade rather than per contract. On one contract at 50 cents the formula gives 1.75 cents and you pay 2. On a thousand contracts it gives $17.50 and you pay $17.50. Small orders on cheap contracts are punished hardest by this, which is the opposite of the usual assumption.

Kalshi's maker fee is genuinely unresolved and we will not publish a rate for it. Their newsroom states that resting orders are fee-exempt. Their help centre states that maker fees are charged. Nobody has reconciled the two publicly. Our own scanner models it at a 0.0175 coefficient as our working assumption so the arithmetic has something to run on, and that is a fact about our code, not a fact about Kalshi. Do not take it as one, and do not repeat it as though Kalshi published it.

If your bot's profitability depends on the maker rate, you are depending on a number that no primary source establishes.

Kalshi against Polymarket, for a developer

KalshiPolymarket Gamma
Key needed to readnono
Callable from a browserno, 403 on Originyes
Cache window15s300s
Pagingcursornumeric offset
Price unitsdollars, suffixed field namesdecimal on bestAsk
Resolution source exposedyes, settlement_sourcesin description prose
Markets we collected 20 Aug 20267,936 tradeable600, across 6 pages, tag filtered

The 600 is our own slice, not Polymarket's total. We filter to politics, economics, crypto and sports and stop at 6 pages.

What we could not verify

  • The rate limit. We have never been throttled at a 60 second cycle and we

have not gone looking for the ceiling. We do not know the number.

  • Why the Origin check exists. We observe the 403. We have no statement

from Kalshi about the intent behind it, and we are not going to speculate about their reasoning.

  • Whether authenticated requests behave differently on Origin. All our

testing is unauthenticated reads. We have not held an API credential.

  • The maker rate. Covered above. It is unresolved at the source, not

merely unknown to us.

Questions people actually ask

Is the Kalshi API free? Reading is free and needs no key. On 20 August 2026 unauthenticated calls to the markets and events endpoints both returned HTTP 200, in 178ms and 273ms. We have not established the rate limits, so free does not mean unlimited.

Why does my Kalshi API call return 403 in the browser? Because it is a browser. Kalshi returns HTTP 403 to any request carrying an Origin header, which browsers always attach and cannot be told to omit. The identical request from a server returns 200. You need a backend in between.

How do I get all Kalshi markets? Call /events?with_nested_markets=true&limit=200 and follow the cursor field back into the next request until it comes back empty. We collected 7,936 tradeable markets this way on 20 August 2026. Cap your loop, because no total count is returned.

What does Kalshi charge in fees? The taker fee is roundup(0.07 x contracts x P x (1 - P)), rounded up to the whole cent across the whole trade. At 50 cents on one contract that is 2 cents against a formula value of 1.75. The maker rate is contradicted by Kalshi's own two documents and we do not publish a figure.

Can I trade through the Kalshi API? This page covers read access only, which is what we have used. Placing orders requires credentials we have never held, so we cannot describe that flow from experience and will not describe it from documentation as though we had.

Which is easier to build against, Kalshi or Polymarket? Polymarket, if you want anything client side, because Kalshi's 403 rules the browser out entirely. Kalshi, if you want fresh prices and explicit resolution sources, because it caches for 15 seconds against Gamma's 300 and exposes settlement_sources as a field rather than buried in prose.

Sources

Timings are single measurements from one machine on one connection, taken on the date stamped beside each. They indicate order of magnitude, not a benchmark. The 403 result was reproduced on two separate days with two different Origin values.

  1. https://api.elections.kalshi.com/trade-api/v2/events Kalshi events endpoint with nested markets. Called direct 20 Aug 2026 11:42 UTC. HTTP 200, 273ms, 18,751 bytes, Cache-Control public max-age=15.
  2. https://api.elections.kalshi.com/trade-api/v2/markets Kalshi markets endpoint. Called direct 20 Aug 2026 11:42 UTC. HTTP 200, 178ms, Cache-Control public max-age=15. The same URL with an Origin request header returned HTTP 403 Forbidden in 173ms.
  3. https://api.elections.kalshi.com/trade-api/v2/exchange/status Kalshi exchange status. Called direct 20 Aug 2026 11:42 UTC. HTTP 200, 196ms, 642 bytes, Cache-Control public max-age=1.
  4. https://docs.kalshi.com/ Kalshi developer documentation. Taker fee formula, sub-penny fee rounding, whole-cent rebate accumulator.
  5. PredictionEdge scanner, scanner/kalshi_intel.py : our own client. 7,936 tradeable markets collected 20 Aug 2026.