BlogPrediction markets

Kalshi Python SDK Setup and a Safer Way to Wrap It

A Kalshi Python SDK is a library that wraps Kalshi's API in Python functions, so you call a method instead of building requests, signing them and parsing JSON yourself. Which SDK is official, what it's called on PyPI, which version is current and how it authenticates are all set by Kalshi, so start from the links in Kalshi's own API documentation, not from a search result.

Once you have the right package, the useful work is around it: testing against a demo environment before real money is involved, pinning the version, and keeping the library behind a small interface of your own. That's what this guide covers.

Finding the right package

Searches for kalshi-python are usually looking for a package by roughly that name. Before you install anything:

  1. Follow the link from Kalshi's docs. Package names on PyPI are first come, first served, and a name that sounds official doesn't make a package official.
  2. Check who publishes it and when it was last released. A wrapper that hasn't kept up with the API will fail in confusing ways.
  3. Read what it covers. Some wrappers do market data only, some do trading, and streaming support varies.
  4. Pin the exact version in your requirements file, and upgrade on purpose after reading the changelog.

If no maintained SDK covers what you need, plain HTTP with the requests or httpx library is a perfectly good choice. Kalshi's docs describe the requests either way.

Use the Kalshi demo environment first

Kalshi has documented a separate demo environment for testing, with its own address and its own credentials. Check its docs for whether that's still current and how it works before relying on it. When it's available, it's the right place for everything up to your first real order: parsing, order placement, cancel logic and error handling.

Keep the switch between demo and live in one setting, not scattered through your code:

import os

ENVIRONMENTS = {
    "demo": os.environ.get("KALSHI_DEMO_BASE_URL"),
    "live": os.environ.get("KALSHI_LIVE_BASE_URL"),
}

def base_url() -> str:
    env = os.environ.get("KALSHI_ENV", "demo")  # default to demo, never to live
    url = ENVIRONMENTS.get(env)
    if not url:
        raise RuntimeError(f"no base URL configured for KALSHI_ENV={env!r}")
    return url

Both addresses come from Kalshi's docs and live in your environment, so the code holds no addresses at all. Defaulting to demo means a missing setting fails safe. Demo credentials and live credentials are separate secrets, and our guide to handling a Kalshi API key covers storing them.

Searches for the "kalshi demo api" often come from people whose code works in demo and fails live. The usual causes are a mixed-up key, a different address, or a market that exists in one environment and not the other. Log the environment name with every request and you'll spot it in a minute.

Wrap the SDK behind your own interface

The most useful thing you can do with any Kalshi Python SDK is keep it out of your strategy code. Define the few operations you need, then write one adapter that calls the library:

from typing import Protocol

class Market(Protocol):
    def best_yes(self, ticker: str) -> tuple[int | None, int | None]:
        """Best yes bid and ask in cents, or None when a side is empty."""
        ...

class FakeMarket:
    def __init__(self, quotes: dict[str, tuple[int | None, int | None]]):
        self.quotes = quotes

    def best_yes(self, ticker):
        return self.quotes.get(ticker, (None, None))

def spread_cents(market: Market, ticker: str) -> int | None:
    bid, ask = market.best_yes(ticker)
    if bid is None or ask is None:
        return None
    if not (1 <= bid < ask <= 99):
        raise ValueError(f"unexpected quote for {ticker}: {bid}/{ask}")
    return ask - bid

print(spread_cents(FakeMarket({"EXAMPLE": (41, 44)}), "EXAMPLE"))  # 3

The real adapter, the one that calls the SDK, is the only file that changes when the library does. Your tests run on FakeMarket, with no network and no key. And the checks in spread_cents catch a crossed or out-of-range quote before it reaches a model: a bid of 41 and an ask of 44 cents is a 3-cent spread, or a midpoint of 42.5%.

Things an SDK won't do for you

  • Respect limits by itself. Some wrappers retry, some don't. Read what yours does on a rate-limit error, and add backoff if it doesn't.
  • Keep prices honest about age. Record when you received each quote. A cached object from ten minutes ago looks exactly like a fresh one.
  • Decide risk. Position caps, loss limits and a way to stop everything belong in your code. Our guide to what Kalshi bots need covers that part, and why trading event contracts puts real money at risk.

Where ZippyOddsAPI fits

ZippyOddsAPI is independent and not affiliated with Kalshi. It's a read-only sports odds API: it doesn't place orders, and its documented /v1/prediction-contracts endpoint returns raw contract and best bid/offer observations from Polymarket US, marked raw_only: true with no payout or EV model. Where a Python project needs sportsbook prices alongside prediction-market data, ZippyOddsAPI returns those over plain HTTP with a Bearer header, so the same adapter pattern above works for it too. The ZippyOddsAPI docs list every endpoint and its credit cost.

For a comparison of venues and what each one offers developers, see the prediction market API overview.