A professional Python SDK for the Kalshi prediction markets API
Project description
kalshi-sdk
A professional, spec-first Python SDK for the Kalshi prediction markets API.
- Full coverage of the Kalshi REST API (89 endpoints across 19 resources) and WebSocket API (12 message types).
- Sync and async clients sharing one transport — no thread-pool wrapping.
- Typed end-to-end: Pydantic v2 models,
mypy --strictclean, shipspy.typed. - Spec-aligned with drift guards: hard-fail contract tests catch query, body, and WebSocket payload drift on every commit.
- Safe defaults: only idempotent verbs (
GET/HEAD/OPTIONS) retry;POST/DELETEnever retry to avoid duplicate orders or cancels.
Install
pip install kalshi-sdk
Requires Python 3.12+.
Quickstart — sync
from kalshi import KalshiClient
with KalshiClient(
key_id="your-key-id",
private_key_path="~/.kalshi/private_key.pem",
) as client:
page = client.markets.list(status="open", limit=10)
for market in page:
print(market.ticker, market.yes_bid, market.yes_ask)
Quickstart — async
import asyncio
from kalshi import AsyncKalshiClient
async def main() -> None:
async with AsyncKalshiClient(
key_id="your-key-id",
private_key_path="~/.kalshi/private_key.pem",
) as client:
# list_all() yields across pages — works directly with `async for`.
async for market in client.markets.list_all(status="open"):
print(market.ticker, market.yes_bid)
asyncio.run(main())
Authentication
Kalshi uses RSA-PSS request signing. Generate a key pair in your Kalshi account settings and download the PEM.
From environment variables
export KALSHI_KEY_ID="..."
export KALSHI_PRIVATE_KEY_PATH="~/.kalshi/private_key.pem"
# or, inline:
export KALSHI_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----..."
# Optional:
export KALSHI_DEMO=true # use the demo (sandbox) environment
export KALSHI_API_BASE_URL=... # override base URL
from kalshi import KalshiClient
client = KalshiClient.from_env()
from_env() returns an unauthenticated client if no credentials are set.
Public endpoints still work; private endpoints raise AuthRequiredError.
Demo vs production
KalshiClient(key_id="...", private_key_path="...", demo=True) # sandbox
KalshiClient(key_id="...", private_key_path="...") # production (default)
Public / unauthenticated usage
You don't need credentials to read public market data:
from kalshi import KalshiClient
with KalshiClient(demo=True) as client:
assert client.is_authenticated is False
markets = client.markets.list(status="open", limit=5)
Placing orders
from kalshi import KalshiClient
with KalshiClient.from_env() as client:
order = client.orders.create(
ticker="EXAMPLE-25-T",
side="yes",
action="buy",
count=10,
yes_price="0.65", # 65 cents
time_in_force="good_till_canceled",
client_order_id="my-uuid", # idempotency key
)
print(order.order_id, order.status)
Prices are decimal dollars (e.g. "0.65") per the Kalshi spec. Internally
the SDK uses Decimal via the DollarDecimal type — never float.
WebSocket streaming
import asyncio
from kalshi import KalshiAuth, KalshiConfig
from kalshi.ws.client import KalshiWebSocket
async def main() -> None:
auth = KalshiAuth.from_key_path("your-key-id", "~/.kalshi/private_key.pem")
config = KalshiConfig.demo() # or KalshiConfig.production()
ws = KalshiWebSocket(auth=auth, config=config)
async with ws.connect() as session:
stream = await session.subscribe_orderbook_delta(tickers=["EXAMPLE-25-T"])
async for msg in stream:
print(msg)
asyncio.run(main())
Available channels: ticker, trade, orderbook_delta, fill,
market_positions, user_orders, order_group, market_lifecycle,
multivariate, multivariate_lifecycle, communications.
Error handling
All SDK errors inherit from KalshiError:
from kalshi import (
KalshiError,
KalshiAuthError, # 401 / 403
AuthRequiredError, # called private endpoint without credentials
KalshiNotFoundError, # 404
KalshiValidationError, # 400 (has .details: dict[str, str])
KalshiRateLimitError, # 429 (has .retry_after: float | None)
KalshiServerError, # 5xx
# WebSocket-specific:
KalshiWebSocketError,
KalshiConnectionError,
KalshiSequenceGapError,
KalshiBackpressureError,
KalshiSubscriptionError,
)
try:
client.markets.get("DOES-NOT-EXIST")
except KalshiNotFoundError as e:
print(e.status_code, str(e))
Retry policy
- Retries on
429,502,503,504,500(idempotent GET only). POSTandDELETEare never retried — duplicate order / cancel risk.- Exponential backoff with jitter, capped at
retry_max_delay. Retry-Afteris honored but capped atretry_max_delayto prevent a server-controlled stall.
Tune via KalshiConfig:
from kalshi import KalshiClient, KalshiConfig
config = KalshiConfig(
timeout=10.0,
max_retries=5,
retry_base_delay=0.5,
retry_max_delay=15.0,
)
client = KalshiClient(key_id="...", private_key_path="...", config=config)
Pagination
List endpoints return a Page[T] you can iterate, plus a cursor for manual
control. For "give me everything" use list_all():
# Manual cursor loop:
page = client.markets.list(status="open", limit=200)
while True:
for market in page:
...
if not page.has_more:
break
page = client.markets.list(status="open", limit=200, cursor=page.cursor)
# Or just:
for market in client.markets.list_all(status="open"):
...
Resources
| Kalshi REST OpenAPI spec | https://docs.kalshi.com/openapi.yaml |
| Kalshi WebSocket AsyncAPI spec | https://docs.kalshi.com/asyncapi.yaml |
| Production base URL | https://api.elections.kalshi.com/trade-api/v2 |
| Demo base URL | https://demo-api.kalshi.co/trade-api/v2 |
| Changelog | CHANGELOG.md |
| Issues | https://github.com/TexasCoding/kalshi-python-sdk/issues |
License
MIT — see LICENSE.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file kalshi_sdk-1.0.0.tar.gz.
File metadata
- Download URL: kalshi_sdk-1.0.0.tar.gz
- Upload date:
- Size: 1.1 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a52a5f06788e86e1bb62946e7d024ed19bae7a9bd5c8fdf7c81c6e964f0aeb73
|
|
| MD5 |
9518b06adf2c3eb7674a1ef9772631f4
|
|
| BLAKE2b-256 |
6f77bd37d4aba0b2c5c21250869d2ffd69a37f84a395dcac7e906314ac6c84d4
|
Provenance
The following attestation bundles were made for kalshi_sdk-1.0.0.tar.gz:
Publisher:
release.yml on TexasCoding/kalshi-python-sdk
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
kalshi_sdk-1.0.0.tar.gz -
Subject digest:
a52a5f06788e86e1bb62946e7d024ed19bae7a9bd5c8fdf7c81c6e964f0aeb73 - Sigstore transparency entry: 1500754317
- Sigstore integration time:
-
Permalink:
TexasCoding/kalshi-python-sdk@5a738bfdaf0ac168e96b6664158c53ea5b27c2f5 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/TexasCoding
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@5a738bfdaf0ac168e96b6664158c53ea5b27c2f5 -
Trigger Event:
push
-
Statement type:
File details
Details for the file kalshi_sdk-1.0.0-py3-none-any.whl.
File metadata
- Download URL: kalshi_sdk-1.0.0-py3-none-any.whl
- Upload date:
- Size: 95.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7f10f4e7594091d39a1b806649d6ce17e8acb777fc42399e8dc10f351cb70fa2
|
|
| MD5 |
704366ccfc27d2130ebc3544556b26e5
|
|
| BLAKE2b-256 |
2b9f4940f1b3a0a9e850d6fa4111c1d528a7aada4c0cba246059fcf7f872b19a
|
Provenance
The following attestation bundles were made for kalshi_sdk-1.0.0-py3-none-any.whl:
Publisher:
release.yml on TexasCoding/kalshi-python-sdk
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
kalshi_sdk-1.0.0-py3-none-any.whl -
Subject digest:
7f10f4e7594091d39a1b806649d6ce17e8acb777fc42399e8dc10f351cb70fa2 - Sigstore transparency entry: 1500754510
- Sigstore integration time:
-
Permalink:
TexasCoding/kalshi-python-sdk@5a738bfdaf0ac168e96b6664158c53ea5b27c2f5 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/TexasCoding
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@5a738bfdaf0ac168e96b6664158c53ea5b27c2f5 -
Trigger Event:
push
-
Statement type: