API + MCP live · talk to us

// PREDICTION-MARKET APIS

Kalshi API vs Polymarket API: the differences that bite

2026-07-16 · Mithril

On paper, Kalshi and Polymarket sell the same thing: binary contracts on real-world events, priced between 0 and 1, traded on a limit order book. So a reasonable engineer assumes one integration is 80% of the other. It isn't. The two APIs disagree on auth, identifiers, price formats, fees, order lifecycle, rate limiting, and error semantics — and each disagreement is a bug you will write exactly once, in production, unless you know about it in advance. This post is the list.

For deeper single-venue walkthroughs, see the Kalshi API guide and the Polymarket CLOB API guide. This one is the side-by-side.

The table

KalshiPolymarket
Base APIapi.elections.kalshi.com/trade-api/v2gamma-api.polymarket.com (metadata) + clob.polymarket.com (orders)
AuthAPI key + per-request RSA signingWallet key (L1) → derived API creds (L2); EIP-712 signed orders
Market IDHuman-readable ticker (FED-26SEP-T4.00)condition_id (market) + token_id per outcome
PricesInteger cents, 1–99Decimals, 0.01–0.99
Trading feeTaker: 0.07 × contracts × price × (1−price), rounded up to next cent; makers generally free on most marketsNo exchange trading fee on most markets; spread + Polygon gas
SettlementUSD, held at the exchangeUSDC on Polygon, on-chain
Order sidesbuy/sell × yes/no on one marketBUY/SELL against a specific outcome token
Idempotencyclient_order_id on the orderSigned order payload; check client/docs for replay behavior
StreamingWebSocket (book, tickers, fills)WebSocket (market + user channels)

Now the ones that actually bite.

1. Auth: signed requests vs signed orders

Kalshi authenticates the request: every HTTP call carries a timestamp and an RSA signature over the method and path. Polymarket authenticates in two layers: derived API credentials sign REST requests, but the order itself is an EIP-712 typed-data message signed by a wallet private key.

Why it bites: your secrets-management story is different per venue. A Kalshi key is a trading credential; a Polymarket wallet key is a trading credential and a funds-movement credential. Rotation, storage, and blast radius all differ. And the failure modes differ too — Kalshi auth errors are usually clock skew or a wrong signed path; Polymarket errors are usually chain ID, signature type, or token approvals.

2. Identifiers: tickers vs hex

FED-26SEP-T4.00 tells a human what it is. 0x26d06d9c... tells a human nothing, and the tradable unit on Polymarket is a further step removed — the per-outcome token_id. There is no shared identifier between the venues, and even "the same" event often has different resolution criteria or timestamps across them. If your system needs to know that a Kalshi market and a Polymarket market are the same bet, someone has to build and maintain that mapping. That problem gets its own post: unified market IDs.

3. Price formats: cents vs decimals

Kalshi: 42 (an integer, in cents). Polymarket: 0.42 (a decimal). The bug this produces is not hypothetical — it is the single most common cross-venue bug: a price formatter written for one venue quietly feeding the other. 0.42 sent to Kalshi fails validation (annoying, visible). 42 interpreted as a Polymarket price is out of range (also visible). The dangerous version is intermediate code that "helpfully" divides or multiplies by 100 twice. Normalize to one internal representation (decimal probability is the natural one) at the API boundary, with types that make raw venue prices unrepresentable elsewhere in your code.

4. Fees: a formula vs a spread

Kalshi charges takers a formula — 0.07 × contracts × price × (1 − price) in dollars, rounded up to the next cent — that peaks at 50¢ markets, while resting orders generally trade free on most markets. Polymarket charges no exchange trading fee on most markets; your costs are the spread, price impact, and Polygon gas.

Why it bites: displayed prices are not comparable across venues. A market showing 47¢ on Kalshi and 0.475 on Polymarket may be cheaper on Kalshi after fees or may not, depending on size and price level. Any routing decision based on raw screen prices is systematically wrong near midprice, where Kalshi's fee is largest. The full fee comparison is in Kalshi vs Polymarket fees, and this net-price comparison is exactly what smart order routing automates on every order.

5. Order lifecycle: similar shapes, different states

Both venues have resting limit orders, partial fills, and cancels. But the state machines, field names, and notification channels differ:

  • Kalshi gives you an order with a status, a fills endpoint, and a WebSocket fill channel; client_order_id is your idempotency handle.
  • Polymarket gives you an order ID from the operator, user-channel WebSocket events for placement/match/cancel, and settlement that finalizes on-chain.

Why it bites: retry and reconciliation logic written for one venue misfires on the other. A timeout on order placement is recoverable-by-resend on Kalshi (same client_order_id), but on Polymarket you should re-check open orders before re-signing and re-posting, or you risk stacking duplicate signed orders. Reconciliation must also treat Polymarket's "matched" and "settled" as distinct moments, while Kalshi's exchange-held model has no gas or chain dimension at all.

6. Rate limits: different budgets, different meters

Both venues rate-limit, but the tiers, windows, and which endpoints are expensive differ — and both evolve their published limits over time, so check the current docs rather than hard-coding numbers. The portable advice: treat 429s as a first-class response, back off with jitter, prefer WebSockets to polling for anything that changes often, and budget requests per venue independently — a shared global limiter will let one venue's burst starve the other's cancels, which is the worst possible allocation. Patterns in rate limits, order types, and websockets.

7. Error semantics: same status code, different meaning

The subtlest category. Examples of the shape (not an exhaustive catalog):

  • Validation errors arrive with different structures — Kalshi's JSON error bodies vs the CLOB's error strings — so one parser will not read both.
  • Auth failures mean different things: re-sign and retry is often right on Kalshi (clock skew), while a Polymarket signature error is usually a configuration bug that no retry will fix.
  • "Order not found" may mean canceled, expired, settled, or never accepted, and the two venues carve those cases differently.

Why it bites: error handling is where cross-venue code quietly diverges. The safe design is a per-venue adapter that translates raw errors into a small shared taxonomy (retryable / not-retryable / reconcile-first) before any shared logic sees them.

What this means for your architecture

If you take one thing from this post: do not abstract too early. The venues are similar enough to tempt you into a shared client with venue flags, and different enough that the flags metastasize. The design that survives is two thin, honest venue adapters underneath one normalized internal model — decimal prices, unified market references, a shared order state machine, a shared error taxonomy.

That normalized layer is, in effect, a product of its own — it is the one Mithril ships as a single REST API over both venues, with unified mkt_ IDs, fee-aware routing, and an execution receipt on every fill. Build it or buy it, but budget for it: it is where most of the engineering time in a two-venue system actually goes.

APIs change. Details above reflect public documentation as of July 2026 — always confirm against the venues' own docs before trading real money.

One API + MCP for Kalshi and Polymarket

Fee-aware routing, unified market IDs, and hard server-side risk limits — live today, free during beta.