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

Kalshi Python Tutorial: SDK, Market Data, and Candlesticks

Kalshi Python Tutorial: SDK, Market Data, and Candlesticks

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, Python 3.13+, authenticated with your API key ID and RSA private key. Everything below is working code, rechecked against Kalshi's docs on 9 August 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 package name that doesn't match its import. This tutorial gets you from zero to live Kalshi market data, candlestick history, and authenticated calls, using SDK 3.27.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) but import kalshi_python. The old kalshi-python package is deprecated. Version 3.27.0 at the time of writing, and Python 3.13+ is required.
  • 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 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()

Yes: install kalshi-python-sync, import kalshi_python. The mismatch is official, and the deprecated kalshi-python package on PyPI is the wrong one. An async twin, kalshi-python-async, exists 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 the package page will mislead you about. Its README still prints pip install kalshi-python and claims Python 3.9+, while the actual metadata for 3.27.0, published on 5 August 2026, sets requires_python to 3.13 or newer. Trust the metadata. 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.

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?

Mostly. 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 it as the one that might change. An account and API keys are needed for trading, portfolio data, and any WebSocket streaming.

What is the official Kalshi Python SDK called?

Install kalshi-python-sync (or kalshi-python-async), then import kalshi_python. The similarly named kalshi-python package is the deprecated predecessor. Version 3.27.0 was current at the time of writing, and its metadata requires Python 3.13 or newer even though the README still says 3.9.

What candlestick intervals does Kalshi support?

Exactly three: 1-minute, 1-hour, and 1-day, passed as period_interval values of 1, 60, and 1440. Batch endpoints cover up to 100 markets per request, and event-level candles aggregate a whole event.

How far back does Kalshi data go?

Kalshi documents a target window of three months for live endpoints; everything older moves behind the /historical/ namespace, with a moving boundary you can read at any time from GET /historical/cutoff. Serious backtesting pipelines check the cutoff first and combine both sources.

Can I stream Kalshi prices without an account?

No. The WebSocket requires signed authentication at connection time even for public-data channels. Without an account, poll the public REST endpoints instead.

Conclusion

Kalshi in Python is a genuinely pleasant integration once you know its quirks: the package and import name mismatch, 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.