2.1 — Polymarket API & CLOB — Fetching Live Market Data
Polymarket API & CLOB — Fetching Live Market Data
Aapka bot kitna bhi smart ho, agar usse live market data nahi milta toh woh blind hai. The foundation of any profitable trading bot is a reliable, real-time data pipeline. In this lesson, we build the data ingestion layer for your Polymarket Oracle — fetching live market prices, order book depth, and trade history through Polymarket's official APIs.
The Two APIs You Need
Polymarket's infrastructure uses two separate APIs that serve different purposes:
POLYMARKET DATA ARCHITECTURE
┌──────────────────────────────────────────────────┐
│ │
│ GAMMA API (gamma-api.polymarket.com) │
│ Purpose: Market Discovery & Metadata │
│ ├── Find active markets │
│ ├── Read resolution criteria │
│ ├── Check volume & liquidity │
│ └── Get token IDs for CLOB │
│ │
│ ↓ (token IDs flow down) │
│ │
│ CLOB API (clob.polymarket.com) │
│ Purpose: Live Trading & Real-Time Data │
│ ├── Live prices (bid/ask/mid) │
│ ├── Order book depth │
│ ├── Place/cancel orders │
│ ├── Trade history │
│ └── WebSocket streaming │
│ │
└──────────────────────────────────────────────────┘
Gamma API (gamma-api.polymarket.com): The market catalogue. This is where you discover markets, read resolution criteria, check volume, and find the technical identifiers you need for trading.
CLOB API (clob.polymarket.com): The exchange engine. This handles live prices, order book data, order placement, and trade history. Everything involving real-time market state lives here.
The workflow is always: Gamma first (find what you want to trade) → CLOB (execute and monitor).
Fetching All Active Markets
import requests
def get_active_markets(min_volume=50000):
"""Fetch all markets above a volume threshold."""
url = "https://gamma-api.polymarket.com/markets"
params = {
"active": "true",
"closed": "false",
"limit": 100,
"offset": 0
}
all_markets = []
while True:
response = requests.get(url, params=params).json()
markets = response if isinstance(response, list) else response.get("markets", [])
# Filter by volume
filtered = [m for m in markets if float(m.get("volume", 0)) >= min_volume]
all_markets.extend(filtered)
if len(markets) < 100: # Last page
break
params["offset"] += 100
return all_markets
# Run it
markets = get_active_markets(min_volume=50000)
print(f"Found {len(markets)} markets with volume >= $50,000")
# Print top 5 by volume
for m in sorted(markets, key=lambda x: float(x.get('volume', 0)), reverse=True)[:5]:
print(f" {m['question'][:60]}... | Vol: ${float(m['volume']):,.0f}")
Reading the CLOB Order Book
The order book shows you exactly what buyers and sellers are willing to pay right now. This is your entry price, your spread cost, and your liquidity check in one data structure:
from py_clob_client.client import ClobClient
import os
# Initialize CLOB client
client = ClobClient(
host="https://clob.polymarket.com",
key=os.getenv("POLYMARKET_PRIVATE_KEY"),
chain_id=137 # Polygon mainnet
)
def get_market_orderbook(token_id):
"""Get the current order book for a market."""
book = client.get_order_book(token_id)
best_bid = book.bids[0].price if book.bids else 0
best_ask = book.asks[0].price if book.asks else 1
mid_price = (best_bid + best_ask) / 2
spread = best_ask - best_bid
return {
"token_id": token_id,
"best_bid": best_bid,
"best_ask": best_ask,
"mid_price": mid_price,
"spread": spread,
"spread_pct": spread / mid_price * 100 if mid_price > 0 else 0
}
Understanding Spread — The Hidden Cost:
| Spread (cents) | Market Price | Spread % | What It Means |
|---|---|---|---|
| 1-2¢ | 50¢ | 2-4% | Tight — good for trading |
| 3-5¢ | 50¢ | 6-10% | Moderate — acceptable for high-confidence trades |
| 8-15¢ | 50¢ | 16-30% | Wide — avoid unless very high conviction |
A spread of 8 cents means you need the price to move 8 cents in your favor just to break even. Always check spread before entering.
Streaming Real-Time Price Updates
For a reactive bot that needs near-real-time data, polling every 30 seconds is inefficient. Polymarket's CLOB provides WebSocket streaming for price updates:
import asyncio
import websockets
import json
async def stream_market_prices(token_ids: list, callback):
"""Stream real-time price updates for multiple markets."""
uri = "wss://clob.polymarket.com/ws/"
async with websockets.connect(uri) as ws:
# Subscribe to price channels
subscribe_msg = {
"type": "subscribe",
"channel": "market",
"market_slugs": token_ids
}
await ws.send(json.dumps(subscribe_msg))
async for message in ws:
data = json.loads(message)
if data.get("type") == "price_change":
await callback(data)
async def on_price_update(data):
print(f"Market {data['market_id']}: YES={data['yes_price']:.3f}, NO={data['no_price']:.3f}")
# asyncio.run(stream_market_prices(["token_123"], on_price_update))
Handling Authentication
For read-only data (market discovery, prices, order books), no authentication is needed. For placing orders, you need a Polymarket API key derived from your wallet's private key.
import os
from py_clob_client.client import ClobClient
from py_clob_client.clob_types import ApiCreds
# Create API credentials (one-time setup)
def setup_api_credentials():
private_key = os.getenv("POLYMARKET_PRIVATE_KEY") # Your MetaMask private key
client = ClobClient(
host="https://clob.polymarket.com",
key=private_key,
chain_id=137
)
# Create API key (stores in Polymarket's system)
api_creds = client.create_or_derive_api_creds()
print(f"API Key: {api_creds.api_key}")
print(f"API Secret: {api_creds.api_secret}")
# Save these to your .env file
NEVER hardcode your private key. Always load from environment variables. Never commit your .env file to Git.
Rate Limits and Polite Scraping
Polymarket's public APIs have rate limits. Respect them:
| API | Rate Limit | Recommendation |
|---|---|---|
| Gamma REST | ~60 req/min | Batch market discovery, cache results |
| CLOB REST | ~120 req/min | Use for order placement, not price monitoring |
| CLOB WebSocket | No effective limit | Use this for real-time price data |
For a production bot scanning 200+ markets, use exponential backoff on 429 errors and stagger your requests with asyncio.sleep(0.5) between batches.
import time
def safe_api_call(url, params, max_retries=3):
"""API call with exponential backoff for rate limits."""
for attempt in range(max_retries):
response = requests.get(url, params=params)
if response.status_code == 200:
return response.json()
elif response.status_code == 429:
wait = 2 ** attempt # 1s, 2s, 4s
print(f"Rate limited. Waiting {wait}s...")
time.sleep(wait)
else:
print(f"Error {response.status_code}: {response.text}")
return None
return None
Pakistan Context: Access & Funding
Polymarket operates on the Polygon blockchain. From Pakistan, here's how to access and fund your account:
Funding Options:
- Crypto route (most common from PK): Buy USDC on Binance P2P (PKR → USDC), transfer to MetaMask on Polygon network, deposit to Polymarket
- International card: Some Pakistani debit cards (Sadapay, Nayapay) work with Polygon bridge services
- Cost: Binance P2P spread is typically 1-2% above market rate. Polygon gas fees are negligible (under $0.01 per transaction)
Important Notes:
- Polymarket is currently restricted in some jurisdictions — check current access policies
- Use a VPN if needed for market access (NordVPN or similar)
- Start with small amounts ($30-50) while testing your bot
- Keep your private key secure — hardware wallet (Ledger) recommended for amounts above $500
Practice Lab
-
Market scan: Run the
get_active_markets()function and print the top 10 markets by volume. For each, extract: market question, current YES price, total volume, resolution date. -
Order book analysis: For 5 of those markets, call
get_market_orderbook()and calculate the spread in cents and as a percentage. Which market has the tightest spread? Which has the widest? What does this tell you about where your bot should focus? -
Build a market snapshot tool: Combine Gamma + CLOB to build a function that takes a market slug (e.g., "will-sbp-cut-rates-q2-2026") and returns a complete snapshot: price, spread, volume, resolution criteria, and days to resolution. This is your bot's "market briefing" function.
-
Rate limit testing: Write a script that calls the Gamma API 100 times in a loop. Measure how many requests succeed before you get a 429 error. Implement the
safe_api_callfunction with exponential backoff and verify it recovers gracefully from rate limits.
Key Takeaways
- Two APIs serve different purposes: Gamma for market discovery and metadata, CLOB for live prices and order execution — always use both
- Always check spread before entering a position — a wide spread means your break-even point is already significantly against you
- WebSocket streaming is the correct architecture for a reactive bot; REST polling every 30 seconds introduces too much latency and wastes rate limit budget
- Never hardcode private keys — use environment variables, and never commit
.envfiles to version control - Exponential backoff on API errors is essential for production bots — a 429 error without backoff will get your IP temporarily banned
- From Pakistan, the Binance P2P → USDC → Polygon → Polymarket route is the most reliable funding path, with ~1-2% total cost
- Start with read-only API calls (no authentication needed) before moving to authenticated trading — build confidence in your data pipeline before risking capital
Lesson Summary
Quiz: Polymarket API & CLOB — Fetching Live Market Data
4 questions to test your understanding. Score 60% or higher to pass.