This tutorial walks you through the Polymarket API from first authentication to a working trading bot. You’ll set up py_clob_client, authenticate with EIP-712, place orders, stream prices via WebSocket, and handle rate limits — all with tested Python code.

Polymarket splits its functionality across three APIs (CLOB, Gamma, Data) plus WebSocket channels. This guide covers them all in the order you’ll need them. For a method-by-method SDK reference, see the py_clob_client Reference. For a side-by-side comparison of Polymarket and Kalshi endpoints, see the Prediction Market API Reference.

Last verified against Polymarket’s API: April 2026. We update this guide whenever the API changes. Check the Polymarket Changelog for the latest updates.

Try it live: Test these endpoints instantly in the API Playground — no setup, no keys, real data in your browser.


Architecture Overview

Polymarket’s API isn’t one API — it’s three primary APIs plus a Bridge API and real-time streaming channels. Understanding this architecture is essential before writing any code.

ServiceBase URLAuth RequiredPurpose
CLOB APIhttps://clob.polymarket.comPartial (trading endpoints)Order book, prices, order management
Gamma APIhttps://gamma-api.polymarket.comNoMarket discovery, metadata, events
Data APIhttps://data-api.polymarket.comNoUser positions, trade history, leaderboards
Bridge APIhttps://bridge.polymarket.comYesDeposits and withdrawals (proxies fun.xyz)

Real-time streaming channels (not separate REST APIs):

ChannelEndpointAuthPurpose
Market WebSocketwss://ws-subscriptions-clob.polymarket.com/ws/marketNoOrderbook updates, price changes, new markets
User WebSocketwss://ws-subscriptions-clob.polymarket.com/ws/userYesOrder fills, cancellations, status updates
Sports WebSocketwss://sports-api.polymarket.com/wsNoLive game scores and status
RTDSwss://ws-live-data.polymarket.comOptionalCrypto prices, comments stream

Polymarket’s sports channel covers game scores and status, but not traditional odds formats. For comparing prediction market probabilities against sportsbook lines, see Offshore Sportsbook Odds Normalization and the Offshore Sportsbook API Guide.

Polymarket US: A separate CFTC-regulated API exists at api.polymarket.us with different auth (Ed25519), different SDKs, and KYC requirements. See the Polymarket US vs. Global API Guide for a complete comparison.

The mental model: Use the Gamma API to discover markets. Use the CLOB API to read prices and trade. Use the Data API to track positions and history. Use WebSockets for real-time updates.


How Polymarket Works Under the Hood

Before diving into endpoints, you need to understand the on-chain architecture because it affects how you interact with the API.

Polymarket runs on the Polygon blockchain. Markets use the Conditional Token Framework (CTF), an ERC-1155 standard where each market outcome is a tradable token. When you “buy YES” on a market, you’re actually acquiring YES outcome tokens.

The CLOB (Central Limit Order Book) is hybrid-decentralized: order matching happens off-chain for speed, but settlement happens on-chain for security. Orders are EIP-712 signed messages — you sign a structured order with your private key, and the operator matches it off-chain, then settles the swap on-chain via the Exchange contract.

Key implications for developers:

  • You need a Polygon wallet (private key) to trade
  • USDC is the collateral asset — all positions are denominated in USDC on Polygon
  • Token IDs are critical — every market outcome has a unique token ID (not a ticker symbol)
  • Prices range from 0.00 to 1.00 — representing the probability (and cost in USDC) of that outcome
  • YES + NO prices should sum to ~$1.00 — any deviation is an arbitrage opportunity
  • Tick sizes matter — most markets use 0.01 ticks, but some use 0.001

Getting Started: Your First API Call (No Auth Required)

The fastest way to start is reading public market data. No wallet, no keys, no authentication.

Fetch All Active Markets (Gamma API)

import requests

# Fetch open markets from the Gamma API
response = requests.get(
    "https://gamma-api.polymarket.com/markets",
    params={
        "closed": False,
        "limit": 10
    }
)

markets = response.json()

for market in markets:
    print(f"{market['question']}")
    print(f"  Token ID (YES): {market['clobTokenIds']}")
    print(f"  Outcome Prices: {market['outcomePrices']}")
    print(f"  Volume: ${float(market.get('volume', 0)):,.0f}")
    print()

Get a Price (CLOB API)

# Get the current price for a specific outcome token
token_id = "<your-token-id>"  # Get this from the Gamma API response

price = requests.get(
    f"https://clob.polymarket.com/price",
    params={
        "token_id": token_id,
        "side": "BUY"
    }
)

print(f"Current buy price: {price.json()['price']}")

Get the Order Book (CLOB API)

book = requests.get(
    f"https://clob.polymarket.com/book",
    params={"token_id": token_id}
)

data = book.json()
print(f"Best bid: {data['bids'][0]['price'] if data['bids'] else 'No bids'}")
print(f"Best ask: {data['asks'][0]['price'] if data['asks'] else 'No asks'}")
print(f"Midpoint: {data.get('mid', 'N/A')}")

Authentication

Public endpoints (prices, order books, market listings) require no authentication. Trading endpoints (placing orders, canceling orders, checking your positions) require EIP-712 signed requests.

How Authentication Works

  1. You have a private key — either from an EOA (MetaMask), an email/Magic wallet, or a browser wallet
  2. You derive API credentials — the client library generates a key/secret pair from your wallet signature
  3. You sign each trading request — using five L2 headers: POLY-ADDRESS, POLY-SIGNATURE, POLY-TIMESTAMP, POLY-API-KEY, and POLY-PASSPHRASE

Auth issues? See our Polymarket Auth Troubleshooting Guide for common error codes (INVALID_SIGNATURE, NONCE_ALREADY_USED, clock drift) and step-by-step fixes.

Wallet Types and Signature Types

Polymarket supports three wallet configurations:

Wallet Typesignature_typefunder RequiredNotes
EOA (MetaMask)0NoDirect wallet, must set token allowances
Email / Magic wallet1Yes — your proxy addressLegacy Polymarket web users
Browser / Gnosis Safe2Yes — your proxy addressMost common wallet type (Coinbase Wallet, Safe, browser proxy)

The funder address is the actual address holding your funds on Polymarket. When using proxy wallets, the signing key differs from the funded address.

Python Authentication Setup

from py_clob_client.client import ClobClient

HOST = "https://clob.polymarket.com"
CHAIN_ID = 137  # Polygon mainnet

# For EOA (MetaMask) users:
client = ClobClient(
    HOST,
    key="<your-private-key>",
    chain_id=CHAIN_ID
)

# For browser wallet / Gnosis Safe users (most common):
client = ClobClient(
    HOST,
    key="<your-private-key>",
    chain_id=CHAIN_ID,
    signature_type=2,
    funder="<your-proxy-wallet-address>"
)

# Derive and set API credentials (do this once, then reuse)
client.set_api_creds(client.create_or_derive_api_creds())

TypeScript Authentication Setup

import { ClobClient, Side } from "@polymarket/clob-client";

const client = new ClobClient(
    "https://clob.polymarket.com",
    137,  // Polygon chain ID
    signer,  // ethers.js Signer
    creds    // API credentials
);

Rust Authentication Setup

use polymarket_client_sdk::clob::{Client, Config};
use alloy::signers::local::LocalSigner;
use std::str::FromStr;

let private_key = std::env::var("PRIVATE_KEY").expect("Need a private key");
let signer = LocalSigner::from_str(&private_key)?
    .with_chain_id(Some(137));

let client = Client::new("https://clob.polymarket.com", Config::default())?
    .authentication_builder(&signer)
    .authenticate()
    .await?;

Token Allowances (EOA Users Only)

If you’re using a direct EOA wallet (not a proxy), you must approve the Exchange contracts before trading:

# You need to approve USDC and conditional tokens for the exchange contracts
# This only needs to be done once per wallet
# See: https://docs.polymarket.com/developers/CLOB/quickstart

Proxy wallet users (email or browser wallet) do not need to set allowances — the proxy handles this.


CLOB API Reference

The CLOB API is the core trading interface. It handles prices, order books, and order management.

Public Endpoints (No Auth)

GET /price

Returns the current price for a token.

GET https://clob.polymarket.com/price?token_id=<id>&side=BUY

Parameters:

  • token_id (required) — The outcome token ID
  • side (required) — BUY or SELL

Example response:

{
  "price": "0.567"
}

Try /price live in the API Playground →

GET /midpoint

Returns the midpoint between best bid and best ask.

GET https://clob.polymarket.com/midpoint?token_id=<id>

Example response:

{
  "mid": "0.5650"
}

Try /midpoint live in the API Playground →

GET /book

Returns the full order book for a token.

GET https://clob.polymarket.com/book?token_id=<id>

Response includes bids, asks, and market metadata. The book endpoints now return enriched market metadata fields — including question text, slug, end date, outcome labels, and market status — that previously required separate Gamma API calls. This means you can get basic market context directly from the order book response without a second round-trip to the Gamma API.

Example response (truncated):

{
  "market": "0x...",
  "asset_id": "21742633143463906290569050155826241533067272736897614950488156847949938836455",
  "bids": [
    {"price": "0.56", "size": "1250.00"},
    {"price": "0.55", "size": "3400.00"}
  ],
  "asks": [
    {"price": "0.57", "size": "800.00"},
    {"price": "0.58", "size": "2100.00"}
  ],
  "mid": "0.5650",
  "spread": "0.01",
  "hash": "0x..."
}

Try /book live in the API Playground →

GET /books

Batch endpoint — fetch order books for multiple tokens in one request.

from py_clob_client.clob_types import BookParams

books = client.get_order_books([
    BookParams(token_id="<token-id-1>"),
    BookParams(token_id="<token-id-2>"),
])

Authenticated Endpoints (Trading)

All trading endpoints require L2 authentication headers.

POST /order — Place a Single Order

All orders on Polymarket are limit orders. Market orders are simulated by setting a marketable price against resting liquidity.

Order types:

  • GTC (Good Til Cancelled) — Rests on the book until filled or cancelled
  • FOK (Fill or Kill) — Must execute immediately and completely, or is rejected entirely
  • FAK (Fill and Kill) — Fills whatever is available immediately, cancels the rest

Place a limit order (Python):

from py_clob_client.clob_types import OrderArgs, OrderType
from py_clob_client.order_builder.constants import BUY

order = OrderArgs(
    token_id="<token-id>",
    price=0.50,       # Price in USDC (0.00 to 1.00)
    size=10.0,        # Number of shares
    side=BUY
)

signed = client.create_order(order)
response = client.post_order(signed, OrderType.GTC)
print(response)

Place a market order (Python):

from py_clob_client.clob_types import MarketOrderArgs, OrderType
from py_clob_client.order_builder.constants import BUY

market_order = MarketOrderArgs(
    token_id="<token-id>",
    amount=25.0,       # Dollar amount to spend
    side=BUY,
    order_type=OrderType.FOK
)

signed = client.create_market_order(market_order)
response = client.post_order(signed, OrderType.FOK)

Place a limit order (TypeScript):

const order = await client.createAndPostOrder(
    {
        tokenID: "<token-id>",
        price: 0.50,
        size: 10,
        side: Side.BUY
    },
    {
        tickSize: "0.01",
        negRisk: false  // true for neg-risk markets
    }
);

Post-only orders:

The postOnly flag ensures your order is added to the book as a maker order. If it would immediately match (cross the spread), it’s rejected instead of executed. This is critical for market-making strategies where you only want to earn the maker rebate.

order = OrderArgs(
    token_id="<token-id>",
    price=0.48,
    size=100.0,
    side=BUY
)

signed = client.create_order(order)
# postOnly cannot be combined with FOK or FAK
response = client.post_order(signed, OrderType.GTC, post_only=True)

Example response:

{
  "orderID": "0xabc123...",
  "status": "live",
  "transactionsHashes": []
}

Possible status values: live (resting on book), matched (fully filled), delayed (pending matching engine processing).

POST /orders — Batch Orders

Place up to 15 orders in a single request. Essential for market makers updating multiple price levels.

orders = []
for price in [0.48, 0.49, 0.50]:
    order = OrderArgs(
        token_id="<token-id>",
        price=price,
        size=50.0,
        side=BUY
    )
    orders.append(client.create_order(order))

response = client.post_orders(orders, OrderType.GTC)

The batch limit was increased from 5 to 15 orders per call in 2025.

DELETE /order — Cancel a Single Order

response = client.cancel(order_id="<order-id>")

DELETE /orders — Cancel All Orders

response = client.cancel_all()

Gamma API Reference

The Gamma API is your market discovery layer. Use it to find markets, get metadata, and understand the structure of events.

Understanding Gamma’s Data Model

Polymarket organizes data hierarchically:

Series (e.g., "US Presidential Election")
  └── Event (e.g., "2028 Presidential Election Winner")
       └── Market (e.g., "Will Kamala Harris win?")
            └── Outcomes (YES / NO, each with a token ID)

Key Endpoints

GET /events — List Events

GET https://gamma-api.polymarket.com/events?closed=false&limit=20

Returns events with their associated markets. Each event can contain multiple markets.

GET /markets — List Markets

GET https://gamma-api.polymarket.com/markets?closed=false&limit=50

Key fields in the response:

  • question — The human-readable market question
  • clobTokenIds — Array of token IDs for each outcome (critical for trading)
  • outcomePrices — Current prices as a JSON string
  • volume — Total trading volume in USDC
  • liquidity — Current available liquidity
  • endDate — When the market resolves
  • conditionId — The on-chain condition identifier
  • slug — URL-friendly market identifier

Note (April 9, 2026): GET /markets now defaults closed=false, so passing closed=false explicitly is no longer required (though still fine). Pass closed=true to fetch resolved markets instead.

GET /events/{id} — Get Event Details

GET https://gamma-api.polymarket.com/events/<event-id>

Returns full event details including all child markets.

GET /markets?tag= — Filter by Category

Polymarket tags markets with categories. Common tags: politics, crypto, sports, science, pop-culture.

# Get all sports markets
response = requests.get(
    "https://gamma-api.polymarket.com/markets",
    params={"tag": "sports", "closed": False}
)

Sports-Specific Endpoints

Polymarket has dedicated sports endpoints for structured sports market data:

GET https://gamma-api.polymarket.com/sports

Data API Reference

The Data API provides user-specific and aggregate analytics data.

Base URL: https://data-api.polymarket.com

Key Endpoints

GET /positions — User Positions

Returns current positions for a wallet address, including token balances and unrealized P&L.

GET /activity — User Activity

Returns trade history, deposits, withdrawals.

GET /trades — Trade History

Historical trade data for analysis and backtesting.

The Data API is fully public and requires no authentication. You can query any wallet’s positions and activity.


Bridge API

The Bridge API at https://bridge.polymarket.com handles deposits and withdrawals. It proxies fun.xyz infrastructure for cross-chain bridging.

POST /withdraw

Bridges USDC.e from your Polymarket account to any supported chain and token. This endpoint was added in January 2026 and allows programmatic withdrawals without using the Polymarket web interface. Destinations now include EVM chains (ETH, Arbitrum, Base, Optimism), Solana, and Bitcoin — Polygon USDC.e is the source in all cases.

import requests

response = requests.post(
    "https://bridge.polymarket.com/withdraw",
    headers={
        "Authorization": f"Bearer {api_key}",
        "Content-Type": "application/json",
    },
    json={
        "amount": "100000000",       # Amount in USDC.e base units (6 decimals)
        "destinationChain": "ETH",   # Target chain (ETH, ARB, BASE, OP, etc.)
        "destinationToken": "USDC",  # Target token
        "destinationAddress": "0x..."  # Recipient address on destination chain
    }
)
print(response.json())

Check the Polymarket Changelog for the full list of supported chains and tokens.


WebSocket API

For real-time applications — trading bots, live dashboards, arbitrage systems — the WebSocket API is essential. Polymarket offers four WebSocket channels:

ChannelEndpointAuthHeartbeat
Marketwss://ws-subscriptions-clob.polymarket.com/ws/marketNoPING every 10s
Userwss://ws-subscriptions-clob.polymarket.com/ws/userYesPING every 10s
Sportswss://sports-api.polymarket.com/wsNoRespond to server ping within 10s
RTDSwss://ws-live-data.polymarket.comOptionalPING every 5s

Market Channel (Public)

Orderbook updates, price changes, trade data, and custom events (best_bid_ask, new_market, market_resolved when custom_feature_enabled: true).

import asyncio
import websockets
import json

async def stream_prices():
    uri = "wss://ws-subscriptions-clob.polymarket.com/ws/market"

    async with websockets.connect(uri) as ws:
        await ws.send(json.dumps({
            "assets_ids": ["<token-id>"],
            "type": "market",
            "custom_feature_enabled": True
        }))

        async def heartbeat():
            while True:
                await asyncio.sleep(10)
                await ws.send("PING")

        asyncio.create_task(heartbeat())

        async for message in ws:
            if message == "PONG":
                continue
            data = json.loads(message)
            print(f"Update: {data}")

asyncio.run(stream_prices())

User Channel (Authenticated)

Real-time order status updates — fills, cancellations, status changes. Requires API key/secret/passphrase in the subscription payload. Subscribes by condition ID (not asset ID).

Sports Channel

Streams live game scores, periods, and status for all active sports events. No subscription message needed — connections receive all active events automatically.

RTDS (Real-Time Data Socket)

Low-latency stream for crypto prices (from Binance and Chainlink sources) and platform comments. Supports topic-based subscriptions with optional filters.

WebSocket connections do not count against REST API rate limits. Using WebSockets instead of polling is the most effective way to avoid 429 errors.

HeartBeats API (January 2026): Polymarket added a dedicated HeartBeats API endpoint for connection health monitoring and automatic order cancellation on disconnect. If your bot holds open orders over a long WebSocket session, wiring this in protects you from stranded orders during transient network drops. See the Polymarket Changelog for the endpoint spec.

Deep dive: For complete channel documentation, subscription formats, orderbook reconstruction, and production reconnection patterns, see the Polymarket WebSocket & Orderbook Guide.


Rate Limits

Polymarket publishes concrete rate limits enforced via Cloudflare throttling. When limits are exceeded, requests are delayed/queued rather than immediately rejected.

APIGeneral LimitKey Endpoint Limits
CLOB9,000 / 10s/book 1,500/10s, /price 1,500/10s, POST /order 3,500/10s burst + 36K/10min sustained
Gamma4,000 / 10s/events 500/10s, /markets 300/10s, search 350/10s
Data1,000 / 10s/trades 200/10s, /positions 150/10s
General15,000 / 10sApplies across all APIs

Every response includes X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers.

Basic retry pattern:

import time
import random

def retry_on_429(func, *args, max_retries=3, **kwargs):
    for attempt in range(max_retries):
        try:
            return func(*args, **kwargs)
        except Exception as e:
            if "429" in str(e) and attempt < max_retries - 1:
                delay = 2 ** attempt + random.uniform(0, 1)
                time.sleep(delay)
            else:
                raise

If you’re hitting rate limits frequently:

  1. Use WebSockets instead of polling for real-time data
  2. Batch order operationsPOST /orders handles up to 15 orders in one request
  3. Cache Gamma API responses — market metadata changes infrequently
  4. Apply for the Market Maker program for elevated limits

Full rate limits reference: See our Polymarket Rate Limits Guide for complete header documentation, retry code in Python and TypeScript, and strategies for staying within limits.


Key Concepts for Agent Builders

Finding Token IDs

Every market outcome has a unique token ID. This is the primary identifier you’ll use for all trading operations. You get token IDs from the Gamma API:

response = requests.get(
    "https://gamma-api.polymarket.com/markets",
    params={"slug": "will-bitcoin-hit-100k-by-2026"}
)

market = response.json()[0]
token_ids = market["clobTokenIds"]  # [YES_token_id, NO_token_id]

Understanding Neg-Risk Markets

Some Polymarket markets use a “negative risk” model where multiple outcomes share a single collateral pool. This is common in multi-outcome markets (e.g., “Who will win the election?” with 5+ candidates). When trading neg-risk markets, you need to set negRisk: true in your order parameters.

Common gotcha: Forgetting negRisk: true on neg-risk markets causes three distinct failures: (1) your order is rejected with a cryptic error because the Exchange contract doesn’t match, (2) split/merge operations fail because the CTF framework routes to the wrong contract, and (3) fee calculations are wrong because neg-risk markets use a different fee structure. Always check the market’s neg_risk field from the Gamma API before placing orders.

To check programmatically:

market = requests.get(
    "https://gamma-api.polymarket.com/markets",
    params={"slug": "your-market-slug"}
).json()[0]

is_neg_risk = market.get("negRisk", False)
# Pass this to your order builder

Inventory Management: Split and Merge

The Conditional Token Framework allows you to:

  • Split USDC into YES + NO token pairs
  • Merge YES + NO tokens back into USDC
  • Redeem winning tokens after market resolution

This is essential for market makers managing inventory across both sides of a market.

# Splitting: 1 USDC → 1 YES token + 1 NO token
# Merging: 1 YES token + 1 NO token → 1 USDC
# Redeem: 1 winning token → 1 USDC (after resolution)

Fees

Polymarket fees are applied symmetrically on output assets (proceeds). The fee schedule varies by market type and has expanded significantly since late 2025:

  • Maker fees: 0% on all markets — limit orders that add liquidity are free
  • Taker fees: Variable by market category (effective March 30, 2026). Peak effective rates at 50% probability: Crypto 1.80%, Sports 0.75%, Finance 1.00%, Politics 1.00%, Economics 1.50%, Culture 1.25%, Weather 1.25%, Tech 1.00%, Other/General 1.25%, Mentions 1.56%. Geopolitics markets are fee-free.
  • Maker rebates: Calculated per-market — makers compete with other makers in the same market for rebates funded by taker fees. Rebate percentages vary by category (20–50%). As of March 17, 2026, orders must remain on the book for a minimum of 3.5 seconds of active time to qualify for liquidity rewards.
  • Gas fees: Minimal on Polygon (fractions of a cent per transaction)

Reading fees programmatically (March 31, 2026 change): Per-market fee data is now exposed via a feeSchedule object on each market returned from the Gamma API, replacing the older legacy fee fields. For anything user-facing, read market["feeSchedule"] instead of hardcoding the category values above — category fees can shift and per-market overrides are now authoritative.

Check the Polymarket Changelog for the latest fee schedule and the Builder Program for current maker rebate rates.

Builder Program & Relayer Client

If you’re building a consumer-facing application on top of Polymarket (not just a personal bot), the Builder Program provides higher rate limits, volume-based rewards, and access to the Relayer Client for gasless transactions. The program has tiered levels based on volume contributed.

Builder authentication uses HMAC-signed headers that are separate from the standard L1/L2 CLOB authentication. You need builder-specific API credentials (key, secret, passphrase) obtained through the Builder Program onboarding at builders.polymarket.com.

Relayer Client enables gasless order submission — your users don’t need MATIC for gas fees. Orders are submitted to the Relayer, which sponsors the gas and submits them on-chain. This is essential for consumer apps where end-users shouldn’t need to manage gas tokens.

SDK packages:

LanguagePackageInstall
Pythonpy-builder-relayer-clientpip install py-builder-relayer-client
TypeScript@polymarket/builder-relayer-clientnpm install @polymarket/builder-relayer-client

Python setup:

from py_builder_relayer_client import BuilderRelayerClient

relayer = BuilderRelayerClient(
    key="<your-builder-api-key>",
    secret="<your-builder-api-secret>",
    passphrase="<your-builder-passphrase>",
)

result = relayer.submit_order(signed_order)

See the Builder Program docs for onboarding, tier details, and the full Relayer API reference.


Official SDK Libraries

Looking for a complete method reference? See our py_clob_client Method Reference for every method with parameters, return types, and working code examples.

Polymarket maintains official client libraries:

LanguagePackageRepository
Pythonpy-clob-clientgithub.com/Polymarket/py-clob-client
TypeScript@polymarket/clob-clientgithub.com/Polymarket/clob-client
Rustpolymarket-client-sdkgithub.com/Polymarket/rs-clob-client

The Rust SDK is the newest addition and includes features like automatic heartbeats (if the client disconnects, all open orders are cancelled), cross-chain bridge support, and zero-cost abstractions with no dynamic dispatch in hot paths.

Community alternative: The polymarket-apis package (v0.5.3, Python 3.12+) is a third-party unified wrapper around CLOB, Gamma, Data, Web3, WebSocket, and GraphQL clients with Pydantic validation. It is actively maintained and can be a good choice if you want a single import for all Polymarket APIs. Install with pip install polymarket-apis.

Install

# Python
pip install py-clob-client
# Pin to the version this guide was verified against:
# pip install py-clob-client==0.34.6

# TypeScript
npm install @polymarket/clob-client
# Pin: npm install @polymarket/[email protected]

# Rust
cargo add polymarket-client-sdk
# Pin: cargo add [email protected]

Version note: This guide was last verified against the versions shown above. If you encounter breaking changes with newer releases, pin to these versions and let us know on Twitter.


Putting It Together: A Minimal Trading Agent

Here’s a complete Python example that finds a market, checks the price, and places an order:

import requests
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

# 1. Initialize authenticated client (signature_type=2 for browser/Safe wallets)
client = ClobClient(
    "https://clob.polymarket.com",
    key="<your-private-key>",
    chain_id=137,
    signature_type=2,
    funder="<your-proxy-address>"
)
client.set_api_creds(client.create_or_derive_api_creds())

# 2. Discover a market via the Gamma API
markets = requests.get(
    "https://gamma-api.polymarket.com/markets",
    params={"closed": False, "limit": 5, "order": "volume", "ascending": False}
).json()

target = markets[0]
token_id = target["clobTokenIds"][0]  # YES token
print(f"Market: {target['question']}")
print(f"Current price: {target['outcomePrices']}")

# 3. Check the order book
book = client.get_order_book(token_id)
best_ask = float(book["asks"][0]["price"]) if book["asks"] else None
print(f"Best ask: {best_ask}")

# 4. Place a limit order below the current ask
if best_ask and best_ask > 0.05:
    my_price = round(best_ask - 0.02, 2)
    order = OrderArgs(
        token_id=token_id,
        price=my_price,
        size=10.0,
        side=BUY
    )
    signed = client.create_order(order)
    result = client.post_order(signed, OrderType.GTC)
    print(f"Order placed: {result}")

Common Patterns

Pattern 1: Price Monitoring Bot

Poll prices at an interval and trigger actions on threshold changes.

import time

def monitor_market(token_id, threshold_low, threshold_high):
    while True:
        mid = client.get_midpoint(token_id)
        price = float(mid)

        if price < threshold_low:
            print(f"ALERT: Price dropped to {price} — buying opportunity")
            # Place buy order logic here

        if price > threshold_high:
            print(f"ALERT: Price rose to {price} — selling opportunity")
            # Place sell order logic here

        time.sleep(5)  # Check every 5 seconds

Pattern 2: Cross-Market Arbitrage Scanner

Check if YES + NO prices deviate from $1.00 across markets.

def scan_arb_opportunities():
    markets = requests.get(
        "https://gamma-api.polymarket.com/markets",
        params={"closed": False, "limit": 100}
    ).json()

    for market in markets:
        prices = market.get("outcomePrices")
        if prices:
            try:
                parsed = [float(p) for p in prices.strip("[]").split(",")]
                total = sum(parsed)
                if total < 0.98 or total > 1.02:
                    print(f"ARB: {market['question']}")
                    print(f"  Prices sum to: {total:.4f}")
                    print(f"  Gap: {abs(1.0 - total):.4f}")
            except (ValueError, IndexError):
                continue

For agents scanning beyond prediction markets, the same arbitrage logic applies to sportsbook lines. Offshore sportsbooks like BetOnline and Bovada frequently price sports events differently from Polymarket — and the API access patterns differ significantly. See the Sports Betting Arbitrage Bot Guide for a full cross-platform implementation, and Offshore vs. Regulated Sportsbooks for a comparison of data access across both worlds.

Pattern 3: WebSocket + LLM Agent

Stream market updates and feed them to an LLM for analysis.

import asyncio
import websockets
import json

async def agent_loop():
    uri = "wss://ws-subscriptions-clob.polymarket.com/ws/"

    async with websockets.connect(uri) as ws:
        # Subscribe to multiple markets
        for token_id in watched_tokens:
            await ws.send(json.dumps({
                "type": "subscribe",
                "channel": "market",
                "assets_id": token_id
            }))

        async for message in ws:
            data = json.loads(message)
            # Feed price update to your LLM/agent for analysis
            # agent.analyze(data)
            # If the agent recommends a trade, execute via CLOB API

Security Considerations

Building autonomous agents that handle real money demands careful security practices. See our Security Best Practices for Agent Betting guide for the full treatment. Key points:

  • Never hardcode private keys — use environment variables or a secrets manager
  • Use spending limits — configure your wallet with maximum position sizes
  • Implement kill switches — your bot should have a way to cancel all orders and stop trading instantly
  • Guard against prompt injection — if using LLMs, sanitize all market data before feeding it to the model
  • Test on small amounts first — Polymarket has no demo/testnet environment, so start with minimal positions
  • Monitor the heartbeat — the Rust SDK supports automatic heartbeats that cancel all orders if your client disconnects

Troubleshooting

For an in-depth breakdown of the 10 most common integration failures — including L1/L2 auth confusion, wallet type mismatches, identifier confusion, heartbeat timeouts, and US NO-side pricing — see the top 10 Polymarket API problems guide.

Order Rejected Errors

The CLOB matching engine rejects orders that violate market constraints. Common causes:

  • Check that your price aligns with the market’s tick size (usually 0.01, but some markets use 0.001 — check the /book response for the min_tick_size field)
  • Ensure you have sufficient USDC balance on Polygon
  • For EOA wallets, verify token allowances are set for both the Exchange and Neg Risk Exchange contracts
  • If using postOnly, your order may be crossing the spread — lower your bid or raise your ask
  • Orders with size below the market minimum (typically 1.0 shares) are silently rejected

Authentication Failures (401 / INVALID_SIGNATURE)

Auth errors almost always come down to a mismatch between your wallet type and the signature parameters you’re sending.

  • Verify your signature_type matches your wallet type: 0 for EOA, 1 for email/Magic proxy, 2 for browser/Gnosis Safe wallet (most common)
  • Ensure the funder address is correct for proxy wallets — this is the address holding funds, not the signing key address
  • API credentials may need to be re-derived if you get persistent 401 errors — call client.create_or_derive_api_creds() again
  • Check for clock drift — EIP-712 signatures include a timestamp, and a drift of more than 60 seconds causes rejection
  • See the full Polymarket Auth Troubleshooting Guide for error code reference

Rate Limit Errors (429)

When you hit rate limits, Polymarket’s Cloudflare layer queues your requests rather than immediately rejecting them — but sustained overuse will eventually return 429s.

  • Switch from polling to WebSockets for real-time data — WebSocket connections do not count against REST rate limits
  • Batch order operations using the POST /orders endpoint (up to 15 orders per call)
  • Cache Gamma API responses — market metadata (questions, token IDs, slugs) changes infrequently
  • Check X-RateLimit-Remaining headers before making bursts of requests
  • See the Polymarket Rate Limits Guide for the full per-endpoint breakdown

Missing or Wrong Token IDs

Token ID confusion is the single most common issue for new Polymarket developers.

  • Token IDs come from the Gamma API, not the CLOB API — use the clobTokenIds field from GET /markets
  • clobTokenIds is an array: index 0 is typically YES, index 1 is NO, but always verify against outcomes
  • Some multi-outcome (neg-risk) markets have their token IDs in a nested structure within the event response
  • Token IDs are long numeric strings (not hex addresses) — e.g., 21742633143463906290569050155826241533067272736897614950488156847949938836455
  • If a token ID returns empty order books, the market may have been resolved or delisted — check closed status via Gamma

What’s New in Official Docs

Polymarket’s official documentation has expanded significantly. Key areas now covered:

  • GET /markets default changed (April 2026) — The closed query parameter now defaults to false, so most market discovery calls no longer need to pass it explicitly
  • Programmatic fee schedules (March 2026) — Markets now expose a feeSchedule object for reading fees directly, replacing the legacy per-field fee layout
  • Liquidity reward floor (March 2026) — Orders must sit on the book for at least 3.5 seconds to count toward liquidity rewards
  • Multi-chain withdrawals (January 2026)POST /withdraw now supports Solana and Bitcoin destinations in addition to EVM chains
  • Fees and maker rebates — Per-market fee tiers, taker fees on crypto/sports markets, and per-market rebate calculation
  • CTF operations — Split, merge, and redeem operations for conditional tokens
  • Bridge flows — Deposit and withdrawal documentation via the Bridge API, including the new /withdraw endpoint for cross-chain bridging
  • Resolution process — How markets resolve via the UMA oracle
  • Market Maker program — Setup, trading requirements, liquidity rewards, and data feeds
  • Builder Program — Tiers, profile setup, API keys, order attribution, and relayer client
  • Order book metadataGET /book and POST /books now return market metadata fields (question, slug, end date, etc.), reducing the need for separate Gamma API calls
  • Subscription limits removed — The 100-token subscription limit on the Markets WebSocket channel has been removed; you can subscribe to unlimited token IDs
  • Agent Skills — Polymarket published agent-skills on GitHub, a structured skill pack for AI agents covering auth, orders, WebSocket, CTF, bridge, and gasless transactions

AgentBets guides complement these official docs by providing implementation depth, cross-platform context, and agent-first patterns.


Changelog

DateChange
April 9, 2026Verified against official Polymarket docs. Noted GET /markets now defaults to closed=false (as of today). Documented feeSchedule object (Mar 31), 3.5s liquidity-reward floor (Mar 17), multi-chain Bridge /withdraw for Solana/Bitcoin (Jan 28), and HeartBeats API (Jan 6).
March 23, 2026Updated fee structure with per-market taker fees (crypto, sports). Added /withdraw endpoint. Expanded Builder Program with Relayer Client docs. Added agent-skills repo. Added polymarket-apis community package. Updated signature_type=2 as default. Documented get-book metadata enrichment.
March 2026Updated rate limits table with concrete per-endpoint figures. Added Rust SDK setup and examples. Added WebSocket sports channel and RTDS documentation. Expanded neg-risk market guidance.
February 2026Initial publication. Covers CLOB, Gamma, Data, and Bridge APIs. Python and TypeScript SDK examples. Authentication setup for all wallet types.

Official Resources

AgentBets Guides


Frequently Asked Questions

How do I check my balance with py_clob_client?

Use client.get_balance() to get your USDC balance, or client.get_balance_allowance() for balance plus token allowance info. See our complete method reference for full examples with response structures.

What are Polymarket’s API rate limits?

Polymarket publishes concrete rate limits enforced via Cloudflare throttling. The general limit is 15,000 requests per 10 seconds, with specific limits per endpoint (e.g., POST /order allows 3,500/10s burst and 36,000/10min sustained). See our Rate Limits Guide for the full per-endpoint table and retry code.

How do I get my positions using py_clob_client?

Call client.get_positions() to retrieve all open positions. You can also query the Data API at data-api.polymarket.com/positions or use the Polymarket CLI with polymarket data positions. See the get_positions() reference for full parameter docs.

What is MarketOrderArgs in py_clob_client?

MarketOrderArgs is the data class used to specify market orders (fill-or-kill). It takes token_id, amount (in USDC), side, and order_type. Use it with client.create_market_order(). See the MarketOrderArgs reference for the full field reference and examples.


Where This Fits in the Agent Betting Stack

This guide covers Layer 3 (Trading) of the Agent Betting Stack. Polymarket’s API is the execution layer — where your agent converts intelligence into positions.

To build a complete autonomous agent, you also need:


This guide is maintained by AgentBets.ai. Found an error or API change we missed? Let us know on Twitter.

Not financial advice. Built for builders.