All posts
GuidesPythonSDKPolymarket API

The Polymarket Python SDK in 2026 (and Migrating off py-clob-client)

If your code still imports ClobClient from py_clob_client, it is built on a superseded package — and so is most of the Polymarket code an AI assistant will write for you. Here is the current SDK and how to move.

10 min read

This matters more than a normal version bump for one reason: almost every Polymarket code sample on the internet predates it. Blog posts, Stack Overflow answers, GitHub examples, and — because they were trained on that material — the code your AI assistant writes when you ask it for a Polymarket bot. If you have found yourself debugging imports that do not exist, this is why.

What actually changed

The old approach gave you one client per Polymarket service. You imported a CLOB client for the order book, called Gamma over raw HTTP for market discovery, hit the Data API separately for positions, and pulled in a builder-signing package if you needed relayer transactions. Four mental models, four error shapes, four pagination styles.

The unified SDK collapses that into one client covering every surface.

Previous clientsUnified SDK
PackagesSeveral, one per serviceOne
Polymarket surfacesService-specific APIsOne client interface
Data modelsRaw JSON dictsTyped models
PaginationVaried by APIOne consistent paginator
Auth and signingImplement per clientSDK helpers
RealtimeConnect to each WebSocketsubscribe() merges feeds

Install

pip install polymarket-client
# uv add polymarket-client
# poetry add polymarket-client

Four clients — pick deliberately

ClientPublic dataTradingRealtimeUse for
AsyncPublicClientYesNoYesBots, data collectors
AsyncSecureClientYesYesYesTrading bots
PublicClientYesNoNoScripts, notebooks
SecureClientYesYesNoScripts that trade
import asyncio
from polymarket import AsyncPublicClient


async def main() -> None:
    async with AsyncPublicClient() as client:
        pages = client.list_markets(closed=False)
        first = await pages.first_page()
        for market in first.items:
            print(market.slug)


asyncio.run(main())

Public data needs no credentials, so build and test everything you can against AsyncPublicClient before introducing a key. Discovery, parsing, pagination, signal generation and backoff can all be exercised with nothing at risk.

Pagination

One paginator interface across every list method. Three ways to consume it, depending on whether you care about page boundaries:

pages = client.list_markets(closed=False, page_size=10)

# 1. Page at a time — use this when you need to control pacing
#    against the tight /markets rate limit (300 req / 10s).
async for page in pages:
    for market in page.items:
        ...

# 2. Item at a time, boundaries ignored.
async for market in pages.iter_items():
    ...

# 3. Manual, resumable.
page = await pages.first_page()
if page.next_cursor:
    nxt = await pages.from_cursor(page.next_cursor).first_page()

Typed models

Methods return real types rather than dictionaries, exported from the top-level package:

from polymarket import Event, Market, OrderBook, PriceHistoryPoint

This is the least glamorous improvement and probably the highest-value one. Under the old clients, a renamed or missing field surfaced as a KeyError deep in your strategy loop at runtime — often in production, often while holding a position. Typed models move that failure to where it belongs.

Realtime subscriptions

subscribe() merges multiple feeds into a single typed event stream, which is a genuine improvement over managing several WebSocket connections by hand.

from polymarket.streams import MarketSpec, SportsSpec

async with await client.subscribe(
    [MarketSpec(token_ids=[token_id]), SportsSpec()],
) as stream:
    async for event in stream:
        # MarketBookEvent | MarketPriceChangeEvent
        # | MarketLastTradePriceEvent | MarketTickSizeChangeEvent
        # | MarketBestBidAskEvent | NewMarketEvent
        # | MarketResolvedEvent | SportsEvent
        handle(event)

The SDK handles the heartbeat, framing and reconnects that you would otherwise implement yourself — including the requirement to PING every 10 seconds on a raw connection. Details in the WebSocket guide.

Placing an order

market = await client.get_market(slug="<market-slug>")
token_id = market.outcomes.yes.token_id
assert token_id is not None

response = await client.place_market_order(
    token_id=token_id,
    side="BUY",
    amount="10",     # pUSD to spend, not shares to buy
)

if not response.ok:
    raise RuntimeError(response.message)

# Settlement is on-chain and asynchronous.
hashes = await client.wait_for_order_fill_settlement(response)

# Reconcile rather than assuming.
page = await client.list_positions(market=[market.condition_id]).first_page()
position = next((p for p in page.items if p.token_id == token_id), None)

Migrating from py-clob-client

  1. Take an inventory of every Polymarket call, including raw requests calls to Gamma and the Data API. Those are in scope too — the unified SDK covers them, and folding them in is most of the benefit.
  2. Install polymarket-client, remove the old packages. Keeping both installed to migrate incrementally invites importing the wrong client symbol.
  3. Replace client construction. One AsyncSecureClient.create(...) replaces the old per-service clients and their separate credential handling.
  4. Replace dict access with attribute access. market["condition_id"] becomes market.condition_id. Tedious, and it is where the type checker starts paying for itself.
  5. Rewrite pagination to the paginator interface rather than manual offset or cursor loops.
  6. Replace hand-rolled WebSocket code with subscribe(), and delete your heartbeat and reconnect logic.
  7. Re-run your read-only paths first, against the public client, before letting anything place an order.

Why your AI assistant gets this wrong

Worth addressing directly, since it is how many people arrive here. Ask an LLM for Polymarket Python code and you will very often get py_clob_client imports, ClobClient(...) construction and OrderArgs objects. The model is not malfunctioning — it is reproducing the overwhelming majority of the Polymarket code it ever saw, which predates the unified SDKs.

  • Check the import line first. from polymarket import ... is current; from py_clob_client... is not.
  • Paste the current docs into context. Polymarket publishes machine-readable Markdown at docs.polymarket.com/llms.txt, and every page has a .md variant. Giving the model the real thing beats arguing with it.
  • Distrust confident code that will not run. Generated code frequently mixes old and new APIs into something that has never existed.

The same problem applies to data providers. We publish llms.txt, llms-full.txt and an OpenAPI spec, and allow AI crawlers, precisely so assistants can write correct PolyTest integrations without guessing. Some providers block those crawlers, which is a legitimate choice but means your assistant will improvise their API surface.

What the SDK still cannot do

The unified SDK is a much better interface to Polymarket's APIs. It does not change what those APIs contain — and the thing bot builders most often want is not in them.

There is no historical order book. client.get_market() will return a resolved market's metadata, but its book is gone: /book returns 404 and prices-history returns 200 with an empty array. No SDK method, plan or credential recovers it.

For backtesting you need snapshots recorded while markets ran. PolyTest provides them for crypto Up/Down markets — 90M+ point-in-time snapshots, 8 levels of depth per side, sub-second timestamps, resolved markets retained. It is a plain REST API with an X-API-Key header, so it drops into the same async code alongside the SDK. Start free, or read how to build a bot for the whole arc.

Frequently asked questions

What is the official Polymarket Python SDK in 2026?
The official Python SDK is polymarket-client, installed with pip install polymarket-client and imported as polymarket. It is the unified SDK covering the CLOB, Gamma and Data APIs plus realtime streams through one client interface, and it supersedes py-clob-client. The TypeScript equivalent is @polymarket/client, and a unified Rust SDK is in development.
Is py-clob-client deprecated?
It has been superseded by the unified polymarket-client SDK, alongside the TypeScript packages @polymarket/clob-client-v2, @polymarket/builder-relayer-client and @polymarket/builder-signing-sdk. Polymarket publishes a migration guide for moving existing integrations. Most tutorials and AI-generated code still reference the older packages because they long predate the unified SDKs.
Which Polymarket Python client should I use for a trading bot?
Use AsyncSecureClient. There are four clients: AsyncPublicClient and PublicClient for public data, AsyncSecureClient and SecureClient for trading and account access. Realtime subscriptions are async-only — the sync clients do not implement subscribe() — so any bot that streams book updates needs an async client. Build and test against AsyncPublicClient before introducing credentials.
How do I paginate results with the Polymarket Python SDK?
List methods return a paginator. Iterate it directly for page-at-a-time control, call iter_items() to iterate individual records ignoring page boundaries, or use first_page() and from_cursor() for manual resumable scans. The next_cursor value is opaque, so store it verbatim rather than parsing or constructing one, and omit it to start from the first page.
Why does ChatGPT or Claude generate Polymarket code that does not work?
Because the overwhelming majority of Polymarket code in training data predates the unified SDKs, models tend to produce py_clob_client imports, ClobClient construction and OrderArgs objects. Check the import line first — from polymarket import is current, from py_clob_client is not. Polymarket publishes machine-readable docs at docs.polymarket.com/llms.txt and a .md variant of every page, so pasting current documentation into context is more effective than correcting the model repeatedly.
Can the Polymarket SDK fetch historical order book data?
No. The SDK is an interface to Polymarket's APIs and cannot return data those APIs do not hold. A resolved market's metadata is retrievable, but its order book returns 404 and its price history returns HTTP 200 with an empty array. Backtesting requires point-in-time snapshots recorded by a third party while the markets were live.

Get the historical data Polymarket does not keep

PolyTest records Polymarket crypto Up/Down markets as they run — 90M+ snapshots with 8 levels of order book depth, sub-second timestamps, and resolved markets preserved. Free tier, no card.

Keep reading