Polymarket API Guide
by @nwcalvin
Polymarket API and data access guide. Learn how to connect, find markets, get real-time data via WebSocket, access order books, place orders via CLOB SDK, an...
clawhub install innotech-polymarket-apiπ About This Skill
name: innotech-polymarket-api description: Polymarket API and data access guide. Learn how to connect, find markets, get real-time data via WebSocket, access order books, place orders via CLOB SDK, and understand market mechanics.
Polymarket API & Data Access Guide
Purpose: Comprehensive reference for connecting to, understanding, and using Polymarket APIs Last Updated: 2026-03-23
π― What This Skill Covers
1. β Connect to Polymarket - API endpoints, authentication 2. β Find Markets - Search, filter, time-based pattern discovery 3. β Get Real-time Data - WebSocket usage and event types 4. β Access Order Books - Liquidity, bid/ask spreads, depth 5. β Place Orders - CLOB SDK, market/limit orders, decimal precision 6. β Market Mechanics - How 5-min markets work, winner determination, timing 7. β Known Pitfalls - Data reliability, timing issues, common bugs
This skill does NOT include:
ποΈ Architecture Overview
API Components
| Component | URL | Purpose | Auth? |
|-----------|-----|---------|-------|
| Gamma API | https://gamma-api.polymarket.com | Market info, prices, metadata | No |
| CLOB API | https://clob.polymarket.com | Order books, trades, market structure | No |
| CLOB Auth API | https://clob-auth.polymarket.com | JWT token for trading | Yes |
| Data API | https://data-api.polymarket.com | Historical data, order books | No |
| WebSocket | wss://ws-subscriptions-clob.polymarket.com/ws/market | Real-time price/book updates | No |
Data Flow for 5-Minute BTC Up/Down Markets
1. Generate timestamp β slug: btc-updown-5m-{timestamp}
2. Fetch market info from Gamma API β get asset IDs
3. Subscribe to WebSocket with asset IDs β real-time bid/ask
4. Place orders via CLOB SDK β buy UP or DOWN tokens
5. Market closes (5 min) β Polymarket resolves winner
6. Verify winner via Gamma API outcomePrices (1.0 = winner)
π Finding Markets
5-Minute Markets β Timestamp Pattern
import timedef get_current_interval(interval_seconds=300):
"""Calculate current 5-minute interval timestamp"""
current_time = int(time.time())
return (current_time // interval_seconds) * interval_seconds
def generate_market_timestamps(num_markets=8):
"""Generate N market timestamps: current + next (N-1) intervals"""
base = get_current_interval()
return [base + i * 300 for i in range(num_markets)]
Supported cryptos: btc, eth, sol, xrp
Slug pattern: {crypto}-updown-5m-{timestamp}
Example: btc-updown-5m-1772699400
API Search Methods
import requestsBy slug (most reliable for 5-min markets)
resp = requests.get("https://gamma-api.polymarket.com/markets", params={"slug": "btc-updown-5m-1772699400"})By keyword search
resp = requests.get("https://gamma-api.polymarket.com/markets", params={"_s": "bitcoin"})Active markets
resp = requests.get("https://gamma-api.polymarket.com/markets", params={"active": "true", "limit": 100})
π Asset IDs (Token IDs)
Critical: Each outcome (UP/DOWN) has a unique asset ID required for WS subscriptions and trading.
Getting Asset IDs
import jsondef get_asset_ids(market):
"""Get asset IDs from clobTokenIds field (preferred)"""
clob_token_ids_str = market.get('clobTokenIds')
if clob_token_ids_str:
return json.loads(clob_token_ids_str) # List of 2 IDs: [UP_ID, DOWN_ID]
return []
Important:
clobTokenIds is a JSON string, not an array β must json.loads()tokens[].token_id only if clobTokenIds missingπ‘ WebSocket β Real-time Data
Connection & Subscription
import aiohttp, asyncio, json, timeasync def monitor_market(asset_ids):
async with aiohttp.ClientSession() as session:
ws = await session.ws_connect(
"wss://ws-subscriptions-clob.polymarket.com/ws/market",
heartbeat=None,
timeout=aiohttp.ClientWSTimeout(ws_close=30.0)
)
# Subscribe
await ws.send_json({
"assets_ids": asset_ids,
"type": "market",
"custom_feature_enabled": True
})
# Heartbeat every 10 seconds
last_ping = time.time()
async for msg in ws:
if msg.data == "PONG": continue
if time.time() - last_ping >= 10:
await ws.send_str("PING")
last_ping = time.time()
if msg.type == aiohttp.WSMsgType.TEXT:
data = json.loads(msg.data)
# Handle events...
β οΈ CRITICAL: Event Type Reliability
| Event Type | Description | Reliability | Use? |
|------------|-------------|-------------|------|
| book | Full orderbook snapshot | βββ | β
YES |
| best_bid_ask | Best bid/ask update | βββ | β
YES |
| price_change | Past trade records | β | β NO |
| last_trade_price | Last trade price | β | β NO |
π₯ Key Lesson: price_change = past trades, NOT current orderbook state. Using it as real-time bid/ask causes impossible states (ask < bid). Only use book and best_bid_ask.
β οΈ CRITICAL: Book Event Structure
# β WRONG: bids/asks are NOT under 'book' key
bids = data.get('book', {}).get('bids', []) # EMPTY!β
CORRECT: bids/asks at TOP LEVEL
bids = data.get('bids', [])
asks = data.get('asks', [])
Order Book Fields
# Each bid/ask entry:
{
'price': '0.4550', # Price level (0-1)
'size': '7270' # Size in USD at this price
}Liquidity = sum of all bid sizes + sum of all ask sizes
total_liquidity = sum(float(b.get('size', 0)) for b in bids) + sum(float(a.get('size', 0)) for a in asks)
π° Placing Orders β CLOB SDK
Authentication
from py_clob_client.client import ClobClient
from py_clob_client.clob_types import ApiCredshost = "https://clob.polymarket.com"
chain_id = 137 # Polygon
client = ClobClient(host, key=key, chain_id=chain_id)
client.set_api_creds(ApiCreds(
api_key="YOUR_API_KEY",
api_secret="YOUR_API_SECRET",
api_passphrase="YOUR_API_PASSPHRASE"
))
π₯ CRITICAL: Decimal Precision Rules
| Amount Type | Max Decimals | Example | |-------------|-------------|---------| | Maker amount (tokens) | 4 | 10.7419 | | Taker amount (USD) | 2 | 3.33 | | Price | 4 | 0.3100 |
Rule: Calculate tokens FIRST, then derive USD
# β
CORRECT: Explicit precision control
raw_tokens = amount_usd / price # e.g. 3.33 / 0.31 = 10.74193548
tokens = round(raw_tokens, 4) # 10.7419 (max 4 decimals)
usd_amount = round(tokens * price, 2) # 3.33 (max 2 decimals)
price_rounded = round(price, 4) # 0.3100 (max 4 decimals)β WRONG: Let SDK auto-calculate β may produce > 4 decimal tokens
β WRONG: Market order with SDK auto-calculation β same issue
Limit Order (Recommended β More Control)
from py_clob_client.order_builder.constants import BUYorder_args = OrderArgs(
token_id=asset_id,
price=price_rounded, # 0.3100 (max 4 decimals)
size=tokens, # 10.7419 (max 4 decimals)
side=BUY,
expiration=0, # GTC (Good Till Cancelled)
)
A limit order at market price = instant fill with full precision control
Market Order (From Official Docs)
market_order = client.create_market_order(
token_id="TOKEN_ID",
side=BUY,
amount=100, # Dollar amount! SDK calculates tokens
price=0.50, # Slippage protection (can be 0)
order_type=OrderType.FOK
)
client.post_order(market_order, OrderType.FOK)
Market Order Notes:
amount = USD amount (SDK calculates tokens internally)price = slippage protection ceiling, NOT exact execution priceβ οΈ ALWAYS Read Official Docs Before Trading
Never guess API behavior β read docs first. Multiple failed versions were caused by not reading official documentation.
β° 5-Minute Market Mechanics
How Markets Work
1. New market every 5 minutes β timestamp-based: btc-updown-5m-{unix_ts}
2. Market duration: Exactly 5 minutes (300 seconds)
3. Winner determination: Polymarket resolves based on BTC price movement during the 5-minute interval
- UP wins β outcomePrices: UP=1.0, DOWN=0.0
- DOWN wins β outcomePrices: UP=0.0, DOWN=1.0
4. Market appears on Polymarket: ~30-60 seconds before interval start
5. Orderbook becomes active: Shortly after market appears
Timing β What Happens When
| Time | Event |
|------|-------|
| T-60s | Market appears on Gamma API |
| T-30s | Orderbook starts getting liquidity |
| T+0 | 5-minute interval starts |
| T+0 ~ T+30s | Ask prices around 0.50, active trading |
| T+240s | Most trading done, prices converge |
| T+300s | Market closes |
| T+300s ~ T+360s | Winner being resolved |
| T+360s+ | Gamma API outcomePrices shows 1.0/0.0 (resolved) |
β οΈ CRITICAL: Winner Resolution Timing
# β WRONG: Fetch winner immediately after market close
Gamma API returns trading prices (0.60/0.40), NOT resolved prices (1.0/0.0)
Can take 60+ seconds to resolve
β
CORRECT: Wait for outcomePrices to show 1.0/0.0
After market close, poll Gamma API until resolved
outcomePrices [1.00, 0.00] = UP winner confirmed
outcomePrices [0.00, 1.00] = DOWN winner confirmed
π Data Reliability β What to Trust
Price Data Hierarchy
| Data Source | Use For | Accuracy | Latency |
|-------------|---------|----------|---------|
| book (WS) | Orderbook, bid/ask | βββ | Real-time |
| best_bid_ask (WS) | Quick price check | βββ | Real-time |
| outcomePrices (Gamma) | Market probability | ββ | 5-30s delayed |
| outcomePrices (Gamma, resolved) | Winner verification | βββ | 60s+ after close |
| price_change (WS) | Past trades only | β | N/A |
| best_bid_ask ask price | Trading probability | β οΈ | NOT true probability |
β οΈ best_bid_ask Ask Price β Market Probability
π Market Rotation
Interval-Based Rotation (Correct Method)
def get_current_interval():
"""Get current 5-minute interval boundary"""
current_time = int(time.time())
return (current_time // 300) * 300def should_rotate(current_interval, last_interval):
"""Check if we've entered a new interval"""
return current_interval != last_interval
β WRONG: Time-based (rotates at wrong time)
time_since_rotation = time.time() - last_rotation
if time_since_rotation >= 300: rotate() # Off by up to 15 seconds!
β
CORRECT: Interval-based (rotates exactly when interval changes)
if get_current_interval() != current_base_timestamp: rotate()
β οΈ Known Pitfalls & Lessons Learned
1. Never Use price_change as Orderbook Data
price_change = past trade recordsprice_change = someone bought at this price IN THE PAST2. Decimal Precision Will Break Your Orders
3. Read Official Docs Before Implementing
4. Winner Resolution Takes Time
β οΈ Pitfall #5: Market Resolution Timing
6. Market Appearance Timing is Variable
7. Ask Price at Market Start β 0.50
π API Reference
Gamma API Endpoints
| Endpoint | Method | Description | Auth? |
|----------|--------|-------------|-------|
| /markets | GET | List/search markets | No |
| /markets/{id} | GET | Get market by ID | No |
| /markets?slug={slug} | GET | Get market by slug | No |
| /markets/{id}/price | GET | Current prices | No |
WebSocket Events
| Event | Direction | Reliable? | Structure |
|-------|-----------|-----------|-----------|
| book | SβC | β
| bids[], asks[] at top level |
| best_bid_ask | SβC | β
| best_bid, best_ask |
| price_change | SβC | β | Past trades, not orderbook |
| last_trade_price | SβC | β | Last trade, not orderbook |
Rate Limits
| API | Limit | Best Practice | |-----|-------|---------------| | Gamma API | ~100 req/min | Use WebSocket instead of polling | | Data API | ~50 req/min | Cache responses | | WebSocket | No hard limit | PING every 10s, batch subscriptions |
π§ CLOB SDK Quick Reference
from py_clob_client.client import ClobClient
from py_clob_client.clob_types import OrderArgs, OrderType, ApiCreds
from py_clob_client.order_builder.constants import BUYInitialize
client = ClobClient("https://clob.polymarket.com", key=key, chain_id=137)
client.set_api_creds(ApiCreds(api_key=..., api_secret=..., api_passphrase=...))Limit order (recommended)
order = OrderArgs(token_id=ASSET_ID, price=0.50, size=10.0, side=BUY, expiration=0)
size = tokens (max 4 decimals), price (max 4 decimals)
Market order
market_order = client.create_market_order(token_id=ASSET_ID, side=BUY, amount=100, price=0, order_type=OrderType.FOK)
amount = USD, price = slippage ceiling
Nonces: Must be unique per order. Use counter or UUID.