Polymarket Trading - Complete API Reference
Full access to Polymarket's CLOB (Central Limit Order Book) via the official py_clob_client library.
60+ methods documented. This is the complete reference.
Required Environment Variables
PRIVATE_KEY=0x... # Ethereum private key for signing POLY_FUNDER_ADDRESS=0x... # Your wallet address on Polygon POLY_API_KEY=... # From Polymarket API POLY_API_SECRET=... # Base64 encoded secret POLY_API_PASSPHRASE=... # API passphrase
Installation
pip install py-clob-client requests
Authentication Levels
Level Requirements Capabilities
L0 None Read-only: orderbooks, prices, markets
L1 Private key Create & sign orders (not post)
L2 Private key + API creds Full trading: post orders, cancel, query
Signature Types
Type Use Case
0
Standard EOA (MetaMask, hardware wallets)
1
Magic/email wallets (delegated signing)
2
Proxy wallets (Gnosis Safe, browser proxy)
ClobClient - Complete API (60+ Methods)
Initialization
from py_clob_client.client import ClobClient from py_clob_client.clob_types import ( OrderArgs, MarketOrderArgs, ApiCreds, OrderType, BookParams, TradeParams, OpenOrderParams, BalanceAllowanceParams, AssetType, OrderScoringParams, OrdersScoringParams, DropNotificationParams ) from py_clob_client.order_builder.constants import BUY, SELL from py_clob_client.constants import POLYGON # 137
Level 2 Auth (full trading access)
client = ClobClient( host="https://clob.polymarket.com", key=os.getenv("PRIVATE_KEY"), # Private key for signing chain_id=POLYGON, # 137 for mainnet, 80002 for Amoy testnet funder=os.getenv("POLY_FUNDER_ADDRESS"), # Wallet address (for proxy wallets) signature_type=2 # 0=EOA, 1=MagicLink, 2=Proxy )
Set API credentials for authenticated endpoints
client.set_api_creds(ApiCreds( api_key=os.getenv("POLY_API_KEY"), api_secret=os.getenv("POLY_API_SECRET"), api_passphrase=os.getenv("POLY_API_PASSPHRASE") ))
Health & Configuration
client.get_ok() # Check if server is up client.get_server_time() # Get server timestamp client.get_address() # Your signer's public address client.get_collateral_address() # USDC contract address client.get_conditional_address() # CTF contract address client.get_exchange_address() # Exchange contract (neg_risk=False default)
Market Data - Single Token
Get prices and spreads
client.get_midpoint(token_id) # Mid market price client.get_price(token_id, side="BUY") # Best price for side client.get_spread(token_id) # Current spread client.get_last_trade_price(token_id) # Last executed trade price
Get full orderbook
orderbook = client.get_order_book(token_id)
Returns: OrderBookSummary with bids, asks, tick_size, neg_risk, timestamp, hash
Market Data - Batch (Multiple Tokens)
params = [ BookParams(token_id="TOKEN1", side="BUY"), BookParams(token_id="TOKEN2", side="SELL") ]
client.get_midpoints(params) # Multiple midpoints client.get_prices(params) # Multiple prices client.get_spreads(params) # Multiple spreads client.get_order_books(params) # Multiple orderbooks client.get_last_trades_prices(params) # Multiple last prices
Market Metadata
client.get_tick_size(token_id) # Returns: "0.1", "0.01", "0.001", or "0.0001" client.get_neg_risk(token_id) # Returns: True/False (negative risk market) client.get_fee_rate_bps(token_id) # Returns: fee rate in basis points (0 or 1000)
Order Types
from py_clob_client.clob_types import OrderType
OrderType.GTC # Good Till Cancelled - stays open until filled/cancelled OrderType.FOK # Fill Or Kill - fill entirely immediately or cancel OrderType.GTD # Good Till Date - expires at timestamp (min 60 seconds) OrderType.FAK # Fill And Kill - fill what's possible, cancel rest
When to Use Each Order Type
Type Use Case Example
GTC Entries - wait for fill Place buy at 0.45, wait for dip
FOK Exits - need immediate fill Market sell entire position NOW
GTD Time-limited orders Offer expires in 5 minutes
FAK Partial fills OK Get as much as possible now
OrderArgs - Limit Orders
OrderArgs( token_id: str, # Token ID (outcome to trade) price: float, # Price 0.01-0.99 size: float, # Number of shares side: str, # "BUY" or "SELL" (or use BUY/SELL constants) fee_rate_bps: int = 0, # Optional: fee rate in bps (0 or check market) nonce: int = 0, # Optional: unique nonce for cancellation expiration: int = 0, # Optional: expiry timestamp (0 = GTC, use timestamp for GTD) taker: str = ZERO_ADDRESS # Optional: specific taker (ZERO_ADDRESS = anyone) )
MarketOrderArgs - Market Orders
MarketOrderArgs( token_id: str, # Token ID amount: float, # Total USDC amount to spend (BUY) or shares (SELL) side: str, # "BUY" or "SELL" price: float = 0, # Optional: worst acceptable price (slippage protection) fee_rate_bps: int = 0, # Optional: fee rate nonce: int = 0, # Optional: nonce taker: str = ZERO_ADDRESS, # Optional: taker address order_type: OrderType = FOK # Optional: FOK (default) or FAK )
Complete Order Examples
from py_clob_client.order_builder.constants import BUY, SELL
1. LIMIT BUY (GTC) - sits on book until filled
order = client.create_and_post_order( OrderArgs(token_id=TOKEN_ID, price=0.45, size=100.0, side=BUY) )
2. LIMIT SELL (GTC)
order = client.create_and_post_order( OrderArgs(token_id=TOKEN_ID, price=0.55, size=50.0, side=SELL) )
3. MARKET BUY - spend $100 USDC at current prices (FOK)
signed = client.create_market_order( MarketOrderArgs(token_id=TOKEN_ID, amount=100.0, side=BUY) ) result = client.post_order(signed, orderType=OrderType.FOK)
4. MARKET SELL - sell all shares immediately (FOK)
signed = client.create_market_order( MarketOrderArgs(token_id=TOKEN_ID, amount=my_shares, side=SELL) ) result = client.post_order(signed, orderType=OrderType.FOK)
5. POST-ONLY MAKER ORDER (avoid taker fees, earn rebates)
signed = client.create_order( OrderArgs(token_id=TOKEN_ID, price=0.44, size=100.0, side=BUY) ) result = client.post_order(signed, orderType=OrderType.GTC, post_only=True)
If order would cross spread, it gets REJECTED instead of taking
6. GOOD TIL DATE (GTD) - expires after timestamp
import time expiry = int(time.time()) + 300 # 5 minutes from now signed = client.create_order( OrderArgs(token_id=TOKEN_ID, price=0.50, size=100.0, side=BUY, expiration=expiry) ) result = client.post_order(signed, orderType=OrderType.GTD)
7. FILL AND KILL (FAK) - fill what you can, cancel rest
signed = client.create_market_order( MarketOrderArgs(token_id=TOKEN_ID, amount=1000.0, side=BUY) ) result = client.post_order(signed, orderType=OrderType.FAK)
Order Operations
Create and Post Orders
SIMPLE: Create and post in one call (recommended)
result = client.create_and_post_order( OrderArgs( token_id="123456789012345678901234567890", price=0.45, size=10.0, side="BUY" ) )
Returns: {"orderID": "...", "status": "...", ...}
ADVANCED: Separate create and post
order = client.create_order(OrderArgs(...)) # Returns SignedOrder result = client.post_order(order, orderType=OrderType.GTC, post_only=False)
Market order (calculates price automatically)
result = client.create_market_order( MarketOrderArgs( token_id="...", amount=100.0, # Spend $100 USDC side="BUY" ) )
Calculate expected fill price before market order
price = client.calculate_market_price( token_id="...", side="BUY", amount=100.0, order_type=OrderType.FOK )
Cancel Orders
client.cancel(order_id="ORDER_ID") # Cancel specific order client.cancel_orders(["ID1", "ID2", "ID3"]) # Cancel multiple client.cancel_all() # Cancel ALL open orders client.cancel_market_orders( # Cancel by market/asset market="CONDITION_ID", asset_id="TOKEN_ID" )
Query Orders
Get all open orders
orders = client.get_orders( params=OpenOrderParams( id="ORDER_ID", # Optional: specific order market="COND_ID", # Optional: filter by market asset_id="TOKEN" # Optional: filter by token ), next_cursor="MA==" # For pagination )
Get specific order
order = client.get_order(order_id="ORDER_ID")
Trade History
trades = client.get_trades( params=TradeParams( id="TRADE_ID", # Optional: specific trade maker_address="0x...", # Optional: filter by maker market="CONDITION_ID", # Optional: filter by market asset_id="TOKEN_ID", # Optional: filter by token before="2024-01-01", # Optional: before date after="2023-01-01" # Optional: after date ), next_cursor="MA==" )
Balance & Allowance
Check balance and allowances
balance = client.get_balance_allowance( params=BalanceAllowanceParams( asset_type=AssetType.COLLATERAL, # USDC balance # or AssetType.CONDITIONAL # Token balance token_id="TOKEN_ID" # For conditional tokens ) )
Update/refresh allowance cache
client.update_balance_allowance(params=...)
Market Discovery
Get Markets from CLOB
All active markets
markets = client.get_markets(next_cursor="MA==") simplified = client.get_simplified_markets()
Specific market
market = client.get_market(condition_id="CONDITION_ID")
Market trade events
events = client.get_market_trades_events(condition_id="CONDITION_ID")
Sampling/featured markets
client.get_sampling_markets() client.get_sampling_simplified_markets()
Get Markets from Gamma API (More Details)
import requests
def search_markets(query: str, limit: int = 10): """Search Polymarket markets by keyword""" url = "https://gamma-api.polymarket.com/markets" params = { "_q": query, "active": "true", "closed": "false", "_limit": limit } r = requests.get(url, params=params) return r.json()
Get market details
markets = search_markets("bitcoin") for m in markets: print(f"Question: {m['question']}") print(f"Condition ID: {m['condition_id']}") print(f"Volume: ${m.get('volume', 0):,.2f}") for token in m.get('tokens', []): print(f" {token['outcome']}: {token['token_id']}") print(f" Price: {float(token['price']):.2f}")
On-Chain Operations
Check Token Balance (Position Size)
import requests
CTF_CONTRACT = "0x4D97DCd97eC945f40cF65F87097ACe5EA0476045" RPC_URL = "https://polygon-rpc.com/"
def get_token_balance(wallet: str, token_id: str) -> float: """Get balance of a specific outcome token in shares""" token_int = int(token_id) # ERC-1155 balanceOf(address,uint256) data = f"0x00fdd58e000000000000000000000000{wallet[2:].lower()}{token_int:064x}"
r = requests.post(RPC_URL, json={
"jsonrpc": "2.0",
"method": "eth_call",
"params": [{"to": CTF_CONTRACT, "data": data}, "latest"],
"id": 1
})
result = r.json().get("result", "0x0")
balance = int(result, 16) / 1e6 # Convert from raw to shares
return balance
Usage
balance = get_token_balance( wallet="0x7c2211103e7Fbb257Ac6fa59f972cfd8bc9D4795", token_id="12345678901234567890" ) print(f"Position: {balance} shares")
Check USDC Balance
USDC_CONTRACT = "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174"
def get_usdc_balance(wallet: str) -> float: """Get USDC balance on Polygon""" # ERC-20 balanceOf(address) data = f"0x70a08231000000000000000000000000{wallet[2:].lower()}"
r = requests.post(RPC_URL, json={
"jsonrpc": "2.0",
"method": "eth_call",
"params": [{"to": USDC_CONTRACT, "data": data}, "latest"],
"id": 1
})
result = r.json().get("result", "0x0")
return int(result, 16) / 1e6 # USDC has 6 decimals
API Key Management
Create new API key
creds = client.create_api_key(nonce=0)
Derive existing API key (if you lost creds but have private key)
creds = client.derive_api_key(nonce=0)
Create or derive (tries both)
creds = client.create_or_derive_api_creds(nonce=0)
Get all your API keys
keys = client.get_api_keys()
Delete current API key
client.delete_api_key()
Readonly API keys (for monitoring only)
readonly = client.create_readonly_api_key() client.get_readonly_api_keys() client.delete_readonly_api_key(key="...") client.validate_readonly_api_key(address="0x...", key="...")
Advanced Features
Order Heartbeat (Keep Orders Alive)
Start heartbeat - if not sent within 10s, all orders cancelled
heartbeat_id = client.post_heartbeat(heartbeat_id=None)
Continue sending heartbeats
while trading: client.post_heartbeat(heartbeat_id=heartbeat_id) time.sleep(5)
Order Scoring
Check if order is scoring (earning rewards)
is_scoring = client.is_order_scoring( params=OrderScoringParams(order_id="...") )
Check multiple orders
scores = client.are_orders_scoring( params=OrdersScoringParams(order_ids=["ID1", "ID2"]) )
Notifications
notifications = client.get_notifications() client.drop_notifications(params=DropNotificationParams(...))
Fee Structure
IMPORTANT: Most Polymarket markets have ZERO fees (0% maker, 0% taker).
15-min Crypto Markets (Exception)
Only 15-minute BTC/ETH/SOL/XRP price prediction markets have fees:
fee = shares × 0.25 × (price × (1 - price))²
Entry Price Fee % (per side)
0.50 ~1.56%
0.60 or 0.40 ~1.44%
0.70 or 0.30 ~1.10%
0.80 or 0.20 ~0.64%
0.90 or 0.10 ~0.20%
TAKER = crosses spread = PAYS fee MAKER = adds liquidity = NO fee + earns rebates
To be a maker: Post orders that don't immediately fill (inside the spread).
Decimal Precision Rules
Order Side Price Decimals Size Decimals
BUY 2 4
SELL 2 2
Min order size: $1 per side
Complete Trading Example
#!/usr/bin/env python3 """ Production-ready Polymarket trading script """
import os import time import requests from py_clob_client.client import ClobClient from py_clob_client.clob_types import OrderArgs, ApiCreds, OrderType
Initialize
client = ClobClient( "https://clob.polymarket.com", key=os.getenv("PRIVATE_KEY"), chain_id=137, funder=os.getenv("POLY_FUNDER_ADDRESS"), signature_type=2 ) client.set_api_creds(ApiCreds( api_key=os.getenv("POLY_API_KEY"), api_secret=os.getenv("POLY_API_SECRET"), api_passphrase=os.getenv("POLY_API_PASSPHRASE") ))
TOKEN_ID = "YOUR_TOKEN_ID" WALLET = os.getenv("POLY_FUNDER_ADDRESS") CTF = "0x4D97DCd97eC945f40cF65F87097ACe5EA0476045"
def get_balance(token_id): """Get position size""" token_int = int(token_id) data = f"0x00fdd58e000000000000000000000000{WALLET[2:].lower()}{token_int:064x}" r = requests.post("https://polygon-rpc.com/", json={ "jsonrpc": "2.0", "method": "eth_call", "params": [{"to": CTF, "data": data}, "latest"], "id": 1 }) return int(r.json().get("result", "0x0"), 16) / 1e6
def get_orderbook(token_id): """Get current bid/ask""" book = client.get_order_book(token_id) return { "best_bid": float(book.bids[0].price) if book.bids else 0, "best_ask": float(book.asks[0].price) if book.asks else 1 }
Check position
position = get_balance(TOKEN_ID) print(f"Current position: {position} shares")
Get market
book = get_orderbook(TOKEN_ID) print(f"Bid: {book['best_bid']:.2f}, Ask: {book['best_ask']:.2f}")
Place a buy order (maker - inside spread)
buy_price = book['best_bid'] + 0.01 # 1 cent above bid if buy_price < book['best_ask']: # Ensure we're maker result = client.create_and_post_order(OrderArgs( token_id=TOKEN_ID, price=buy_price, size=10.0, side="BUY" )) print(f"Buy order placed: {result}")
Place a sell order (market sell via FOK)
if position > 0: result = client.create_and_post_order(OrderArgs( token_id=TOKEN_ID, price=0.01, # Lowest price = immediate fill size=position, side="SELL" )) print(f"Sold position: {result}")
Error Handling
from py_clob_client.exceptions import PolyApiException
try: result = client.create_and_post_order(OrderArgs(...)) except PolyApiException as e: print(f"API Error: {e}") except Exception as e: print(f"Error: {e}")
Common errors:
-
"insufficient balance"
-
Not enough USDC/tokens
-
"invalid price"
-
Price outside 0.01-0.99 or wrong decimals
-
"order too small"
-
Below minimum order size
-
"market closed"
-
Market not accepting orders
CLI Commands (/poly )
Access Polymarket trading directly from Claude Code:
Market Data
/poly search <query> # Search markets /poly market <condition-id> # Market details /poly book <token-id> # View orderbook
Trading
/poly buy <token-id> <size> <price> # Buy shares (limit) /poly sell <token-id> <size> <price> # Sell shares (limit) /poly orders # Open orders /poly cancel <order-id> # Cancel order /poly cancel all # Cancel all orders /poly trades [limit] # Recent trade history /poly balance # USDC + positions
Advanced Orders
/poly twap <buy|sell> <token> <total> <price> [slices] [interval-sec] /poly bracket <token> <size> <tp> <sl> # TP + SL bracket /poly trigger buy <token> <size> <price> # Buy when price drops
Note: TWAP and bracket orders are persisted to the database and will automatically resume after restarts.
Auto-Redeem (Resolved Positions)
/poly redeem # One-time redeem all resolved positions /poly redeem start # Start auto-polling (default: every 60s) /poly redeem stop # Stop auto-polling /poly redeem status # Check auto-redeemer status /poly redeem pending # List positions pending redemption /poly redeem <conditionId> <tokenId> # Redeem specific position
Env vars:
- POLY_REDEEM_INTERVAL_MS
- Polling interval in ms (default: 60000 = 1 minute)
Real-Time Fills (WebSocket)
/poly fills # Connect fills WebSocket /poly fills status # Show connection + recent fills /poly fills stop # Disconnect fills WebSocket /poly fills clear # Clear tracked fills
Order Heartbeat
/poly heartbeat # Start heartbeat (keeps orders alive) /poly heartbeat status # Check heartbeat status /poly heartbeat stop # Stop heartbeat (orders cancelled in 10s)
Account & Settlements
/poly settlements # View pending settlements from resolved markets /poly allowance # Check USDC approval status /poly orderbooks <token1> [token2] ... # Batch fetch orderbooks
Complete ClobClient Method Reference
Health & Config (L0 - No Auth)
Method Parameters Returns Description
get_ok()
dict Health check
get_server_time()
dict Server timestamp
get_address()
str Your signer address
get_collateral_address()
str USDC contract
get_conditional_address()
str CTF contract
get_exchange_address(neg_risk)
bool str Exchange contract
Market Data (L0 - No Auth)
Method Parameters Returns Description
get_midpoint(token_id)
str dict Mid price
get_midpoints(params)
list[BookParams] dict Multiple mid prices
get_price(token_id, side)
str, str dict Best price for side
get_prices(params)
list[BookParams] dict Multiple prices
get_spread(token_id)
str dict Bid-ask spread
get_spreads(params)
list[BookParams] dict Multiple spreads
get_order_book(token_id)
str OrderBookSummary Full orderbook
get_order_books(params)
list[BookParams] list Multiple orderbooks
get_last_trade_price(token_id)
str dict Last trade
get_last_trades_prices(params)
list[BookParams] dict Multiple last trades
get_tick_size(token_id)
str TickSize "0.1"/"0.01"/"0.001"/"0.0001"
get_neg_risk(token_id)
str bool Is neg_risk market
get_fee_rate_bps(token_id)
str int Fee in basis points
Market Discovery (L0 - No Auth)
Method Parameters Returns Description
get_markets(next_cursor)
str dict Paginated markets
get_simplified_markets(next_cursor)
str dict Simplified markets
get_sampling_markets(next_cursor)
str dict Featured markets
get_market(condition_id)
str dict Single market
get_market_trades_events(condition_id)
str dict Trade events
Order Creation (L1 - Needs Private Key)
Method Parameters Returns Description
create_order(order_args, options)
OrderArgs, CreateOrderOptions dict Sign limit order
create_market_order(order_args, options)
MarketOrderArgs, CreateOrderOptions dict Sign market order
calculate_market_price(token_id, side, amount, order_type)
str, str, float, OrderType float Expected fill price
Order Posting (L2 - Needs API Creds)
Method Parameters Returns Description
post_order(order, orderType, post_only)
SignedOrder, OrderType, bool dict Post single order
post_orders(args)
list[PostOrdersArgs] dict Post batch orders
create_and_post_order(order_args, options)
OrderArgs, PartialCreateOrderOptions dict Create + post (recommended)
Order Cancellation (L2 - Needs API Creds)
Method Parameters Returns Description
cancel(order_id)
str dict Cancel one order
cancel_orders(order_ids)
list[str] dict Cancel multiple
cancel_all()
dict Cancel ALL orders
cancel_market_orders(market, asset_id)
str, str dict Cancel by market
Order Queries (L2 - Needs API Creds)
Method Parameters Returns Description
get_orders(params, next_cursor)
OpenOrderParams, str list Get open orders
get_order(order_id)
str dict Get specific order
get_trades(params, next_cursor)
TradeParams, str list Trade history
API Key Management (L2 - Needs API Creds)
Method Parameters Returns Description
create_api_key(nonce)
int ApiCreds Create new key
derive_api_key(nonce)
int ApiCreds Derive existing key
create_or_derive_api_creds(nonce)
int ApiCreds Create or derive
set_api_creds(creds)
ApiCreds
Set credentials
get_api_keys()
dict List your keys
delete_api_key()
dict Delete current key
create_readonly_api_key()
ReadonlyApiKeyResponse Readonly key
get_readonly_api_keys()
list[str] List readonly keys
delete_readonly_api_key(key)
str bool Delete readonly key
Balance & Allowance (L2 - Needs API Creds)
Method Parameters Returns Description
get_balance_allowance(params)
BalanceAllowanceParams dict Check balance
update_balance_allowance(params)
BalanceAllowanceParams dict Refresh allowance
Advanced Features (L2 - Needs API Creds)
Method Parameters Returns Description
post_heartbeat(heartbeat_id)
str dict Keep orders alive (10s timeout)
is_order_scoring(params)
OrderScoringParams dict Check if earning rewards
are_orders_scoring(params)
OrdersScoringParams dict Check multiple orders
get_notifications()
dict Get notifications
drop_notifications(params)
DropNotificationParams dict Delete notifications
get_closed_only_mode()
dict Check closed-only status
Contract Addresses (Polygon Mainnet)
USDC = "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174" # Collateral token CTF = "0x4D97DCd97eC945f40cF65F87097ACe5EA0476045" # Conditional tokens (ERC-1155) EXCHANGE = "0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E" # Regular exchange NEG_RISK = "0xC5d563A36AE78145C45a50134d48A1215220f80a" # Neg risk exchange (crypto)
Quick Reference: Common Patterns
Check Position → Sell All
balance = get_token_balance(wallet, token_id) if balance > 0: client.create_and_post_order(OrderArgs( token_id=token_id, price=0.01, size=balance, side="SELL" ))
Maker Entry (No Fees)
book = client.get_order_book(token_id) best_bid = float(book.bids[0].price) if book.bids else 0 maker_price = best_bid + 0.01 # 1 cent above bid signed = client.create_order(OrderArgs(token_id=token_id, price=maker_price, size=100, side="BUY")) client.post_order(signed, orderType=OrderType.GTC, post_only=True)
Market Buy $50 Worth
signed = client.create_market_order(MarketOrderArgs(token_id=token_id, amount=50.0, side="BUY")) client.post_order(signed, orderType=OrderType.FOK)
Cancel Everything
client.cancel_all()
WebSocket Channels (Real-Time Updates)
Polymarket provides WebSocket channels for real-time updates. No RPC needed - everything goes through CLOB.
WebSocket URLs
MARKET_WS = "wss://ws-subscriptions-clob.polymarket.com/ws/market" USER_WS = "wss://ws-subscriptions-clob.polymarket.com/ws/user"
Market Channel (Public - No Auth)
Subscribe to orderbook updates, price changes, trades for any market.
import websocket import json
def on_message(ws, message): data = json.loads(message) event_type = data.get("event_type")
if event_type == "book":
# Full orderbook snapshot (on subscribe + after trades)
print(f"Book update: {data['bids']}, {data['asks']}")
elif event_type == "price_change":
# Order placed/cancelled affecting price level
print(f"Price change: {data}")
elif event_type == "last_trade_price":
# Trade executed
print(f"Trade: {data['price']} x {data['size']}")
elif event_type == "tick_size_change":
# Tick size changed (price went extreme)
print(f"Tick size: {data['old_tick_size']} -> {data['new_tick_size']}")
def on_open(ws): # Subscribe to specific token ws.send(json.dumps({ "type": "subscribe", "channel": "market", "assets_ids": [TOKEN_ID] # List of token IDs }))
ws = websocket.WebSocketApp( "wss://ws-subscriptions-clob.polymarket.com/ws/market", on_message=on_message, on_open=on_open ) ws.run_forever()
User Channel (Authenticated - For Fills)
Subscribe to YOUR order updates, fills, trades. This is how you get fill notifications.
import websocket import json import hmac import hashlib import time import base64
API_KEY = os.getenv("POLY_API_KEY") API_SECRET = os.getenv("POLY_API_SECRET") API_PASSPHRASE = os.getenv("POLY_API_PASSPHRASE")
def get_auth_headers(): """Generate HMAC auth for WebSocket""" timestamp = str(int(time.time())) message = f"GET\n{timestamp}\n/ws/user" signature = hmac.new( base64.b64decode(API_SECRET), message.encode(), hashlib.sha256 ).digest() return { "POLY-ADDRESS": WALLET_ADDRESS, "POLY-SIGNATURE": base64.b64encode(signature).decode(), "POLY-TIMESTAMP": timestamp, "POLY-API-KEY": API_KEY, "POLY-PASSPHRASE": API_PASSPHRASE }
def on_message(ws, message): data = json.loads(message) event_type = data.get("event_type")
if event_type == "trade":
# YOUR FILL - order matched!
status = data.get("status") # MATCHED, MINED, CONFIRMED, FAILED
print(f"FILL: {data['side']} {data['size']} @ {data['price']}")
print(f" Status: {status}")
print(f" Trade ID: {data['id']}")
print(f" Market: {data['market']}")
if status == "CONFIRMED":
print(" ✓ Trade confirmed on-chain!")
elif status == "FAILED":
print(" ✗ Trade failed - check logs")
elif event_type == "order":
order_type = data.get("type") # PLACEMENT, UPDATE, CANCELLATION
print(f"ORDER {order_type}: {data['side']} {data['original_size']} @ {data['price']}")
print(f" Filled: {data.get('size_matched', 0)}")
if order_type == "CANCELLATION":
print(" Order cancelled")
def on_open(ws): # Subscribe with auth ws.send(json.dumps({ "type": "subscribe", "channel": "user", "auth": get_auth_headers(), # Optional: filter to specific market # "markets": [CONDITION_ID] }))
ws = websocket.WebSocketApp( "wss://ws-subscriptions-clob.polymarket.com/ws/user", on_message=on_message, on_open=on_open ) ws.run_forever()
Message Types Summary
Market Channel:
Event Trigger Key Fields
book
Subscribe, trades affect book bids, asks, timestamp
price_change
Order placed/cancelled price, size, side
last_trade_price
Trade executed price, size, side
tick_size_change
Price extreme (>0.96 or <0.04) old_tick_size, new_tick_size
User Channel:
Event Trigger Key Fields
trade
Your order filled status, side, size, price, market
order
Order placed/updated/cancelled type (PLACEMENT/UPDATE/CANCELLATION), size_matched
Trade Status Flow
MATCHED → MINED → CONFIRMED (success) → RETRYING → CONFIRMED/FAILED
Keepalive
Send PING every 10 seconds to keep connection alive:
import threading
def send_ping(): while True: ws.send(json.dumps({"type": "ping"})) time.sleep(10)
threading.Thread(target=send_ping, daemon=True).start()
Real-Time Data Client (Alternative)
Polymarket also provides @polymarket/real-time-data-client for TypeScript:
import { RealTimeDataClient } from "@polymarket/real-time-data-client";
const client = new RealTimeDataClient({ onMessage: (msg) => console.log(msg), onConnect: (c) => { // Subscribe to user fills with auth c.subscribe({ subscriptions: [{ topic: "clob_user", type: "*", clob_auth: { key: API_KEY, secret: API_SECRET, passphrase: API_PASSPHRASE } }] }); } });
client.connect();
No RPC Needed - CLOB Handles Everything
Operation Method
Get positions GET /data/positions?user={address} via Gamma API
Get balance client.get_balance_allowance()
Place orders client.create_and_post_order()
Get fills User WebSocket channel
Get prices client.get_order_book() or Market WebSocket
Cancel orders client.cancel()
The only time you might use RPC is to check on-chain token balances independently, but even that's available via Gamma API positions endpoint.