HomeBlogTutorial
Back to Blog
TutorialJanuary 19, 2026

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.

12 min readPython

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:

Terminal
pip install py-clob-client python-dotenv

2Authentication Levels

The Polymarket API has three authentication levels:

LevelRequirementsCapabilities
Level 0NoneRead market data only
Level 1Private keyCreate API credentials
Level 2API credentialsPlace/cancel orders

3Initialize the Client

Create a new file called bot.py:

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 TypeBehavior
GTCGood-Till-Cancelled - stays until filled or cancelled
GTDGood-Till-Date - expires at specified timestamp
FOKFill-Or-Kill - must fill completely or cancel
FAKFill-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:

EndpointLimit
Market data100 requests/minute
Order placement50 requests/minute
Order cancellation100 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 Builder

Free 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