Prediction market APIs enable automated trading, real-time data access, portfolio management, and algorithmic strategies unavailable through platform user interfaces. This comprehensive developer guide covers API integration for Polymarket, Kalshi, and PredictIt—including authentication methods, endpoint documentation, rate limiting, WebSocket connections, error handling, and practical code examples. Whether you’re building a trading bot, analyzing market efficiency, or developing a prediction market aggregator, this guide provides the technical foundation for API-driven prediction market interaction.
From REST API endpoints returning market odds to WebSocket feeds delivering millisecond-latency updates, prediction market infrastructure has evolved to support institutional-grade automated trading since 2020.
Why Use Prediction Market APIs
Automated Trading Execution: Manual trading requires constant monitoring—watching Fed announcements, tracking election results, following sports scores. APIs enable automated trading strategies based on predetermined rules, executing trades 24/7 without human intervention.
Real-Time Data Access: Platform UIs refresh every few seconds; WebSocket APIs deliver updates in milliseconds. Speed advantage crucial for capturing arbitrage opportunities and reacting to breaking news before broader market adjusts.
Portfolio Management at Scale: Managing 50-100+ simultaneous positions across categories manually becomes impossible. APIs enable programmatic position tracking, risk analysis, profit/loss calculation, and automated rebalancing.
Backtesting Strategies: Historical data via APIs allows strategy validation before risking capital. Test whether contrarian political prediction markets betting or momentum sports trading generated positive returns 2020-2024.
Cross-Platform Aggregation: Compare odds across Polymarket, Kalshi, PredictIt simultaneously. Identify arbitrage opportunities, find best prices, or build unified prediction market dashboards aggregating all platforms.
Research & Analysis: Academics studying market efficiency, journalists tracking political odds, analysts researching crowd wisdom—all require structured data unavailable through standard platform interfaces.
Polymarket API: Decentralized Market Access
API Overview
Base URL: https://clob.polymarket.com (CLOB = Central Limit Order Book)
Documentation: docs.polymarket.com (official), polymarket-api.notion.site (community)
Authentication: API keys generated in dashboard + HMAC-SHA256 signing
Rate Limits: 100 req/min (public), 300 req/min (authenticated)
Architecture: Polymarket operates on Polygon L2 blockchain with off-chain CLOB for performance. API interacts with CLOB for trading, queries blockchain for settlement data.
Authentication Setup
Generate API Key:
- Login to Polymarket dashboard
- Navigate to Settings → API Keys
- Generate new key (receives
api_keyandapi_secret) - Store securely—secrets not retrievable after initial display
Request Signing (Python):
import hmac
import hashlib
import time
def sign_request(secret, timestamp, method, path, body=""):
message = f"{timestamp}{method}{path}{body}"
signature = hmac.new(
secret.encode(),
message.encode(),
hashlib.sha256
).hexdigest()
return signature
# Usage
api_secret = "your_secret_here"
timestamp = str(int(time.time() * 1000))
method = "GET"
path = "/markets"
signature = sign_request(api_secret, timestamp, method, path)
Headers:
headers = {
"POLY-API-KEY": "your_api_key",
"POLY-TIMESTAMP": timestamp,
"POLY-SIGNATURE": signature
}
Core Endpoints
GET /markets – List All Markets
import requests
response = requests.get("https://clob.polymarket.com/markets")
markets = response.json()
for market in markets[:5]: # First 5 markets
print(f"{market['question']}: YES ${market['outcomePrices'][0]}")
Response Example:
{
"id": "0x1234...abcd",
"question": "Will Bitcoin reach $150,000 by Dec 31, 2025?",
"outcomes": ["YES", "NO"],
"outcomePrices": ["0.38", "0.62"],
"volume": "8240000",
"liquidity": "2100000",
"endDate": "2025-12-31T23:59:59Z"
}
GET /orderbook/{market_id} – Order Book Depth
Returns full order book with bids/asks at each price level:
market_id = "0x1234...abcd"
response = requests.get(f"https://clob.polymarket.com/orderbook/{market_id}")
orderbook = response.json()
print("BIDS (buy orders):")
for bid in orderbook['bids'][:5]:
print(f" ${bid['price']} - {bid['size']} shares")
print("ASKS (sell orders):")
for ask in orderbook['asks'][:5]:
print(f" ${ask['price']} - {ask['size']} shares")
POST /orders – Place Order
Requires authentication:
order = {
"market": "0x1234...abcd",
"outcome": "YES",
"side": "BUY",
"type": "LIMIT",
"price": "0.38",
"size": "1000"
}
headers = {
"POLY-API-KEY": api_key,
"POLY-TIMESTAMP": timestamp,
"POLY-SIGNATURE": signature
}
response = requests.post(
"https://clob.polymarket.com/orders",
json=order,
headers=headers
)
if response.status_code == 200:
print(f"Order placed: {response.json()['orderId']}")
GET /positions – Current Positions
Returns all user positions with PnL:
response = requests.get(
"https://clob.polymarket.com/positions",
headers=headers
)
for position in response.json():
print(f"{position['market']}: {position['outcome']} "
f"x{position['size']} @ ${position['avgPrice']} "
f"(PnL: ${position['unrealizedPnL']})")
WebSocket Real-Time Feed
Connection:
import websocket
import json
def on_message(ws, message):
data = json.loads(message)
print(f"Update: {data['market']} - YES: ${data['yesPrice']}")
def on_error(ws, error):
print(f"Error: {error}")
ws = websocket.WebSocketApp(
"wss://clob.polymarket.com/ws",
on_message=on_message,
on_error=on_error
)
# Subscribe to specific market
ws.send(json.dumps({
"type": "subscribe",
"market": "0x1234...abcd"
}))
ws.run_forever()
Latency: <50ms for price updates—critical for arbitrage bots
Rate Limiting & Best Practices
Limits:
- Public endpoints: 100 requests/minute
- Authenticated: 300 requests/minute
- WebSocket: Unlimited (but throttle subscriptions to <100 markets)
Handling Rate Limits:
import time
def rate_limited_request(url, max_retries=3):
for attempt in range(max_retries):
response = requests.get(url)
if response.status_code == 429: # Rate limited
retry_after = int(response.headers.get('Retry-After', 60))
print(f"Rate limited, waiting {retry_after}s")
time.sleep(retry_after)
continue
return response
raise Exception("Max retries exceeded")
Kalshi API: CFTC-Regulated Market Access
API Overview
Base URL: https://api.kalshi.com/v1
Sandbox: https://demo-api.kalshi.com/v1
Documentation: docs.kalshi.com/api (comprehensive official docs)
Authentication: OAuth 2.0
Rate Limits: 300 req/min (standard), 1,000 req/min (institutional)
Sandbox Environment: Test all functionality with fake money—mirrors production data with 5-minute delay. Perfect for development without risking capital.
OAuth 2.0 Authentication
Initial Token:
import requests
auth_response = requests.post(
"https://api.kalshi.com/v1/oauth/token",
json={
"email": "user@example.com",
"password": "your_password"
}
)
token = auth_response.json()['access_token']
refresh_token = auth_response.json()['refresh_token']
Using Token:
headers = {
"Authorization": f"Bearer {token}"
}
response = requests.get(
"https://api.kalshi.com/v1/markets",
headers=headers
)
Refresh Token (expires after 7 days):
refresh_response = requests.post(
"https://api.kalshi.com/v1/oauth/refresh",
json={"refresh_token": refresh_token}
)
new_token = refresh_response.json()['access_token']
Core Endpoints
GET /events – All Events
Returns events (Fed decisions, elections, sports):
response = requests.get(
"https://api.kalshi.com/v1/events",
headers=headers
)
for event in response.json()['events']:
print(f"{event['title']} - {event['category']}")
print(f" Markets: {event['market_count']}")
GET /markets – Market Details
params = {
"event_ticker": "FED-25DEC", # Fed December decision
"status": "open"
}
response = requests.get(
"https://api.kalshi.com/v1/markets",
params=params,
headers=headers
)
for market in response.json()['markets']:
print(f"{market['title']}")
print(f" YES: ${market['yes_bid']} - ${market['yes_ask']}")
print(f" Volume: ${market['volume']}")
POST /orders – Place Order
order = {
"market_ticker": "FED-25DEC-T0.25",
"side": "YES",
"action": "BUY",
"count": 100, # 100 contracts
"type": "LIMIT",
"yes_price": 82 # Price in cents ($0.82)
}
response = requests.post(
"https://api.kalshi.com/v1/orders",
json=order,
headers=headers
)
print(f"Order ID: {response.json()['order_id']}")
GET /portfolio/positions – Current Positions
response = requests.get(
"https://api.kalshi.com/v1/portfolio/positions",
headers=headers
)
total_pnl = 0
for position in response.json()['positions']:
pnl = position['realized_pnl'] + position['unrealized_pnl']
total_pnl += pnl
print(f"{position['market_ticker']}: {position['position']} "
f"contracts (PnL: ${pnl/100:.2f})")
print(f"\nTotal PnL: ${total_pnl/100:.2f}")
Advanced Order Types
Stop-Limit Orders:
order = {
"market_ticker": "BTC-150K-31DEC",
"side": "YES",
"action": "SELL",
"count": 50,
"type": "STOP_LIMIT",
"stop_price": 35, # Trigger at $0.35
"yes_price": 33 # Execute at $0.33
}
Fill-or-Kill (FOK):
order = {
"market_ticker": "NFL-SF-SB60",
"side": "YES",
"action": "BUY",
"count": 500,
"type": "LIMIT",
"yes_price": 22,
"time_in_force": "FOK" # Fill entire order or cancel
}
Sandbox Testing
Switch to Sandbox:
KALSHI_BASE = "https://demo-api.kalshi.com/v1" # Sandbox
# KALSHI_BASE = "https://api.kalshi.com/v1" # Production
# All API calls identical, just change base URL
response = requests.get(f"{KALSHI_BASE}/markets", headers=headers)
Benefits:
- Test strategies risk-free
- Verify API integration before production
- Experiment with order types and edge cases
- Production-like data (5-minute delay)
PredictIt API: Read-Only Market Data
API Overview
Base URL: https://www.predictit.org/api/marketdata
Authentication: None required (public read-only)
Rate Limits: 1 request/second (strictly enforced)
Documentation: predictit.freshdesk.com/support/solutions/articles/12000001878
Limitations: No trading via API, no WebSocket, no historical data (use CSV downloads instead)
Available Endpoints
GET /all – All Markets
import requests
import time
response = requests.get("https://www.predictit.org/api/marketdata/all")
data = response.json()
for market in data['markets'][:10]:
print(f"{market['name']}")
for contract in market['contracts']:
print(f" {contract['name']}: ${contract['lastTradePrice']}")
time.sleep(1) # Respect 1 req/sec limit
GET /markets/{id} – Specific Market
market_id = 7053 # Example: 2028 GOP nominee market
response = requests.get(f"https://www.predictit.org/api/marketdata/markets/{market_id}")
market = response.json()
print(f"{market['name']}")
print(f"Status: {market['status']}")
for contract in market['contracts']:
print(f"{contract['name']}: Buy ${contract['bestBuyYesCost']} | "
f"Sell ${contract['bestSellYesCost']}")
Response Fields:
bestBuyYesCost– Current best ask (price to buy YES)bestSellYesCost– Current best bid (price to sell YES)lastTradePrice– Most recent executed tradedisplayOrder– Ranking within market
Rate Limiting Compliance
Strict 1 req/sec Enforcement:
from time import time, sleep
class PredictItClient:
def __init__(self):
self.last_request = 0
self.min_interval = 1.0 # 1 second
def get(self, endpoint):
# Enforce rate limit
elapsed = time() - self.last_request
if elapsed < self.min_interval:
sleep(self.min_interval - elapsed)
response = requests.get(f"https://www.predictit.org/api/marketdata{endpoint}")
self.last_request = time()
return response.json()
# Usage
client = PredictItClient()
data = client.get("/all") # Automatically rate limited
Use Cases for Read-Only API
Price Monitoring:
# Track specific market, alert on price changes
target_price = 0.40
market_id = 7053
while True:
data = client.get(f"/markets/{market_id}")
current_price = data['contracts'][0]['lastTradePrice']
if current_price <= target_price:
send_alert(f"Price hit ${current_price}!")
break
sleep(60) # Check every minute
Cross-Platform Comparison:
# Compare PredictIt vs Polymarket prices
predictit_price = get_predictit_price(market_id)
polymarket_price = get_polymarket_price(equivalent_id)
spread = abs(polymarket_price - predictit_price)
if spread > 0.05: # 5 cent arbitrage opportunity
print(f"Arbitrage: {spread*100:.1f}¢ spread")
Error Handling & Best Practices
Common Errors:
def safe_api_call(func, *args, **kwargs):
try:
return func(*args, **kwargs)
except requests.exceptions.Timeout:
print("Request timed out, retrying...")
time.sleep(5)
return safe_api_call(func, *args, **kwargs)
except requests.exceptions.ConnectionError:
print("Connection error, waiting...")
time.sleep(30)
return safe_api_call(func, *args, **kwargs)
except requests.exceptions.HTTPError as e:
if e.response.status_code == 401:
print("Authentication failed, refresh token...")
refresh_auth_token()
return safe_api_call(func, *args, **kwargs)
elif e.response.status_code == 429:
print("Rate limited, backing off...")
time.sleep(60)
return safe_api_call(func, *args, **kwargs)
else:
raise
Logging for Debugging:
import logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
filename='prediction_market_api.log'
)
logger = logging.getLogger(__name__)
def place_order(market, side, price):
logger.info(f"Placing order: {market} {side} @ ${price}")
try:
response = api.post_order(market, side, price)
logger.info(f"Order success: {response['order_id']}")
return response
except Exception as e:
logger.error(f"Order failed: {str(e)}")
raise
Conclusion
Prediction market APIs transform manual trading into automated, systematic strategies. Polymarket’s WebSocket feeds enable millisecond-latency trading, Kalshi’s OAuth 2.0 and sandbox environment support institutional-grade development, and PredictIt’s read-only API facilitates research and price monitoring. Whether building arbitrage bots, portfolio managers, or research platforms, API integration unlocks capabilities unavailable through platform UIs.
Ready to explore more tools? Learn arbitrage strategies, understand binary market mechanics, explore smart contracts, or return to the complete tools guide. For platform comparisons, review Polymarket features, Kalshi details, or track live markets.