PyKalshi
The Python client for Kalshi prediction markets. WebSocket streaming, automatic retries, pandas integration, and clean interfaces for building trading systems.
from pykalshi import KalshiClient, Action, Side
client = KalshiClient()
# Place a trade
order = client.portfolio.place_order("KXBTC-25MAR15-B100000", Action.BUY, Side.YES, count_fp="10", yes_price_dollars="0.45")
order.wait_until_terminal() # Block until filled/canceled
Features
- WebSocket streaming - Real-time orderbook, ticker, and trade data with typed messages
- Automatic retries - Exponential backoff on rate limits and transient errors
- Domain objects -
Market,Order,Eventwith methods likeorder.cancel(),market.get_orderbook() - pandas integration -
.to_dataframe()on any list of results - Jupyter support - Rich HTML display for markets, orders, and positions
- Local orderbook -
OrderbookManagermaintains state from WebSocket deltas - Type safety - Pydantic models and typed exceptions throughout
Installation
pip install pykalshi
# With pandas support
pip install pykalshi[dataframe]
Get your API credentials from kalshi.com and create a .env file:
KALSHI_API_KEY_ID=your-key-id
KALSHI_PRIVATE_KEY_PATH=/path/to/private-key.key
Quick Start
Interactive demo:
examples/demo.ipynbor
Browse Markets
from pykalshi import MarketStatus, CandlestickPeriod
client = KalshiClient()
# Search markets
markets = client.get_markets(status=MarketStatus.OPEN, limit=100)
btc_markets = client.get_markets(series_ticker="KXBTC")
# Get a specific market
market = client.get_market("KXBTC-25MAR15-B100000")
print(f"{market.title}: ${market.yes_bid_dollars} / ${market.yes_ask_dollars}")
# Market data
orderbook = market.get_orderbook()
trades = market.get_trades(limit=50)
candles = market.get_candlesticks(start_ts, end_ts, period=CandlestickPeriod.ONE_HOUR)
Trading
from pykalshi import Action, Side, OrderStatus
# Check balance
balance = client.portfolio.get_balance()
print(f"${balance.balance / 100:.2f} available")
# Place an order. Orders rest on a single YES-denominated book: book_side
# "bid" is long yes, "ask" is long no, and price_dollars is always the YES leg.
order = client.portfolio.place_order(market, book_side="bid", price_dollars="0.50", count_fp="10")
# An ask at 0.17 is the same resting order as buying NO at 0.83 -- no mental
# 1-p conversion needed.
order = client.portfolio.place_order(market, book_side="ask", price_dollars="0.17", count_fp="10")
# Read direction back with book_side / outcome_side. The legacy action/side
# pair is deprecated by Kalshi and means different things on orders vs fills.
assert order.book_side.value == "ask" and order.is_ask
# Manage orders
order.wait_until_terminal() # Block until filled/canceled
order.amend(price_dollars="0.45") # Amend price (YES leg)
order.decrease(reduce_by_fp="5") # Shrink the resting size
order.cancel() # Cancel
# The legacy vocabulary still works and maps onto the same wire body:
order = client.portfolio.place_order(market, Action.BUY, Side.NO, count_fp="10", no_price_dollars="0.83")
# View portfolio
positions = client.portfolio.get_positions()
fills = client.portfolio.get_fills(limit=100)
orders = client.portfolio.get_orders(status=OrderStatus.RESTING)
Real-time Streaming
from pykalshi import Feed, TickerMessage, OrderbookSnapshotMessage
async with Feed(client) as feed:
await feed.subscribe_ticker("KXBTC-25MAR15-B100000")
await feed.subscribe_orderbook("KXBTC-25MAR15-B100000")
await feed.subscribe_trades("KXBTC-25MAR15-B100000")
async for msg in feed:
match msg:
case TickerMessage():
print(f"Price: ${msg.price_dollars}")
case OrderbookSnapshotMessage():
print(f"Book: {len(msg.yes)} yes levels, {len(msg.no)} no levels")
Local Orderbook
from pykalshi import Feed, OrderbookManager
manager = OrderbookManager()
async with Feed(client) as feed:
await feed.subscribe_orderbook(ticker)
async for msg in feed:
manager.apply(msg)
book = manager.get(ticker)
best_bid = book["yes_dollars"][0] if book["yes_dollars"] else None
pandas Integration
# Any list result has .to_dataframe()
positions_df = client.portfolio.get_positions().to_dataframe()
markets_df = client.get_markets(limit=500).to_dataframe()
fills_df = client.portfolio.get_fills().to_dataframe()
# Candlesticks and orderbooks too
candles_df = market.get_candlesticks(start, end).to_dataframe()
orderbook_df = market.get_orderbook().to_dataframe()
Error Handling
from pykalshi import InsufficientFundsError, RateLimitError, KalshiAPIError
try:
order = client.portfolio.place_order(...)
except InsufficientFundsError:
print("Not enough balance")
except RateLimitError:
pass # Client auto-retries with backoff
except KalshiAPIError as e:
print(f"{e.status_code}: {e.error_code}")
Examples
See the examples/ directory:
- demo.ipynb - Interactive notebook with rich display examples
- basic_usage.py - Browse markets and check portfolio
- place_order.py - Place and manage orders
- stream_orderbook.py - WebSocket streaming patterns
- momentum_bot.py - Simple trading bot example
Web Dashboard
A real-time web dashboard is included for browsing markets, viewing orderbooks, and monitoring your portfolio. It serves as both a development tool and a reference implementation.
pip install pykalshi[web]
uvicorn web.backend.main:app --reload
See web/ for details.
Why pykalshi?
| pykalshi | kalshi-python (official) | |
|---|---|---|
| WebSocket streaming | ✓ | — |
| Automatic retry/backoff | ✓ | — |
| Rate limit handling | ✓ | — |
| Domain objects | ✓ | — |
| pandas integration | ✓ | — |
| Jupyter display | ✓ | — |
| Local orderbook | ✓ | — |
| Typed exceptions | ✓ | — |
| Pydantic models | ✓ | — |
| Full API coverage | — | ✓ |
The official SDK is auto-generated from the OpenAPI spec. pykalshi adds the infrastructure needed for production trading: real-time data, error recovery, and ergonomic interfaces.
Links
This is an unofficial library and is not affiliated with Kalshi.
Metadata
Release files for pykalshi 2.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pykalshi-2.0.0.tar.gz | 123.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pykalshi-2.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 221.5 kB
Release files / pykalshi-2.0.0.tar.gz
| Download URL | pykalshi-2.0.0.tar.gz |
|---|---|
| Size | 123.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
0430a7f716d630d5b55070ede30b62705840a36d933c058e4b6625be153e7e6c
|
|
BLAKE2b-256 checksum How to use checksums |
f2f5f58a9d1c3aa96963db9955fd9873e481de346f466ea9d6fcbb38fef09175
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.8.23
|
Release files / pykalshi-2.0.0-py3-none-any.whl
| Download URL | pykalshi-2.0.0-py3-none-any.whl |
|---|---|
| Size | 98.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0d5b88b7069c33dc0296235dfae90988ee998d65dd2889b6f9a4c378850529a9
|
|
BLAKE2b-256 checksum How to use checksums |
487d4aeec7af5485d1e6d66d18261ca87b68afab30d798004b6e2ab57b6b445f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.8.23
|