NEW: Live arbitrage across 10+ prediction markets.Arbitrage →
← Index
Prediction MarketsAug 6, 2026

Kalshi Python Tutorial (2026)

Kalshi Python Tutorial (2026)

The Short Answer

Most market data needs no account: plain requests against https://external-api.kalshi.com/trade-api/v2. Trading needs the official Kalshi Python SDK: pip install kalshi-python-sync, imported as kalshi_python_sync, Python 3.13+, authenticated with your API key ID and RSA private key. Everything below is working code, rechecked against Kalshi's docs and PyPI on 26 September 2026.

The Kalshi Python stack is the most polished in prediction markets: an official Kalshi Python SDK, an OpenAPI-generated client, a real sandbox environment, and market data you can read without an account. It also carries a few traps that catch nearly everyone once, starting with a deprecated package that sits one suffix away from the current one. This tutorial gets you from zero to live Kalshi market data, candlestick history, and authenticated calls, using SDK 3.30.0 and the endpoints as Kalshi documents them today.

Key Takeaways

  • Public market data needs no authentication: markets and candlesticks are plain GET requests you can make in your first minute. The order book is the one endpoint that documents signed headers, even though it still answers without them.
  • The 2026 SDK trap: install kalshi-python-sync (or -async), not the deprecated kalshi-python, and import it as kalshi_python_sync. Version 3.30.0 as of September 2026, and Python 3.13+ is required; on older Python, pip quietly installs an outdated release.
  • Authenticated calls sign each request with your RSA private key; the two classic bugs are using seconds instead of milliseconds and signing the query string (don't).
TaskHowAuth
List marketsGET /marketsNone
Order bookGET /markets/{ticker}/orderbookNone in practice
CandlesGET /series/{s}/markets/{t}/candlesticksNone
Balance, ordersSDK: KalshiClientAPI key
StreamingWebSocketAPI key
Polling headroomToken bucket, 10 per callTier-based

Step 1: Market Data with Plain Requests (No Account)

pip install requests
import requests

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

markets = requests.get(f"{BASE}/markets", params={"limit": 5}).json()
for m in markets["markets"]:
    print(m["ticker"], "-", m.get("title", ""))

That's live, regulated-exchange market data with zero signup. The API organizes everything in a hierarchy, where a series contains events and an event contains markets (single binary contracts), and the markets endpoint supports cursor pagination: limit defaults to 100 and tops out at 1000, so pass the returned cursor back until it comes back empty.

One modernization to know: Kalshi is migrating from integer-cent prices to fixed-point dollar strings. Responses now carry *_dollars fields (like "0.1200", supporting subpenny precision) alongside legacy cent fields. New code should read the dollar-string fields.

Step 2: The Order Book (Bids Only, On Purpose)

ticker = markets["markets"][0]["ticker"]

resp = requests.get(f"{BASE}/markets/{ticker}/orderbook", params={"depth": 3})
book = resp.json()["orderbook_fp"]

print("yes bids:", book["yes_dollars"])
print("no  bids:", book["no_dollars"])

Two details are doing the work there. The response key is orderbook_fp, not orderbook, and its sides are yes_dollars and no_dollars; each level is a two-element list of strings, price then quantity. And Kalshi's book shows bids only, which is not missing data. In a binary market, a YES bid at price X is identical to a NO ask at $1.00 minus X, so asks are implied. If your code expects an asks array, derive it from the opposite side's bids.

This endpoint also carries a caveat none of the others do, and it is worth knowing before you ship. Kalshi's market data quickstart lists the order book among the endpoints that need no API keys, and it does answer unauthenticated. But at the time of writing its API reference page declares KALSHI-ACCESS-KEY, KALSHI-ACCESS-SIGNATURE and KALSHI-ACCESS-TIMESTAMP as required, where /markets and the candlestick endpoints declare no security at all. Generated SDK clients can therefore demand a key here even when raw requests does not, and the exchange could start enforcing what it has already published. Route this call through the same helper as your authenticated ones so adding headers stays a one-line change.

Step 3: Candlestick History

series = "KXHIGHNY"  # a series ticker; find them via /series or market objects

candles = requests.get(
    f"{BASE}/series/{series}/markets/{ticker}/candlesticks",
    params={
        "start_ts": 1735689600,   # unix seconds
        "end_ts":   1738368000,
        "period_interval": 60,    # exactly 1, 60, or 1440 (minutes)
    },
).json()

Three intervals exist, 1 (minute), 60 (hour) and 1440 (day), and nothing else. Batch and event-level variants exist too, the batch one taking up to 100 tickers per request. The trap for backtesters: Kalshi documents a target window of three months for live data, and anything older sits behind a dedicated /historical/ namespace whose boundary moves. Query GET /historical/cutoff first and route your requests accordingly, or your backtest will silently miss everything older than a quarter.

Step 4: The Official Kalshi Python SDK (Mind the Name)

pip install kalshi-python-sync
from kalshi_python_sync import Configuration, KalshiClient

config = Configuration(host="https://api.elections.kalshi.com/trade-api/v2")
with open("path/to/private_key.pem", "r") as f:
    private_key = f.read()
config.api_key_id = "your-api-key-id"
config.private_key_pem = private_key

client = KalshiClient(config)
balance = client.get_balance()

Install kalshi-python-sync and import kalshi_python_sync: the import is the package name with underscores. The deprecated kalshi-python package on PyPI is the wrong one, and if it is still installed, import kalshi_python quietly loads it instead of the current SDK. An async twin, kalshi-python-async, imports as kalshi_python_async for asyncio codebases. And despite the elections subdomain in the host above, it serves all markets; external-api.kalshi.com works here too.

Two things to know about the package page. The metadata for 3.30.0, published on 15 September 2026, sets requires_python to 3.13 or newer, and on an older interpreter pip does not fail: it quietly installs 3.2.0, the last release that supports your Python, so run pip show kalshi_python_sync to confirm what you got. Kalshi's own SDK page also writes the install line with underscores, pip install kalshi_python_sync, which pip resolves to the same package, so both spellings work and neither is a typo. Kalshi warns on that page that the SDKs can lag the API, so when a field looks missing, check the OpenAPI spec before assuming your code is broken. The TypeScript SDK and the V2 order methods are covered in our Kalshi SDK guide.

How Authentication Actually Works

Keys come from the API Keys section of your Profile Settings page: a key ID plus an RSA private key shown once. Every authenticated request carries three headers, KALSHI-ACCESS-KEY, KALSHI-ACCESS-TIMESTAMP (milliseconds) and KALSHI-ACCESS-SIGNATURE, where the signature is RSA-PSS with SHA-256 over timestamp + METHOD + path, and the path is taken without query parameters. The path includes the /trade-api/v2 prefix. The SDK does all of this for you, which is the main reason to use it; if you sign manually, the two bugs that cause nearly every 401 are seconds-instead-of-milliseconds and leaving the query string in the signed path.

Test everything against the demo environment first: https://external-api.demo.kalshi.co/trade-api/v2, with its own separate account and mock funds. Credentials do not cross over in either direction, so a demo key against production fails with a 401 that looks exactly like a broken signature. One more boundary to know: unlike REST, Kalshi's WebSocket at wss://external-api-ws.kalshi.com/trade-api/ws/v2 requires authentication at the handshake even for public channels, so streaming needs an account where plain polling does not.

Rate Limits: What a Python Polling Loop Actually Gets

Nothing in this tutorial hits a limit, but a loop will, and Kalshi does not publish a simple requests-per-second cap. It runs a token bucket instead: each call costs tokens, 10 by default, drawn from two independent budgets, one for reads and one for writes, that refill continuously and let unused capacity pile up into a burst. At the time of writing the entry-level Basic tier refills 200 read and 100 write tokens per second, which works out to roughly 20 GETs a second, and Kalshi lists six tiers above it. The first upgrade, Advanced, is a self-service API call; the ones past it are granted from trailing 30-day volume. Those numbers move, so read Kalshi's rate limit page before you size anything around them.

Going over returns HTTP 429 with no cooldown and no penalty on your account, so the whole fix is to wait and try again:

import time

def get_json(path, **params):
    for attempt in range(5):
        r = requests.get(f"{BASE}{path}", params=params)
        if r.status_code != 429:
            r.raise_for_status()
            return r.json()
        time.sleep(2 ** attempt)
    raise RuntimeError(f"rate limited five times on {path}")

Wrap every read in that and the order book caveat from Step 2 becomes trivial to handle: when signed headers start being enforced, you add them in one function rather than in twenty call sites.

Frequently Asked Questions

Is the Kalshi API free to use with Python?

Yes, for most of it. Markets, candlesticks and the batch candle endpoints are public REST calls that need no account at all. The order book answers without credentials today, but its API reference declares signed headers, so treat that one as liable to change. Trading, portfolio data and WebSocket streaming need an account and API keys.

What is the official Kalshi Python SDK called?

The official package is kalshi-python-sync, or kalshi-python-async for the async version, imported as kalshi_python_sync or kalshi_python_async. The similarly named kalshi-python is the deprecated predecessor. Version 3.30.0 was current in September 2026 and requires Python 3.13 or newer. Predictefy's Python SDK is an option when you need 15+ venues.

What candlestick intervals does Kalshi support?

Kalshi supports exactly three: one minute, one hour and one day. You pass them as period_interval values of 1, 60 and 1440. No other interval is available. Batch endpoints cover up to 100 markets per request, and event-level candles aggregate a whole event.

How far back does Kalshi data go?

Live endpoints target a documented window of three months. Everything older moves behind the /historical/ namespace, and the boundary between the two moves over time. You can read it at any point from GET /historical/cutoff. Serious backtesting pipelines check the cutoff first, then combine both sources.

Can I stream Kalshi prices without an account?

No. The Kalshi WebSocket requires signed authentication at connection time, even for public-data channels, so there is no anonymous stream. Without an account you have to poll the public REST endpoints instead. Markets, candlesticks and the batch candle endpoints all answer without credentials.

Conclusion

Kalshi in Python is a genuinely pleasant integration once you know its quirks: the deprecated look-alike package, the milliseconds-and-no-query-params signing rules, an order book that documents auth it doesn't currently demand, and the three-month live-data window. Start with the free endpoints, move to the demo environment before real keys, and let the SDK own the signing.

One caveat that has nothing to do with code. This is a tutorial, not financial, tax or legal advice. An API key trades real money on a real exchange, and Kalshi's terms and eligibility rules apply to a script exactly as they do to someone tapping buttons in the app.

For the full API surface, including WebSocket channels and FIX connectivity, see our complete Kalshi API guide. Building the other side of a cross-venue setup? The Polymarket Python tutorial mirrors this one, and together they're the data layer for the strategies in our arbitrage guide.