Guides
9 min read15 views

Kalshi API Guide 2026: Authentication, Endpoints & Python Examples

A developer's guide to the Kalshi v2 API β€” base URLs, RSA-PSS authentication, public market-data and portfolio endpoints, order placement, WebSocket, and working Python you can paste and run.

LT

yesornotool Team

Kalshi API Guide 2026: Authentication, Endpoints & Python Examples

πŸ’‘ Key Takeaways

  • 1Reading Kalshi market data (markets, events, order books) is public and needs no API key; only trading and portfolio endpoints require authentication.
  • 2Current production base URL is https://external-api.kalshi.com/trade-api/v2 (the api.elections.kalshi.com alias still works); WebSocket and FIX 4.4 are also available.
  • 3Authentication is an API key pair + a per-request RSA-PSS (SHA-256) signature over "timestamp + METHOD + path", sent via the three KALSHI-ACCESS-* headers.
  • 4Prices are in cents (1–99): a yes_bid of 62 means a ~62% implied probability and a $0.62 Yes share.
  • 5Test on Kalshi's demo/sandbox first; use a unique client_order_id as an idempotency key; and prefer WebSocket over polling to stay within rate limits.

Kalshi is the first federally regulated (CFTC) prediction market exchange in the US, and unlike a lot of trading venues it ships a genuinely usable public API. If you want to pull live market prices, build a trading bot, run a Polymarket–Kalshi arbitrage scanner, or just log odds over time, this is the guide. Everything below is based on the current v2 Trade API β€” with working Python you can paste and run.

What the Kalshi API gives you

There are two halves. Reading market data is public β€” no account, no keys, no auth. You can hit the markets, events, and order-book endpoints straight away. Trading is authenticated β€” placing or cancelling orders and reading your balance/positions requires an API key pair and a signature on every request. Kalshi exposes three interfaces: a REST API (what most people use), a WebSocket feed for real-time order-book and ticker updates, and FIX 4.4 for high-throughput institutional flow.

Base URLs and environments

The current production REST base is:

https://external-api.kalshi.com/trade-api/v2

You'll also see https://api.elections.kalshi.com/trade-api/v2 in older code β€” it still resolves and, despite the "elections" name, serves every market, not just political ones. The WebSocket host is wss://external-api-ws.kalshi.com/trade-api/ws/v2.

Kalshi also runs a demo/sandbox environment with fake money β€” build and test your bot there before pointing it at real funds. Kalshi has relocated these hosts more than once, so grab the current demo base URL from Kalshi's API Environments doc rather than hard-coding an old one.

Reading market data (no auth needed)

The fastest way to confirm you're connected β€” list open markets with plain curl:

curl "https://external-api.kalshi.com/trade-api/v2/markets?limit=5&status=open"

The same in Python, printing each market's ticker and current yes/no bid:

import requests

BASE = "https://external-api.kalshi.com/trade-api/v2"

r = requests.get(f"{BASE}/markets", params={"limit": 20, "status": "open"})

for m in r.json()["markets"]:

print(m["ticker"], "|", m["title"], "| yes", m.get("yes_bid"), "no", m.get("no_bid"))

The public, key-free endpoints you'll use most:

  • GET /series β€” top-level market series (e.g. an ongoing recurring market)
  • GET /events β€” events, each grouping related markets
  • GET /markets β€” individual yes/no contracts, filterable by status, event_ticker, etc.
  • GET /markets/{ticker}/orderbook β€” full bid/ask depth for one market

Prices come back in cents (1–99): a yes_bid of 62 means the market implies a ~62% chance and a Yes share costs $0.62.

Authentication: RSA-PSS request signing

This is the part that trips people up, so here it is precisely. In your Kalshi account settings you create an API key pair β€” Kalshi shows you an Access Key ID and lets you download a private key (a .pem file). Kalshi keeps the public half; you keep the private key and sign with it.

Every authenticated request needs three headers:

  • KALSHI-ACCESS-KEY β€” your Access Key ID
  • KALSHI-ACCESS-TIMESTAMP β€” current time in milliseconds
  • KALSHI-ACCESS-SIGNATURE β€” a base64 RSA-PSS signature

You sign the string timestamp + METHOD + path, where the path includes /trade-api/v2 and excludes any query string. For example the message might be 1703123456789GET/trade-api/v2/portfolio/balance. The algorithm is RSA-PSS with SHA-256 (MGF1-SHA256, salt length equal to the digest length). Here's a complete, working signer:

import time, base64, requests

from cryptography.hazmat.primitives import hashes, serialization

from cryptography.hazmat.primitives.asymmetric import padding

HOST = "https://external-api.kalshi.com"

ACCESS_KEY = "your-access-key-id" # from Kalshi -> Settings -> API Keys

with open("kalshi_private_key.pem", "rb") as f:

private_key = serialization.load_pem_private_key(f.read(), password=None)

def signed_headers(method, path): # path includes /trade-api/v2, no query string

ts = str(int(time.time() * 1000))

message = (ts + method.upper() + path).encode()

signature = private_key.sign(

message,

padding.PSS(mgf=padding.MGF1(hashes.SHA256()),

salt_length=padding.PSS.DIGEST_LENGTH),

hashes.SHA256(),

)

return {

"KALSHI-ACCESS-KEY": ACCESS_KEY,

"KALSHI-ACCESS-TIMESTAMP": ts,

"KALSHI-ACCESS-SIGNATURE": base64.b64encode(signature).decode(),

}

Read your balance

path = "/trade-api/v2/portfolio/balance"

resp = requests.get(HOST + path, headers=signed_headers("GET", path))

print(resp.json())

Two things that cause 401s: signing the wrong path (drop the query string, keep /trade-api/v2), and a clock more than a few seconds out of sync β€” the timestamp you sign must be the same one you send.

Placing and managing orders

Everything under /portfolio is authenticated:

  • GET /portfolio/balance β€” cash balance
  • GET /portfolio/positions β€” open positions
  • GET /portfolio/orders β€” your resting orders
  • POST /portfolio/orders β€” place an order
  • DELETE /portfolio/orders/{order_id} β€” cancel

A limit order to buy 10 Yes shares at 60Β’:

import json

order = {

"ticker": "SOME-MARKET-TICKER",

"client_order_id": "my-unique-id-001", # your idempotency key

"action": "buy", # buy | sell

"side": "yes", # yes | no

"count": 10,

"type": "limit", # limit | market

"yes_price": 60, # price in cents, 1-99

}

path = "/trade-api/v2/portfolio/orders"

body = json.dumps(order)

resp = requests.post(HOST + path, data=body,

headers={**signed_headers("POST", path),

"Content-Type": "application/json"})

print(resp.json())

Always send a unique client_order_id β€” it's your idempotency key, so a retried request can't accidentally double-fill.

Real-time data over WebSocket

For a live order book or ticker you don't want to poll REST. Connect to wss://external-api-ws.kalshi.com/trade-api/ws/v2 (authenticated the same way, via headers on the upgrade request) and subscribe to channels like orderbook_delta or ticker for the tickers you care about. This is how most serious bots keep prices fresh with sub-second latency.

Rate limits, SDKs, and gotchas

  • Rate limits are tiered (Kalshi offers higher tiers for active/institutional traders). Design your bot to back off on 429s and lean on WebSocket for real-time data instead of hammering REST.
  • Official SDK: Kalshi's docs demonstrate raw requests + cryptography (as above), and there are solid community Python clients β€” but knowing the raw signing flow means you're never blocked waiting on a wrapper.
  • Cents, not dollars. Prices and P&L are in cents; a "$100 position" is 100-count at some cents price. Convert carefully.

Kalshi API vs Polymarket API

If you're building cross-platform, the two are different beasts. Polymarket is on-chain (Polygon) β€” you authenticate with a wallet signature and settle in USDC; see our Polymarket API complete guide. Kalshi is a centralized, CFTC-regulated exchange β€” you authenticate with an API key + RSA signature and settle in USD from a funded account. The data models rhyme (events β†’ markets β†’ yes/no contracts, prices in cents), which is exactly why cross-market arbitrage between them is a thing. We break down the economics in Polymarket vs Kalshi and the Polymarket–Kalshi arbitrage guide.

Don't want to build it yourself?

Plenty of tools already wrap both APIs so you get unified data, alerts, and arbitrage signals without writing a signer:

Whether you build on the raw API or buy a tool off the shelf, the Kalshi v2 API is one of the cleanest in the space β€” start on the demo environment, get a public /markets call working, then layer in signing.

This guide is informational, not financial or legal advice. API details change β€” confirm specifics against Kalshi's official documentation before going live.

Frequently Asked Questions

Is the Kalshi API free to use?

Reading market data (markets, events, order books) is completely free and needs no account or API key. Trading through the API β€” placing/cancelling orders and reading your balance and positions β€” requires a funded Kalshi account and an API key pair, but Kalshi does not charge a separate fee for API access itself.

How do you authenticate with the Kalshi API?

You create an API key pair in your Kalshi account settings, download the private key, and sign every authenticated request with RSA-PSS (SHA-256). Each request sends three headers: KALSHI-ACCESS-KEY (your key ID), KALSHI-ACCESS-TIMESTAMP (milliseconds), and KALSHI-ACCESS-SIGNATURE (base64 signature of the string timestamp + METHOD + path).

What is the Kalshi API base URL?

The current production REST base is https://external-api.kalshi.com/trade-api/v2. The older https://api.elections.kalshi.com/trade-api/v2 still works and serves all markets. WebSocket is wss://external-api-ws.kalshi.com/trade-api/ws/v2. There is also a separate demo/sandbox environment for testing with fake money.

Does Kalshi have a Python SDK?

Kalshi's official docs demonstrate direct calls with the requests and cryptography libraries rather than a single official SDK, and there are several community-maintained Python clients. Because the signing flow is simple RSA-PSS, many developers just implement it directly (see the code example above).

Can I arbitrage between Kalshi and Polymarket using their APIs?

Yes β€” both expose live prices in cents over similar event/market data models, so you can detect when the same outcome is priced differently on each and trade the gap. It requires accounts and capital on both sides plus fast data (WebSocket). Many traders use a ready-made arbitrage scanner instead of building it from scratch.

Polymarket tools worth checking out

All tools
LT

Written by

yesornotool Team

Enjoyed this article?

Share it with fellow traders