This is the single reference document for every way an AI agent can interact with prediction markets programmatically. It covers every API endpoint, CLI command, and SDK method across Polymarket and Kalshi — the two dominant platforms — side by side, so you can find the equivalent operation on each platform without switching between documentation sites.
If you’re building an agent that needs to browse markets, read prices, place bets, manage positions, or stream live data, this page has the exact call you need for each platform.
For an overview of how these trading APIs fit into the full agent stack (identity, wallet, trading, intelligence), see The Agent Betting Stack Explained.
Platform Overview
Before diving into endpoints, here’s what you’re working with.
| Polymarket | Kalshi | |
|---|---|---|
| Base URL (Market Data) | https://gamma-api.polymarket.com | https://api.elections.kalshi.com/trade-api/v2 |
| Base URL (Trading) | https://clob.polymarket.com | Same as above |
| Base URL (WebSocket) | wss://ws-subscriptions-clob.polymarket.com/ws/ | wss://api.elections.kalshi.com/trade-api/ws/v2 |
| CLI Tool | polymarket (Rust, via Homebrew) | None (REST only) |
| Python SDK | py-clob-client | kalshi-python |
| TypeScript SDK | @polymarket/clob-client | @anthropic-ai/kalshi-ts |
| Auth (read-only) | None required | None required for market data |
| Auth (trading) | API key + HMAC signature (L2 headers) | API key + RSA signature |
| Currency | USDC on Polygon | USD (cents) |
| Rate Limits | Varies by endpoint tier | Varies by endpoint |
| Chain | Polygon (chainId: 137) | N/A (centralized) |
| Market Structure | Series → Events → Markets | Series → Events → Markets |
| Pricing | 0.00 to 1.00 (USDC per share) | 1 to 99 (cents per contract) |
| Demo/Sandbox | No official sandbox | https://demo-api.kalshi.co/trade-api/v2 |
Note on the “elections” subdomain: Kalshi’s
api.elections.kalshi.comprovides access to ALL markets — not just election-related ones. This includes economics, weather, sports, technology, and more.
Authentication
Polymarket
Polymarket uses a three-tier authentication system.
Level 0 (no auth): Browse markets, read prices, check order books. No credentials needed.
Level 1 (wallet signature): Create or derive API credentials. Requires your private key to sign a message.
Level 2 (API key): Place orders, manage positions, cancel orders. Requires API key + secret + passphrase sent as HMAC-signed headers.
# Polymarket: Create a client with full trading auth
from py_clob_client.client import ClobClient
client = ClobClient(
host="https://clob.polymarket.com",
key="YOUR_PRIVATE_KEY", # Wallet private key
chain_id=137, # Polygon
signature_type=1, # 1 = proxy wallet
funder="YOUR_FUNDER_ADDRESS" # Address holding your funds
)
client.set_api_creds(client.create_or_derive_api_creds())
# Polymarket CLI: Import wallet for trading
polymarket wallet import 0xYOUR_PRIVATE_KEY
polymarket wallet show
Kalshi
Kalshi uses RSA key-pair authentication. You generate an API key and a private RSA key in your Kalshi account settings, then sign each request.
# Kalshi: Create authenticated client
import kalshi_python
config = kalshi_python.Configuration(
host="https://api.elections.kalshi.com/trade-api/v2"
)
with open('path/to/private_key.pem', 'r') as f:
config.private_key_pem = f.read()
config.api_key_id = "your-api-key-id"
client = kalshi_python.KalshiClient(config)
# Kalshi: Raw curl with auth headers
curl --request GET \
--url https://api.elections.kalshi.com/trade-api/v2/portfolio/balance \
--header 'KALSHI-ACCESS-KEY: YOUR_API_KEY' \
--header 'KALSHI-ACCESS-SIGNATURE: COMPUTED_SIGNATURE' \
--header 'KALSHI-ACCESS-TIMESTAMP: UNIX_MS_TIMESTAMP'
Quick Comparison: Auth
| Task | Polymarket | Kalshi |
|---|---|---|
| Browse markets | No auth | No auth |
| Read order books | No auth | No auth (most endpoints) |
| Place orders | L2 (API key + HMAC) | RSA signature |
| Manage positions | L2 | RSA signature |
| Key format | API key + secret + passphrase | API key ID + RSA private key PEM |
| Key creation | Derived from wallet signature | Generated in account settings |
Browsing Markets
This is the most common agent operation: scanning available markets to find trading opportunities.
List Markets
Polymarket (Gamma API):
# CLI
polymarket markets list --limit 20
polymarket -o json markets list --limit 100
# curl
curl "https://gamma-api.polymarket.com/markets?limit=20&closed=false"
# Python SDK (read-only, no auth)
from py_clob_client.client import ClobClient
client = ClobClient("https://clob.polymarket.com")
markets = client.get_markets()
Kalshi:
# curl (no auth needed)
curl "https://api.elections.kalshi.com/trade-api/v2/markets?limit=20&status=open"
# Python SDK
markets = client.get_markets(limit=20, status="open")
for m in markets.markets:
print(f"{m.ticker}: {m.title}")
Search Markets
Polymarket:
# CLI
polymarket markets search "bitcoin"
polymarket markets search "fed rate" --limit 5
# Gamma API
curl "https://gamma-api.polymarket.com/search?query=bitcoin&limit=5"
Kalshi:
# Filter by series ticker
curl "https://api.elections.kalshi.com/trade-api/v2/markets?series_ticker=KXBTC"
# No dedicated search endpoint — filter by series, event, or category
Get Market Details
Polymarket:
# CLI
polymarket markets get bitcoin-above-100k
polymarket -o json markets get bitcoin-above-100k
# Gamma API (by slug)
curl "https://gamma-api.polymarket.com/markets?slug=bitcoin-above-100k"
Kalshi:
curl "https://api.elections.kalshi.com/trade-api/v2/markets/TICKER_HERE"
market = client.get_market("KXBTC-26FEB28-T100000")
print(f"Title: {market.market.title}")
print(f"Yes price: {market.market.yes_price}¢")
Browse by Category/Tag
Polymarket:
# CLI (by tag)
polymarket events list --tag politics
polymarket events list --tag crypto
polymarket events list --tag sports
# Gamma API (by tag ID)
curl "https://gamma-api.polymarket.com/events?tag_id=2&closed=false"
# Tag IDs: politics=2, crypto=21, sports=100639, tech=1401, culture=596
Kalshi:
# Filter by category via series
curl "https://api.elections.kalshi.com/trade-api/v2/events?series_ticker=KXBTC"
# List all series to discover categories
curl "https://api.elections.kalshi.com/trade-api/v2/series/KXHIGHNY"
Quick Comparison: Market Browsing
| Operation | Polymarket CLI | Polymarket API | Kalshi API |
|---|---|---|---|
| List markets | markets list --limit N | GET /markets | GET /markets |
| Search | markets search "query" | GET /search?query=X | Filter by series/event |
| Get one market | markets get SLUG | GET /markets?slug=X | GET /markets/{ticker} |
| By category | events list --tag TAG | GET /events?tag_id=N | Filter by series_ticker |
| List events | events list | GET /events | GET /events |
| Get event | — | GET /events/{id} | GET /events/{ticker} |
| JSON output | -o json on any command | All responses are JSON | All responses are JSON |
| Pagination | --limit, --offset | limit, offset params | limit, cursor params |
| Auth required | No | No | No |
Order Books & Pricing
Reading order books and current prices is the second most common agent operation — essential for arbitrage detection, spread analysis, and order sizing.
Get Order Book
Polymarket:
# CLI
polymarket clob book TOKEN_ID
# curl
curl "https://clob.polymarket.com/book?token_id=TOKEN_ID"
from py_clob_client.client import ClobClient
from py_clob_client.clob_types import BookParams
client = ClobClient("https://clob.polymarket.com")
book = client.get_order_book("TOKEN_ID")
# Batch: get multiple books at once
books = client.get_order_books([
BookParams(token_id="TOKEN_1"),
BookParams(token_id="TOKEN_2")
])
Kalshi:
curl "https://api.elections.kalshi.com/trade-api/v2/markets/TICKER/orderbook"
book = client.get_market_orderbook("TICKER", depth=10)
# Returns yes bids and no bids only (no asks)
# In binary markets: yes bid at X¢ = no ask at (100-X)¢
Important Kalshi note: Kalshi only returns bids, not asks. This is because in binary markets, a yes bid at price X is equivalent to a no ask at (100-X). Your agent needs to compute asks by inverting the opposite side’s bids.
Get Midpoint Price
Polymarket:
# CLI
polymarket clob midpoint TOKEN_ID
polymarket -o json clob midpoint TOKEN_ID | jq '.mid'
# Python
mid = client.get_midpoint("TOKEN_ID")
Kalshi:
# No dedicated midpoint endpoint — compute from orderbook
# Midpoint = (best_yes_bid + (100 - best_no_bid)) / 2
Get Price History
Polymarket:
# CLI
polymarket clob price-history TOKEN_ID --interval 1d
polymarket clob price-history TOKEN_ID --interval 1h
# CLOB API
curl "https://clob.polymarket.com/prices-history?market=TOKEN_ID&interval=1d"
Kalshi:
curl "https://api.elections.kalshi.com/trade-api/v2/markets/TICKER/history?limit=100"
Quick Comparison: Order Books & Pricing
| Operation | Polymarket CLI | Polymarket API | Kalshi API |
|---|---|---|---|
| Order book | clob book TOKEN | GET /book?token_id=X | GET /markets/{ticker}/orderbook |
| Midpoint | clob midpoint TOKEN | get_midpoint(token) | Compute from orderbook |
| Best price | — | get_price(token, side) | Best bid from orderbook |
| Spread | — | get_spread(token) | Compute from orderbook |
| Price history | clob price-history TOKEN | GET /prices-history | GET /markets/{ticker}/history |
| Batch books | — | get_order_books([...]) | One call per market |
| Book format | Bids + Asks | Bids + Asks | Bids only (compute asks) |
Placing Orders
The core trading operation. Both platforms support limit orders and market orders, but with different mechanics.
Limit Orders
Polymarket:
# CLI
polymarket clob create-order \
--token TOKEN_ID \
--side buy \
--price 0.45 \
--size 10
from py_clob_client.clob_types import OrderArgs, OrderType
from py_clob_client.order_builder.constants import BUY
order = OrderArgs(
token_id="TOKEN_ID",
price=0.45, # Price per share in USDC (0.00-1.00)
size=10.0, # Number of shares
side=BUY
)
signed = client.create_order(order)
resp = client.post_order(signed, OrderType.GTC) # Good-til-canceled
// TypeScript
import { ClobClient, Side } from "@polymarket/clob-client";
const order = await client.createAndPostOrder(
{ tokenID: "TOKEN_ID", price: 0.50, size: 10, side: Side.BUY },
{ tickSize: "0.01", negRisk: false }
);
Kalshi:
import uuid
order = client.create_order(
ticker="MARKET_TICKER",
action="buy",
side="yes",
count=10, # Number of contracts
type="limit",
yes_price=45, # Price in cents (1-99)
client_order_id=str(uuid.uuid4())
)
curl --request POST \
--url https://api.elections.kalshi.com/trade-api/v2/portfolio/orders \
--header 'Content-Type: application/json' \
--header 'KALSHI-ACCESS-KEY: KEY' \
--header 'KALSHI-ACCESS-SIGNATURE: SIG' \
--header 'KALSHI-ACCESS-TIMESTAMP: TS' \
--data '{
"ticker": "MARKET_TICKER",
"action": "buy",
"side": "yes",
"count": 10,
"type": "limit",
"yes_price": 45,
"client_order_id": "UUID"
}'
Market Orders
Polymarket:
# CLI
polymarket clob market-order \
--token TOKEN_ID \
--side buy \
--amount 5
from py_clob_client.clob_types import MarketOrderArgs, OrderType
from py_clob_client.order_builder.constants import BUY
mo = MarketOrderArgs(
token_id="TOKEN_ID",
amount=25.0, # USDC amount to spend
side=BUY,
order_type=OrderType.FOK # Fill-or-kill
)
signed = client.create_market_order(mo)
resp = client.post_order(signed, OrderType.FOK)
Kalshi:
order = client.create_order(
ticker="MARKET_TICKER",
action="buy",
side="yes",
count=10,
type="market", # No price needed for market orders
client_order_id=str(uuid.uuid4())
)
Order Types
| Type | Polymarket | Kalshi |
|---|---|---|
| Good-til-canceled (GTC) | OrderType.GTC | type: "limit" (default) |
| Fill-or-kill (FOK) | OrderType.FOK | time_in_force: "fill_or_kill" |
| Market order | MarketOrderArgs + FOK | type: "market" |
| Post-only | Not directly supported | post_only: true |
| Reduce-only | Not directly supported | reduce_only: true |
Price Formats
This is a common source of bugs when building cross-platform agents:
| Polymarket | Kalshi | |
|---|---|---|
| Price range | 0.00 to 1.00 | 1 to 99 |
| Unit | USDC per share | Cents per contract |
| “50% chance” | price: 0.50 | yes_price: 50 |
| “10% chance” | price: 0.10 | yes_price: 10 |
| Size unit | Number of shares (decimal) | Number of contracts (integer) |
| Tick sizes | 0.01 or 0.001 (varies) | 1 cent |
Order Management
View Open Orders
Polymarket:
# CLI
polymarket clob orders
# Python
orders = client.get_orders()
Kalshi:
orders = client.get_orders(status="resting")
curl "https://api.elections.kalshi.com/trade-api/v2/portfolio/orders?status=resting" \
--header 'KALSHI-ACCESS-KEY: KEY' \
--header 'KALSHI-ACCESS-SIGNATURE: SIG' \
--header 'KALSHI-ACCESS-TIMESTAMP: TS'
Cancel Orders
Polymarket:
# CLI: Cancel one
polymarket clob cancel ORDER_ID
# CLI: Cancel all
polymarket clob cancel-all
# Python
client.cancel(order_id="ORDER_ID")
client.cancel_all()
client.cancel_market_orders(market="CONDITION_ID")
Kalshi:
# Cancel one
client.cancel_order("ORDER_ID")
# Cancel all (batch)
client.batch_cancel_orders(
tickers=["TICKER1", "TICKER2"]
)
# Cancel one
curl --request DELETE \
"https://api.elections.kalshi.com/trade-api/v2/portfolio/orders/ORDER_ID" \
--header 'KALSHI-ACCESS-KEY: KEY' \
--header 'KALSHI-ACCESS-SIGNATURE: SIG' \
--header 'KALSHI-ACCESS-TIMESTAMP: TS'
Amend Orders
Polymarket: Not supported via API — cancel and replace.
Kalshi:
# Amend an existing order (change price/size without cancel+replace)
client.amend_order(
order_id="ORDER_ID",
count=15,
yes_price=48
)
curl --request POST \
"https://api.elections.kalshi.com/trade-api/v2/portfolio/orders/ORDER_ID/amend" \
--header 'Content-Type: application/json' \
--data '{"count": 15, "yes_price": 48}'
Quick Comparison: Order Management
| Operation | Polymarket CLI | Polymarket API | Kalshi API |
|---|---|---|---|
| List orders | clob orders | get_orders() | GET /portfolio/orders |
| Cancel one | clob cancel ID | cancel(order_id) | DELETE /portfolio/orders/{id} |
| Cancel all | clob cancel-all | cancel_all() | POST /portfolio/orders/batch (cancel) |
| Amend | Not supported | Not supported | POST /portfolio/orders/{id}/amend |
| Batch create | Not supported | post_orders([...]) | POST /portfolio/orders/batch |
Positions & Portfolio
Check Positions
Polymarket:
# CLI
polymarket data positions YOUR_ADDRESS
# Data API
curl "https://data-api.polymarket.com/positions?user=YOUR_ADDRESS"
Kalshi:
positions = client.get_positions(settlement_status="unsettled")
for p in positions.market_positions:
print(f"{p.ticker}: {p.position} contracts")
curl "https://api.elections.kalshi.com/trade-api/v2/portfolio/positions" \
--header 'KALSHI-ACCESS-KEY: KEY' \
--header 'KALSHI-ACCESS-SIGNATURE: SIG' \
--header 'KALSHI-ACCESS-TIMESTAMP: TS'
Check Balance
Polymarket:
# CLI
polymarket clob balance --asset-type collateral
# Python
balance = client.get_balance_allowance()
Kalshi:
balance = client.get_balance()
# Returns balance in cents + portfolio_value (balance + positions value)
print(f"Available: ${balance.balance / 100:.2f}")
print(f"Portfolio: ${balance.portfolio_value / 100:.2f}")
Portfolio Value
Polymarket:
# CLI
polymarket data value YOUR_ADDRESS
Kalshi:
balance = client.get_balance()
# portfolio_value includes balance + position market values
Trade History
Polymarket:
# CLI
polymarket clob trades
# Python
trades = client.get_trades()
Kalshi:
fills = client.get_fills(ticker="MARKET_TICKER")
# A "fill" = a completed trade match
Quick Comparison: Portfolio
| Operation | Polymarket CLI | Polymarket API | Kalshi API |
|---|---|---|---|
| Positions | data positions ADDR | GET /positions | GET /portfolio/positions |
| Balance | clob balance | get_balance_allowance() | GET /portfolio/balance |
| Portfolio value | data value ADDR | Compute from positions | Included in balance response |
| Trade history | clob trades | get_trades() | GET /portfolio/fills |
| Settlements | — | On-chain (CTF redeem) | GET /portfolio/settlements |
WebSocket Streaming
For agents that need real-time data, both platforms offer WebSocket feeds.
Polymarket WebSocket
Base URL: wss://ws-subscriptions-clob.polymarket.com/ws/
Channels:
market — Order book updates, price changes (public)
user — Order status updates (authenticated)
# Subscribe to market channel
import websocket
import json
ws = websocket.create_connection(
"wss://ws-subscriptions-clob.polymarket.com/ws/market"
)
# Subscribe to specific assets
ws.send(json.dumps({
"type": "subscribe",
"assets_ids": ["TOKEN_ID_1", "TOKEN_ID_2"]
}))
while True:
msg = json.loads(ws.recv())
# msg contains orderbook updates, trade events
Kalshi WebSocket
Base URL: wss://api.elections.kalshi.com/trade-api/ws/v2
Channels:
orderbook — Live order book snapshots
ticker — Price/volume updates
trades — Completed trades
fills — Your fills (authenticated)
market_lifecycle — Market state changes
# Kalshi WebSocket subscription
import websocket
import json
ws = websocket.create_connection(
"wss://api.elections.kalshi.com/trade-api/ws/v2"
)
# Authenticate
ws.send(json.dumps({
"id": 1,
"cmd": "subscribe",
"params": {
"channels": ["orderbook", "ticker"],
"market_tickers": ["TICKER1", "TICKER2"]
}
}))
Quick Comparison: WebSocket
| Feature | Polymarket | Kalshi |
|---|---|---|
| Public channels | market | orderbook, ticker, trades, market_lifecycle |
| Authenticated channels | user | fills, positions |
| Subscription format | assets_ids array | market_tickers array |
| Book updates | Incremental | Snapshots |
| Auth method | Separate auth handshake | Auth headers on connect |
Polymarket CLI Complete Command Reference
The Polymarket CLI is unique to the ecosystem — no other platform has a dedicated CLI for agent access. Here’s the full command map for quick reference.
Browsing (No Wallet Required)
polymarket markets list [--limit N] [--offset N]
polymarket markets search "QUERY" [--limit N]
polymarket markets get SLUG
polymarket events list [--tag TAG] [--limit N]
polymarket clob book TOKEN_ID
polymarket clob midpoint TOKEN_ID
polymarket clob price-history TOKEN_ID [--interval 1d|1h|1m]
polymarket status # API health check
Wallet Management
polymarket wallet create # Generate new wallet
polymarket wallet import 0xKEY # Import existing private key
polymarket wallet show # Display wallet address and config
polymarket approve set # Approve USDC for trading (needs MATIC)
Trading (Wallet Required)
polymarket clob market-order --token TOKEN --side buy|sell --amount USDC
polymarket clob create-order --token TOKEN --side buy|sell --price 0.XX --size N
polymarket clob balance [--asset-type collateral|conditional]
polymarket clob orders # List open orders
polymarket clob trades # List trade history
polymarket clob cancel ORDER_ID
polymarket clob cancel-all
Data & Positions
polymarket data positions ADDRESS # All positions for an address
polymarket data value ADDRESS # Portfolio value
Utility
polymarket setup # Guided first-time setup
polymarket upgrade # Update CLI to latest version
polymarket shell # Interactive REPL mode
polymarket --version
polymarket --help
polymarket -o json COMMAND # JSON output (on any command)
SDKs & Client Libraries
Polymarket SDKs
| Language | Package | Install | Docs |
|---|---|---|---|
| Python | py-clob-client | pip install py-clob-client | GitHub |
| TypeScript | @polymarket/clob-client | npm i @polymarket/clob-client | GitHub |
| Go | poly-market-sdk | go get github.com/mtt-labs/poly-market-sdk | pkg.go.dev |
| Rust (CLI) | polymarket | brew install polymarket | GitHub |
Kalshi SDKs
| Language | Package | Install | Docs |
|---|---|---|---|
| Python | kalshi-python | pip install kalshi-python | docs.kalshi.com |
| TypeScript | Kalshi TS SDK | See docs | docs.kalshi.com |
Error Handling
Polymarket Common Errors
| Error | Meaning | Fix |
|---|---|---|
INSUFFICIENT_BALANCE | Not enough USDC | Fund wallet or reduce order size |
INVALID_TICK_SIZE | Price doesn’t match market’s tick size | Check tick size (0.01 or 0.001) |
ORDER_ALREADY_EXISTS | Duplicate order submission | Generate new nonce |
MARKET_NOT_TRADABLE | Market is closed or resolved | Check market status before ordering |
INVALID_SIGNATURE | Auth signature mismatch | Regenerate API credentials |
Kalshi Common Errors
| HTTP Code | Meaning | Fix |
|---|---|---|
| 400 | Bad request (invalid params) | Check field types and required fields |
| 401 | Authentication failed | Verify API key and signature |
| 403 | Insufficient permissions | Check account status and permissions |
| 404 | Market/order not found | Verify ticker or order ID |
| 429 | Rate limited | Back off and retry with exponential delay |
| 500 | Server error | Retry after brief delay |
Cross-Platform Arbitrage Reference
For agents comparing prices across platforms, here’s how to normalize data:
def normalize_price(polymarket_price=None, kalshi_price=None):
"""Convert to a common 0-1 probability format."""
if polymarket_price is not None:
return polymarket_price # Already 0.00-1.00
if kalshi_price is not None:
return kalshi_price / 100 # Convert cents to 0.00-1.00
def denormalize_price(probability, platform):
"""Convert common probability to platform-native format."""
if platform == "polymarket":
return round(probability, 2) # 0.00-1.00
elif platform == "kalshi":
return int(probability * 100) # 1-99 cents
When comparing equivalent markets across platforms, an arbitrage opportunity exists when the combined cost of buying “Yes” on one platform and “No” on the other totals less than 1.00 (after fees).
Rate Limits
Polymarket
Rate limits vary by endpoint tier and are enforced per API key. The CLI and SDKs handle rate limiting automatically. For direct API usage, check response headers for X-RateLimit-* values.
Kalshi
Kalshi enforces rate limits per endpoint. When rate limited, the API returns HTTP 429 with a Retry-After header. The demo environment at demo-api.kalshi.co has more generous limits for testing.
Further Reading
- The Agent Betting Stack Explained — How trading APIs fit into the full agent architecture
- Polymarket CLI + Coinbase Agentic Wallets Quickstart — Hands-on setup tutorial
- Security Best Practices — Protecting your API keys and wallet
- Tool Directory — All tools in the prediction market agent ecosystem
- Polymarket Official Docs — Full Polymarket documentation
- Kalshi Official Docs — Full Kalshi documentation