Polymarket API Tutorial: Build Your First Trading Bot
A developer's guide to the Polymarket CLOB API. Learn how to fetch market data, place orders, and build a complete trading bot from scratch using Python.
Polymarket's CLOB (Central Limit Order Book) API gives developers full access to prediction market trading. In this tutorial, we'll build a complete trading bot from scratch using Python and the official py-clob-client library.
Skip the Coding?
If you'd rather not code, PredictEngine lets you create bots with plain English through our AI builder. Try it free.
Prerequisites
Before we start, make sure you have:
Ready to Start Trading?
PredictEngine lets you create automated trading bots for Polymarket in seconds. No coding required.
Get Started Free- Python 3.9 or higher installed
- A Polygon wallet with some MATIC for gas
- USDC on Polygon for trading
- Basic understanding of async Python
1Install the SDK
First, install the official Polymarket Python client:
pip install py-clob-client python-dotenv2Authentication Levels
The Polymarket API has three authentication levels:
| Level | Requirements | Capabilities |
|---|---|---|
| Level 0 | None | Read market data only |
| Level 1 | Private key | Create API credentials |
| Level 2 | API credentials | Place/cancel orders |
3Initialize the Client
Create a new file called bot.py:
from py_clob_client.client import ClobClient
from py_clob_client.clob_types import OrderArgs, OrderType
from py_clob_client.order_builder.constants import BUY, SELL
import os
from dotenv import load_dotenv
load_dotenv()
# Read-only client (Level 0)
client = ClobClient("https://clob.polymarket.com")
# Trading client (Level 2)
trading_client = ClobClient(
host="https://clob.polymarket.com",
key=os.getenv("PRIVATE_KEY"),
chain_id=137, # Polygon mainnet
signature_type=2, # 0=EOA, 1=Magic, 2=Proxy
funder=os.getenv("PROXY_ADDRESS") # Optional
)
# Derive API credentials
creds = trading_client.create_or_derive_api_creds()
trading_client.set_api_creds(creds)Security Warning
Never commit private keys to git. Use environment variables or a .env file. Add .env to your .gitignore.
4Fetch Market Data
Let's fetch available markets and their current prices:
# Get all markets
markets = client.get_simplified_markets()
# Print first 5 markets
for market in markets[:5]:
print(f"Question: {market['question']}")
print(f"Condition ID: {market['condition_id']}")
print(f"Tokens: {market['tokens']}")
print("---")
# Get orderbook for a specific token
token_id = "YOUR_TOKEN_ID"
orderbook = client.get_order_book(token_id)
print(f"Best bid: {orderbook['bids'][0]['price']}")
print(f"Best ask: {orderbook['asks'][0]['price']}")
# Get midpoint price
mid = client.get_midpoint(token_id)
print(f"Midpoint: {mid}")5Place Orders
Now let's place a limit order. There are several order types:
| Order Type | Behavior |
|---|---|
| GTC | Good-Till-Cancelled - stays until filled or cancelled |
| GTD | Good-Till-Date - expires at specified timestamp |
| FOK | Fill-Or-Kill - must fill completely or cancel |
| FAK | Fill-And-Kill - fill what's possible, cancel rest |
# Place a limit buy order
order_args = OrderArgs(
token_id="YOUR_TOKEN_ID",
price=0.45, # $0.45 per share
size=100.0, # 100 shares
side=BUY # or SELL
)
# Sign and post the order
signed_order = trading_client.create_order(order_args)
response = trading_client.post_order(signed_order, OrderType.GTC)
print(f"Order ID: {response['orderID']}")
# Place a market order (FOK)
from py_clob_client.clob_types import MarketOrderArgs
market_order = MarketOrderArgs(
token_id="YOUR_TOKEN_ID",
amount=25.0, # $25 to spend
side=BUY
)
signed_market = trading_client.create_market_order(market_order)
response = trading_client.post_order(signed_market, OrderType.FOK)6Manage Orders
from py_clob_client.clob_types import OpenOrderParams
# Get all open orders
open_orders = trading_client.get_orders(OpenOrderParams())
for order in open_orders:
print(f"Order: {order['id']} - {order['side']} @ {order['price']}")
# Cancel a specific order
trading_client.cancel(order_id="YOUR_ORDER_ID")
# Cancel all orders
trading_client.cancel_all()7Build a Simple Trading Bot
Let's put it all together with a simple arbitrage bot that looks for markets where YES + NO < 0.95:
import asyncio
import time
class ArbitrageBot:
def __init__(self, client, min_spread=0.05):
self.client = client
self.min_spread = min_spread
self.positions = {}
def find_opportunities(self, markets):
"""Find markets where YES + NO < (1 - min_spread)"""
opportunities = []
threshold = 1 - self.min_spread
for market in markets:
tokens = market.get('tokens', [])
if len(tokens) < 2:
continue
yes_token = tokens[0]
no_token = tokens[1]
# Get current prices
yes_price = float(self.client.get_midpoint(yes_token['token_id']))
no_price = float(self.client.get_midpoint(no_token['token_id']))
combined = yes_price + no_price
if combined < threshold:
profit_pct = ((1 - combined) / combined) * 100
opportunities.append({
'market': market['question'],
'yes_price': yes_price,
'no_price': no_price,
'combined': combined,
'profit_pct': profit_pct,
'yes_token': yes_token['token_id'],
'no_token': no_token['token_id']
})
return sorted(opportunities, key=lambda x: x['profit_pct'], reverse=True)
def execute_arbitrage(self, opportunity, amount=25):
"""Buy both YES and NO sides"""
yes_shares = int(amount / opportunity['yes_price'])
no_shares = int(amount / opportunity['no_price'])
# Buy YES
yes_order = OrderArgs(
token_id=opportunity['yes_token'],
price=opportunity['yes_price'] * 1.02, # 2% slippage
size=float(yes_shares),
side=BUY
)
self.client.post_order(
self.client.create_order(yes_order),
OrderType.GTC
)
# Buy NO
no_order = OrderArgs(
token_id=opportunity['no_token'],
price=opportunity['no_price'] * 1.02,
size=float(no_shares),
side=BUY
)
self.client.post_order(
self.client.create_order(no_order),
OrderType.GTC
)
print(f"Executed arbitrage: {opportunity['market']}")
print(f"Expected profit: {opportunity['profit_pct']:.2f}%")
async def run(self, interval=30):
"""Main bot loop"""
while True:
try:
markets = self.client.get_simplified_markets()
opportunities = self.find_opportunities(markets)
if opportunities:
print(f"Found {len(opportunities)} opportunities")
best = opportunities[0]
if best['profit_pct'] >= 3: # Min 3% profit
self.execute_arbitrage(best)
except Exception as e:
print(f"Error: {e}")
await asyncio.sleep(interval)
# Run the bot
if __name__ == "__main__":
bot = ArbitrageBot(trading_client, min_spread=0.05)
asyncio.run(bot.run())Important Contract Addresses
For approvals and direct contract interaction, here are the key addresses:
# Token Addresses USDC_E = "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174" CTF = "0x4D97DCd97eC945f40cF65F87097ACe5EA0476045" # Exchange Contracts (approve tokens for these) CTF_EXCHANGE = "0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E" NEG_RISK_CTF = "0xC5d563A36AE78145C45a50134d48A1215220f80a" NEG_RISK_ADAPTER = "0xd91E80cF2E7be2e162c6513ceD06f1dD0dA35296"
API Rate Limits
Be mindful of rate limits to avoid getting blocked:
| Endpoint | Limit |
|---|---|
| Market data | 100 requests/minute |
| Order placement | 50 requests/minute |
| Order cancellation | 100 requests/minute |
Don't Want to Code?
PredictEngine lets you create trading bots in plain English. Our AI handles all the technical complexity.
Try the No-Code BuilderFree tier available. No credit card required.
Troubleshooting Common Issues
Order rejected: insufficient balance
Ensure you have enough USDC in your wallet and have approved the exchange contracts to spend it.
Order not filling
Your limit price might be too far from market. Try using a FOK order with slight slippage for immediate fills.
Signature verification failed
Check your signature_type matches your wallet type. EOA wallets use 0, proxy wallets use 2.
Rate limit exceeded
Add delays between API calls. Cache market data locally and refresh every 30 seconds instead of on every check.
Next Steps
Now that you have a basic bot running, consider these improvements:
- Add position tracking and P&L monitoring
- Implement stop losses and take profit targets
- Add notification system (Telegram, Discord)
- Implement risk management limits
- Add logging and error recovery