api
Polymarket API with Python (2026): Markets, Order Books, WebSockets and Orders with the Unified SDK
A current, tested Python tutorial for the Polymarket API: the three API hosts, the unified polymarket-client SDK, reading markets and order books, streaming prices, placing orders safely, and what changed from py-clob-client.
Many Polymarket API tutorials that still rank in search were written for client libraries Polymarket has since replaced. This one uses the unified Python SDK Polymarket documents today, polymarket-client on PyPI and imported as polymarket. Version 0.12.0 was released on October 1, 2026 (PyPI, source, docs). We ran every public-data snippet below against the live API on October 8, 2026. The trading snippets follow the official docs, but we didn't execute them with real funds.
If you're deciding whether to build a bot at all, start with our Polymarket trading bot guide.
The API in one minute
Polymarket's API spans several hosts, each with its own job (API overview):
| Host | Purpose | Auth |
|---|---|---|
https://gamma-api.polymarket.com |
Discover events and markets, metadata, tags | None |
https://clob.polymarket.com |
Prices, order books, placing and cancelling orders | Public reads: none. Trading: wallet signature + API credentials |
https://data-api.polymarket.com |
Positions, trades, activity, leaderboards | None for public wallet data |
wss://ws-subscriptions-clob.polymarket.com/ws/market |
Live order books, price changes, trades, tick-size changes | None |
wss://ws-subscriptions-clob.polymarket.com/ws/user |
Your own order and trade updates | API credentials |
Two concepts you'll meet immediately:
- Event vs market. An event is the question users see ("Who will win…?"). Each market inside it is a binary YES/NO condition. Mutually exclusive markets are linked as a negative-risk group (Markets & Events).
- Asset ID. You trade an outcome by its identifier. Older (CTF) markets use a token ID; Polymarket Protocol V2 markets use a position ID. The unified SDK calls both
asset_id, and you pick the right one based onmarket.version(Place Your First Order).
Install
python -m venv .venv && source .venv/bin/activate
pip install polymarket-client # or: uv add polymarket-client
pip install "polymarket-client[pandas]" # optional DataFrame export
The SDK has async clients (AsyncPublicClient, AsyncSecureClient) for bots and services, and sync ones (PublicClient, SecureClient) for scripts and notebooks. Real-time subscriptions are async-only.
Step 1: Discover markets (no auth)
import asyncio
from polymarket import AsyncPublicClient
async def main() -> None:
async with AsyncPublicClient() as client:
page = await client.list_markets(closed=False, page_size=5).first_page()
for m in page.items:
print(m.id, m.slug, m.question)
asyncio.run(main())
You can also fetch a single market or event by slug, which is the last part of a polymarket.com URL:
market = await client.get_market(slug="xi-jinping-out-before-2027")
event = await client.get_event(slug="will-the-us-confirm-that-aliens-exist-before-2027")
list_events(tag_ids=[...], closed=False) filters by category tag, and every list method returns a paginator you can iterate (async for page in pages) or resume from page.next_cursor. For research, await pages.to_pandas(limit=500) turns results into a DataFrame (with the pandas extra installed).
Step 2: Read order books and prices
def asset_ids(market):
yes, no = market.outcomes.yes, market.outcomes.no
if market.version == "v2":
return yes.position_id, no.position_id
return yes.token_id, no.token_id
def best_prices(book):
# Don't assume list order: take the max bid and the min ask explicitly.
best_bid = max((lvl.price for lvl in book.bids), default=None)
best_ask = min((lvl.price for lvl in book.asks), default=None)
return best_bid, best_ask
yes_id, no_id = asset_ids(market)
book = await client.get_order_book(asset_id=yes_id)
print(best_prices(book), book.tick_size, book.min_order_size, book.neg_risk)
print(await client.get_midpoint(asset_id=yes_id))
Two practical notes from testing:
- Don't assume
bids[0]is the best bid. In our test the first level returned was the worst price on the book. Always take the max bid and min ask, as above. - Respect
tick_sizeandmin_order_size. Orders off the tick grid or below the minimum are rejected. Tick size can change as a price approaches the extremes. Polymarket's agent-skills notes say this happens above 96¢ and below 4¢, and that bots using a stale tick size get rejected (websocket notes).
Other useful public reads: get_price(asset_id, side), get_prices([...]), get_spread, get_last_trade_price, list_price_history(asset_id=..., interval="1d") for historical candles, and get_order_books(asset_ids=[yes_id, no_id]) to fetch both sides in one call. That last one is handy for checking whether YES + NO asks add up to less than $1 (see Polymarket arbitrage explained).
Step 3: Stream live data over WebSockets
Polling REST endpoints is fine for research and too slow for most bots. Subscribe instead:
from polymarket.streams import MarketSpec
async with await client.subscribe(MarketSpec(asset_ids=[yes_id])) as stream:
async for event in stream:
if event.type == "book":
print("snapshot", best_prices(event.payload))
elif event.type == "price_change":
for ch in event.payload.price_changes:
print("change", ch.side, ch.price, ch.size, ch.best_bid, ch.best_ask)
elif event.type == "tick_size_change":
print("tick size now", event.payload.new_tick_size)
Pass MarketSpec(asset_ids=[...], custom_feature_enabled=True) to also receive best_bid_ask, new_market and market_resolved events (Real-Time Data). When you use the raw WebSocket instead of the SDK, send a PING every 10 seconds (market channel).
Reconnects are your job. Polymarket's docs say plainly that real-time updates don't replay changes missed during a disconnection. After reconnecting, re-fetch open orders and recent trades, then resume applying stream events (user channel). Treat stale data as "do not trade".
Step 4: Look up any wallet's trades and positions
Every wallet is public, which is what makes copy trading and whale tracking possible:
board = await client.list_trader_leaderboard(window="month", sort_by="PNL", page_size=10).first_page()
wallet = board.items[0].wallet
trades = await client.list_trades(user=wallet, page_size=20).first_page()
for t in trades.items:
print(t.timestamp, t.side, t.price, t.size, t.title)
positions = await client.list_positions(user=wallet, page_size=20).first_page()
stats = await client.get_user_stats(user=wallet) # market count, join date, P&L
A caution we ran into while testing: when we pulled the monthly P&L leaderboard on October 8, 2026, the top wallet had joined about a month earlier and had traded only eight markets. A leaderboard rank says very little about skill. See how to vet a Polymarket wallet.
Step 5: Authenticate and place orders
Trading needs a secure client built from a private key. Polymarket supports several wallet types (EOA, proxy, Safe, deposit wallet), and the SDK exposes them as wallet_type (Wallets and Authentication). For an existing Polymarket account, the quickstart passes the signer key and the account's wallet address:
import os
from polymarket import AsyncSecureClient
client = await AsyncSecureClient.create(
private_key=os.environ["POLYMARKET_PRIVATE_KEY"], # never hard-code, never commit
wallet=os.environ["POLYMARKET_WALLET_ADDRESS"],
)
Under the hood there are two layers of auth: an EIP-712 wallet signature that creates or derives API credentials, then HMAC-signed requests using those credentials. Each order is also signed by the wallet (API auth). The SDK handles all of this.
A resting limit order (a maker order if it doesn't cross the spread, and makers pay no Polymarket fee):
resp = await client.place_limit_order(
asset_id=yes_id,
side="BUY",
price="0.52",
size="10", # shares
post_only=True, # reject instead of crossing the spread
)
if resp.ok:
print(resp.order_id, resp.status)
else:
print(resp.code, resp.message)
A marketable order with a price cap. Estimate first, then cap what you'll pay:
est = await client.estimate_market_price(asset_id=yes_id, side="BUY", amount="10", order_type="FAK")
resp = await client.place_market_order(
asset_id=yes_id, side="BUY", amount="10", max_price=est, order_type="FAK"
)
Order types: GTC (rests until filled or cancelled), GTD (rests until an expiry), FOK (fill completely now or cancel), FAK (fill what you can now, cancel the rest) (Place Orders). Cancel with cancel_order(order_id=...), cancel_orders, cancel_market_orders or cancel_all. After a fill, wait_for_order_fill_settlement(resp) waits for on-chain settlement before you read positions.
Redeeming after resolution: redeem_positions(condition_id=...). Merging a YES + NO pair back into collateral: merge_positions(...).
Fees, delays and restarts your code must handle
- Taker fees apply at match time and depend on the category:
shares × feeRate × p × (1 − p), with rates from 0.04 to 0.07 and geopolitics free. You don't put the fee in the order (fees). Build it into every edge calculation. See Polymarket fees explained. - Taker delay. Selected crypto and finance up/down markets hold marketable orders for 250 ms and re-validate them. During the hold the order is pending and can't be cancelled. You can check a market via
GET https://clob.polymarket.com/clob-markets/{condition_id}(order lifecycle). - Matching-engine restarts. Expect temporary rejections, then two minutes of post-only mode. Honour any server-provided delay, otherwise use exponential backoff starting at 1–2 seconds (matching engine). Polymarket announces restarts on its Telegram channel and Discord.
- Rate limits. The SDK raises
RateLimitError, andcreate()accepts anon_rate_limit_updatecallback. Back off instead of hammering. - Geoblock. Orders from restricted regions are rejected. Call
GET https://polymarket.com/api/geoblockbefore trading and stop ifblockedis true (geoblock). Don't route around it.
Migrating from py-clob-client
If your code imports py_clob_client or py_clob_client_v2 (ClobClient, OrderArgs, create_or_derive_api_creds), Polymarket publishes a mapping guide to the unified SDK (migration guide). The big changes: one client instead of separate CLOB, relayer and builder-signing packages; asset_id instead of token-only IDs; typed Decimal models; and built-in pagination and streams. py-clob-client-v2 is still on PyPI (1.2.0, September 2026). The legacy py-clob-client was last released in February 2026, and AgentBets reports it stopped working against production after the April 2026 CLOB V2 cutover (AgentBets).
Security basics for API keys
- Use a dedicated trading wallet holding only what the bot needs.
- Load keys from environment variables or a secrets manager,
chmod 600any env file, and never commit it. - Pin dependencies and install only from
github.com/Polymarketand the official PyPI names. In December 2025 a fake GitHub copy-trading bot stole keys through a malicious dependency (KuCoin). More in Polymarket bot security.
FAQ
Is the Polymarket API free? Yes. Public data endpoints need no key, and trading credentials are derived from your wallet. You pay trading fees, not API fees.
Do I need an API key to read prices? No. Gamma, the public CLOB endpoints, the Data API and the market WebSocket are open.
Can I use the API from the US? Not to trade on the international exchange. Order placement is geoblocked and the US is close-only (geoblock). Polymarket US is a separate CFTC-regulated venue with its own onboarding. See Is Polymarket legal?
Is there a testnet? The older v2 Python client documents Polygon's Amoy testnet (chain ID 80002) (py-clob-client-v2). Many builders simply paper-trade against live data instead.
Don't want to maintain all this yourself? Ghost Trader packages the plumbing (streams, reconnects, fee-aware edge thresholds, fractional-Kelly sizing, a daily-loss kill switch and Telegram alerts) into a licensed runner you run with your own wallet, plus a hosted dashboard and Telegram alerts. $199/month with paper mode included. See pricing.
Not financial advice. Trading can lose money. Ghost Trader isn't available where Polymarket restricts trading, including the US, and is not affiliated with Polymarket.
This article is general information, not financial advice. Prediction markets are risky, and Polymarket isn’t available everywhere.