Prediction Market API Tutorial: Build Your First Trading Script in Python
Step-by-step Python tutorial for connecting to Kalshi and Polymarket APIs. Fetch markets, check prices, and place your first order.
If you have been trading prediction markets manually — refreshing pages, eyeballing prices, clicking buy buttons — you are leaving money on the table. The edge in prediction markets often comes from speed and consistency: checking dozens of markets every few minutes, spotting mispriced contracts before the crowd, and executing without hesitation. You cannot do that by hand. You need code.
This tutorial walks you through building a practical Python script that connects to the Kalshi API, fetches live market data, analyzes prices, and places orders. By the end, you will have a working price-checker that runs on a schedule and a foundation you can extend into a full trading bot.
We run automated trading systems on prediction markets every day. This guide reflects the patterns and pitfalls we have encountered in production. No toy examples — everything here is designed to actually work.
Prerequisites
Before you start, you will need:
- Python 3.9+ installed
- A Kalshi account with API access enabled
- Basic Python knowledge (variables, functions, HTTP requests)
- The
requestsandschedulelibraries (pip install requests schedule)
Step 1: Setting Up Your Kalshi API Keys
Kalshi provides a REST API that allows you to read market data, manage positions, and place orders programmatically. To get started:
- Log in to your Kalshi account
- Navigate to your account settings or profile
- Find the API section and generate an API key pair
- You will receive an API key (sometimes called email) and API secret (your password or private key depending on the auth method)
Security note: Never hardcode your API credentials in your script. Use environment variables or a .env file.
import os
KALSHI_API_BASE = "https://api.elections.kalshi.com/trade-api/v2"
KALSHI_EMAIL = os.environ.get("KALSHI_EMAIL")
KALSHI_PASSWORD = os.environ.get("KALSHI_PASSWORD")
Set your environment variables before running:
export KALSHI_EMAIL="[email protected]"
export KALSHI_PASSWORD="your-api-password"
Step 2: Authenticating with the Kalshi API
Kalshi uses token-based authentication. You log in with your credentials and receive a session token that you include in subsequent requests.
import requests
def get_kalshi_token():
"""Authenticate with Kalshi and return a session token."""
url = f"{KALSHI_API_BASE}/login"
payload = {
"email": KALSHI_EMAIL,
"password": KALSHI_PASSWORD
}
response = requests.post(url, json=payload)
response.raise_for_status()
data = response.json()
token = data["token"]
member_id = data["member_id"]
print(f"Authenticated successfully. Member ID: {member_id}")
return token
# Create a session with auth headers
def create_session():
"""Create a requests session with authentication."""
token = get_kalshi_token()
session = requests.Session()
session.headers.update({
"Authorization": f"Bearer {token}",
"Content-Type": "application/json"
})
return session
Important: Tokens expire. In production, you should handle token refresh — catch 401 responses and re-authenticate automatically.
Step 3: Fetching Market Data
Now let’s pull live market data. Kalshi organizes markets into events (e.g., “S&P 500 range on Feb 22”) and markets (individual contracts within that event, like “Will S&P 500 close above 6,000?”).
def get_markets(session, status="open", limit=100, cursor=None):
"""Fetch a list of markets from Kalshi."""
url = f"{KALSHI_API_BASE}/markets"
params = {
"status": status,
"limit": limit,
}
if cursor:
params["cursor"] = cursor
response = session.get(url, params=params)
response.raise_for_status()
data = response.json()
markets = data.get("markets", [])
next_cursor = data.get("cursor", None)
return markets, next_cursor
def get_market(session, ticker):
"""Fetch a single market by its ticker."""
url = f"{KALSHI_API_BASE}/markets/{ticker}"
response = session.get(url)
response.raise_for_status()
return response.json().get("market", {})
Let’s try it out:
session = create_session()
# Fetch the first batch of open markets
markets, cursor = get_markets(session, limit=10)
for market in markets:
ticker = market["ticker"]
title = market["title"]
yes_bid = market.get("yes_bid", 0)
yes_ask = market.get("yes_ask", 0)
volume = market.get("volume", 0)
print(f"{ticker}: {title}")
print(f" Yes Bid: ${yes_bid/100:.2f} | Yes Ask: ${yes_ask/100:.2f} | Volume: {volume}")
print()
This gives you a snapshot of live markets with their current prices. The yes_bid and yes_ask values are in cents (integers), so divide by 100 for dollar amounts.
Step 4: Parsing the Order Book
To understand the depth of liquidity at each price level, fetch the order book for a specific market:
def get_orderbook(session, ticker):
"""Fetch the order book for a specific market."""
url = f"{KALSHI_API_BASE}/markets/{ticker}/orderbook"
response = session.get(url)
response.raise_for_status()
return response.json().get("orderbook", {})
def display_orderbook(orderbook):
"""Display the order book in a readable format."""
yes_bids = orderbook.get("yes", [])
no_bids = orderbook.get("no", [])
print("YES side (bids to buy Yes):")
for level in yes_bids:
price = level[0]
quantity = level[1]
print(f" ${price/100:.2f} - {quantity} contracts")
print("\nNO side (bids to buy No):")
for level in no_bids:
price = level[0]
quantity = level[1]
print(f" ${price/100:.2f} - {quantity} contracts")
The order book tells you where the real liquidity sits. Thin books with wide spreads mean you should use limit orders. Thick books with tight spreads are safer for market orders. Understanding order book dynamics and pricing is critical for profitable trading.
Step 5: Placing a Limit Order
Now for the part that matters — placing an actual order. Start with paper trading or very small sizes until you are confident your code is correct. A bug in order placement code can drain your account fast.
def place_order(session, ticker, side, price_cents, count):
"""
Place a limit order on Kalshi.
Args:
session: Authenticated requests session
ticker: Market ticker (e.g., 'KXBTC-26FEB22-T50000')
side: 'yes' or 'no'
price_cents: Price in cents (1-99)
count: Number of contracts
Returns:
Order response dict
"""
url = f"{KALSHI_API_BASE}/portfolio/orders"
payload = {
"ticker": ticker,
"action": "buy",
"side": side,
"type": "limit",
"count": count,
"yes_price": price_cents if side == "yes" else None,
"no_price": price_cents if side == "no" else None,
}
# Remove None values
payload = {k: v for k, v in payload.items() if v is not None}
print(f"Placing order: {side.upper()} {count}x {ticker} @ ${price_cents/100:.2f}")
response = session.post(url, json=payload)
response.raise_for_status()
order = response.json().get("order", {})
print(f"Order placed successfully. Order ID: {order.get('order_id')}")
return order
Example usage:
# Buy 10 Yes contracts at $0.45 each
order = place_order(session, "KXSPX-26FEB22-A6000", "yes", 45, 10)
Canceling an Order
Always implement a cancel function. You will need it more often than you think.
def cancel_order(session, order_id):
"""Cancel an open order."""
url = f"{KALSHI_API_BASE}/portfolio/orders/{order_id}"
response = session.delete(url)
if response.status_code == 200:
print(f"Order {order_id} cancelled successfully.")
elif response.status_code == 404:
print(f"Order {order_id} not found -- may have already filled or been cancelled.")
else:
response.raise_for_status()
Step 6: Checking Your Positions
After placing orders, you need to know what you are holding:
def get_positions(session):
"""Fetch all open positions."""
url = f"{KALSHI_API_BASE}/portfolio/positions"
response = session.get(url)
response.raise_for_status()
positions = response.json().get("market_positions", [])
for pos in positions:
ticker = pos.get("ticker", "")
yes_count = pos.get("position", 0)
avg_cost = pos.get("total_cost", 0)
if yes_count != 0:
print(f"{ticker}: {yes_count} contracts, cost basis: ${avg_cost/100:.2f}")
return positions
Step 7: Building a Price Checker That Runs Every 5 Minutes
Let’s put it all together into a script that monitors specific markets and alerts you when prices cross a threshold:
import schedule
import time
from datetime import datetime
# Markets to watch: (ticker, fair_value_cents, threshold_cents)
WATCHLIST = [
("KXBTC-26FEB28-T100000", 55, 8), # Alert if price is 8+ cents from fair value
("KXSPX-26FEB28-A6100", 40, 10), # Alert if price is 10+ cents from fair value
]
def check_prices(session):
"""Check prices for watchlist markets and alert on opportunities."""
print(f"\n--- Price Check: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} ---")
for ticker, fair_value, threshold in WATCHLIST:
try:
market = get_market(session, ticker)
yes_bid = market.get("yes_bid", 0)
yes_ask = market.get("yes_ask", 0)
if yes_ask == 0 or yes_bid == 0:
print(f"{ticker}: No active quotes")
continue
midpoint = (yes_bid + yes_ask) / 2
edge = fair_value - midpoint
title = market.get("title", ticker)
print(f"{title}")
print(f" Bid: ${yes_bid/100:.2f} | Ask: ${yes_ask/100:.2f} | "
f"Mid: ${midpoint/100:.2f} | Your Fair: ${fair_value/100:.2f} | "
f"Edge: {edge:+.1f}c")
# Alert if edge exceeds threshold
if abs(edge) >= threshold:
side = "YES" if edge > 0 else "NO"
print(f" >>> OPPORTUNITY: Buy {side} -- {abs(edge):.0f} cent edge <<<")
except requests.exceptions.HTTPError as e:
print(f"Error fetching {ticker}: {e}")
except Exception as e:
print(f"Unexpected error for {ticker}: {e}")
def run_monitor():
"""Main monitoring loop."""
session = create_session()
# Run immediately on start
check_prices(session)
# Schedule to run every 5 minutes
schedule.every(5).minutes.do(check_prices, session)
print("\nMonitor running. Checking every 5 minutes. Press Ctrl+C to stop.")
while True:
schedule.run_pending()
time.sleep(1)
if __name__ == "__main__":
run_monitor()
Save this as kalshi_monitor.py and run it:
export KALSHI_EMAIL="[email protected]"
export KALSHI_PASSWORD="your-api-password"
python kalshi_monitor.py
The script will check your watchlist every 5 minutes and flag any markets where the current price deviates significantly from your fair value estimate.
Error Handling in Production
The code above works for learning, but production trading scripts need more robust error handling. Here are the patterns we use:
Retry Logic
import time
def api_call_with_retry(func, *args, max_retries=3, **kwargs):
"""Retry API calls with exponential backoff."""
for attempt in range(max_retries):
try:
return func(*args, **kwargs)
except requests.exceptions.HTTPError as e:
if e.response.status_code == 429: # Rate limited
wait_time = 2 ** attempt * 5
print(f"Rate limited. Waiting {wait_time}s...")
time.sleep(wait_time)
elif e.response.status_code == 401: # Token expired
print("Token expired. Re-authenticating...")
# Re-create session
raise # Let caller handle re-auth
else:
raise
except requests.exceptions.ConnectionError:
wait_time = 2 ** attempt
print(f"Connection error. Retrying in {wait_time}s...")
time.sleep(wait_time)
raise Exception(f"Failed after {max_retries} retries")
Rate Limiting
Kalshi’s API has rate limits. If you are polling many markets frequently, space out your requests:
import time
def fetch_all_markets_throttled(session, tickers, delay=0.2):
"""Fetch multiple markets with a delay between requests."""
results = {}
for ticker in tickers:
results[ticker] = get_market(session, ticker)
time.sleep(delay) # 200ms between requests
return results
Connecting to Polymarket (Brief Overview)
Polymarket uses a different architecture — it is built on the Polygon blockchain, so interacting with it programmatically involves crypto libraries rather than simple REST calls. The basic setup:
# Polymarket uses the CLOB (Central Limit Order Book) API
# You'll need: pip install py-clob-client
from py_clob_client.client import ClobClient
POLYMARKET_HOST = "https://clob.polymarket.com"
POLYMARKET_KEY = os.environ.get("POLYMARKET_API_KEY")
CHAIN_ID = 137 # Polygon mainnet
client = ClobClient(
POLYMARKET_HOST,
key=POLYMARKET_KEY,
chain_id=CHAIN_ID
)
# Fetch markets
markets = client.get_markets()
for market in markets[:5]:
print(f"{market.question} - {market.condition_id}")
Polymarket’s API documentation is available on their GitHub. The full guide to Polymarket covers the platform in more detail.
What to Build Next
This tutorial gives you the foundation. Here are practical next steps:
-
Automated fair value models. Replace the hardcoded fair values in the watchlist with models that pull external data (weather forecasts, financial data, polls) and calculate probabilities dynamically.
-
Auto-execution. When the price checker detects an opportunity above your edge threshold, have it automatically place a limit order instead of just printing an alert.
-
Position management. Build a loop that monitors your open positions and automatically places sell orders when your profit target is hit or when your model’s fair value shifts against you.
-
Multi-market scanning. Instead of a static watchlist, scan all open markets on Kalshi, filter for categories you understand, and rank them by estimated edge.
-
Logging and P&L tracking. Log every trade, every price check, and every decision to a file or database. You cannot improve what you cannot measure.
If you are serious about building a trading system, study the strategies that work in prediction markets before writing code to automate them. Automating a bad strategy just helps you lose money faster.
Common Pitfalls
- Testing with real money. Use small sizes (1-2 contracts) until your code is battle-tested. A loop that places orders instead of canceling them can drain your account in seconds.
- Ignoring fees. Every order costs $0.02 per contract. Your code needs to account for fees when calculating expected profit. A trade that looks profitable before fees might be a loser after them.
- Not handling API errors. Networks fail, tokens expire, rate limits hit. Every API call should have error handling. Silent failures are worse than loud crashes.
- Over-trading. It is easy to build a bot that trades constantly. It is hard to build one that only trades when there is genuine edge. Restraint is a feature, not a limitation.
The Bottom Line
Automating your prediction market trading is the single highest-leverage thing you can do as a retail trader. The API gives you the ability to monitor more markets, react faster, and execute more consistently than any manual trader can. Start with the price checker in this guide, make sure it runs reliably, and gradually add execution logic as you gain confidence.
The code here is a starting point, not a finished product. The traders making real money in prediction markets are the ones who build, test, and iterate relentlessly. Your first script will be rough. Your tenth will be profitable.