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.

PolymarketKalshi
Base URL (Market Data)https://gamma-api.polymarket.comhttps://api.elections.kalshi.com/trade-api/v2
Base URL (Trading)https://clob.polymarket.comSame as above
Base URL (WebSocket)wss://ws-subscriptions-clob.polymarket.com/ws/wss://api.elections.kalshi.com/trade-api/ws/v2
CLI Toolpolymarket (Rust, via Homebrew)None (REST only)
Python SDKpy-clob-clientkalshi-python
TypeScript SDK@polymarket/clob-client@anthropic-ai/kalshi-ts
Auth (read-only)None requiredNone required for market data
Auth (trading)API key + HMAC signature (L2 headers)API key + RSA signature
CurrencyUSDC on PolygonUSD (cents)
Rate LimitsVaries by endpoint tierVaries by endpoint
ChainPolygon (chainId: 137)N/A (centralized)
Market StructureSeries → Events → MarketsSeries → Events → Markets
Pricing0.00 to 1.00 (USDC per share)1 to 99 (cents per contract)
Demo/SandboxNo official sandboxhttps://demo-api.kalshi.co/trade-api/v2

Note on the “elections” subdomain: Kalshi’s api.elections.kalshi.com provides 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

TaskPolymarketKalshi
Browse marketsNo authNo auth
Read order booksNo authNo auth (most endpoints)
Place ordersL2 (API key + HMAC)RSA signature
Manage positionsL2RSA signature
Key formatAPI key + secret + passphraseAPI key ID + RSA private key PEM
Key creationDerived from wallet signatureGenerated 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

OperationPolymarket CLIPolymarket APIKalshi API
List marketsmarkets list --limit NGET /marketsGET /markets
Searchmarkets search "query"GET /search?query=XFilter by series/event
Get one marketmarkets get SLUGGET /markets?slug=XGET /markets/{ticker}
By categoryevents list --tag TAGGET /events?tag_id=NFilter by series_ticker
List eventsevents listGET /eventsGET /events
Get eventGET /events/{id}GET /events/{ticker}
JSON output-o json on any commandAll responses are JSONAll responses are JSON
Pagination--limit, --offsetlimit, offset paramslimit, cursor params
Auth requiredNoNoNo

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

OperationPolymarket CLIPolymarket APIKalshi API
Order bookclob book TOKENGET /book?token_id=XGET /markets/{ticker}/orderbook
Midpointclob midpoint TOKENget_midpoint(token)Compute from orderbook
Best priceget_price(token, side)Best bid from orderbook
Spreadget_spread(token)Compute from orderbook
Price historyclob price-history TOKENGET /prices-historyGET /markets/{ticker}/history
Batch booksget_order_books([...])One call per market
Book formatBids + AsksBids + AsksBids 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

TypePolymarketKalshi
Good-til-canceled (GTC)OrderType.GTCtype: "limit" (default)
Fill-or-kill (FOK)OrderType.FOKtime_in_force: "fill_or_kill"
Market orderMarketOrderArgs + FOKtype: "market"
Post-onlyNot directly supportedpost_only: true
Reduce-onlyNot directly supportedreduce_only: true

Price Formats

This is a common source of bugs when building cross-platform agents:

PolymarketKalshi
Price range0.00 to 1.001 to 99
UnitUSDC per shareCents per contract
“50% chance”price: 0.50yes_price: 50
“10% chance”price: 0.10yes_price: 10
Size unitNumber of shares (decimal)Number of contracts (integer)
Tick sizes0.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

OperationPolymarket CLIPolymarket APIKalshi API
List ordersclob ordersget_orders()GET /portfolio/orders
Cancel oneclob cancel IDcancel(order_id)DELETE /portfolio/orders/{id}
Cancel allclob cancel-allcancel_all()POST /portfolio/orders/batch (cancel)
AmendNot supportedNot supportedPOST /portfolio/orders/{id}/amend
Batch createNot supportedpost_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

OperationPolymarket CLIPolymarket APIKalshi API
Positionsdata positions ADDRGET /positionsGET /portfolio/positions
Balanceclob balanceget_balance_allowance()GET /portfolio/balance
Portfolio valuedata value ADDRCompute from positionsIncluded in balance response
Trade historyclob tradesget_trades()GET /portfolio/fills
SettlementsOn-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

FeaturePolymarketKalshi
Public channelsmarketorderbook, ticker, trades, market_lifecycle
Authenticated channelsuserfills, positions
Subscription formatassets_ids arraymarket_tickers array
Book updatesIncrementalSnapshots
Auth methodSeparate auth handshakeAuth 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

LanguagePackageInstallDocs
Pythonpy-clob-clientpip install py-clob-clientGitHub
TypeScript@polymarket/clob-clientnpm i @polymarket/clob-clientGitHub
Gopoly-market-sdkgo get github.com/mtt-labs/poly-market-sdkpkg.go.dev
Rust (CLI)polymarketbrew install polymarketGitHub

Kalshi SDKs

LanguagePackageInstallDocs
Pythonkalshi-pythonpip install kalshi-pythondocs.kalshi.com
TypeScriptKalshi TS SDKSee docsdocs.kalshi.com

Error Handling

Polymarket Common Errors

ErrorMeaningFix
INSUFFICIENT_BALANCENot enough USDCFund wallet or reduce order size
INVALID_TICK_SIZEPrice doesn’t match market’s tick sizeCheck tick size (0.01 or 0.001)
ORDER_ALREADY_EXISTSDuplicate order submissionGenerate new nonce
MARKET_NOT_TRADABLEMarket is closed or resolvedCheck market status before ordering
INVALID_SIGNATUREAuth signature mismatchRegenerate API credentials

Kalshi Common Errors

HTTP CodeMeaningFix
400Bad request (invalid params)Check field types and required fields
401Authentication failedVerify API key and signature
403Insufficient permissionsCheck account status and permissions
404Market/order not foundVerify ticker or order ID
429Rate limitedBack off and retry with exponential delay
500Server errorRetry 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