Polymarket API in Python: read markets, stream books and place orders
Use Polymarket's API in Python with polymarket-client. Read markets and order books with no key, stream prices, place limit orders and avoid the old clients.
Short answer: Install
polymarket-clientand usePublicClient. Market data needs no key. Trading needsSecureClient, a wallet and funds. Older guides usepy-clob-client, but Polymarket archived it on May 25, 2026, and its README says it no longer works.
We ran the read and stream code below on Nov 5, 2026, with version 0.16.0. We didn't run the order code, because it needs a funded wallet. It follows Polymarket's docs.
The APIs
The SDK wraps these, and you can also call them directly.
| API | Base URL | Used for | Key needed |
|---|---|---|---|
| Gamma | https://gamma-api.polymarket.com | Markets and events | No |
| CLOB | https://clob.polymarket.com | Books, prices and orders | For private requests |
| Data | https://data-api.polymarket.com | Positions and trades | |
| Market WebSocket | wss://ws-subscriptions-clob.polymarket.com/ws/market | Live books | No |
Read a market and its order book
You'll need Python 3.11 or newer, then pip install polymarket-client.
from polymarket import PublicClient
with PublicClient() as client:
page = client.list_markets(closed=False, order="volume24hr", ascending=False, page_size=5).first_page()
market = page.items[0]
print(market.slug, market.question)
# the token for this market's Yes side
yes = market.outcomes.yes
asset_id = yes.position_id if market.version == "v2" else yes.token_id
book = client.get_order_book(asset_id=asset_id)
print("best bid", max(level.price for level in book.bids))
print("best ask", min(level.price for level in book.asks))
print("midpoint", client.get_midpoint(asset_id=asset_id))
print("spread", client.get_spread(asset_id=asset_id))
print("smallest order", book.min_order_size)Use max and min for the best prices. When we ran this, book.bids[0] was the lowest bid on the book, at 0.001, and book.asks[0] was the highest ask.
To look up one market, use its slug or its page address: client.get_market(slug="...") or client.get_market(url="...").
Get price history
points = client.list_price_history(asset_id=asset_id, interval="1d", bucket_seconds=3600).first_page().items
print(points[-1].timestamp, points[-1].price)Prices come back as Decimal, so convert them before doing floating-point math.
Stream the book
Streams are async only. The first event is the whole book, and the later ones are changes.
import asyncio
from polymarket import AsyncPublicClient
from polymarket.streams import MarketSpec
async def main(asset_id: str) -> None:
async with AsyncPublicClient() as client:
async with await client.subscribe([MarketSpec(asset_ids=[asset_id])]) as stream:
async for event in stream:
print(event.topic, event.type)
asyncio.run(main("YOUR_ASSET_ID"))Place and cancel a limit order
Trading needs your wallet's private key and address, and pUSD in the account. Keep the key in an environment variable, and never commit it.
import os
from polymarket import SecureClient
with SecureClient.create(
private_key=os.environ["POLYMARKET_PRIVATE_KEY"],
wallet=os.environ["POLYMARKET_WALLET_ADDRESS"],
) as client:
response = client.place_limit_order(asset_id=asset_id, side="BUY", price="0.52", size="10", post_only=True)
if not response.ok:
raise RuntimeError(response.message)
print(response.order_id)
client.cancel_order(order_id=response.order_id)post_only=True makes the order rest or get rejected, so it can't take liquidity. Makers pay no fee on Polymarket, while takers pay a fee that depends on the category and price.
What goes wrong
| Problem | Why |
|---|---|
order_version_mismatch on market orders | The old client. Move to polymarket-client |
| Order rejected for its price | It isn't on the market's tick size, which can change while a market trades |
| Order rejected for its size | It's below the book's min_order_size |
| Post-only order rejected | It would have matched at once, which is what post-only prevents |
| Expiring order gone early | Good-till-date orders expire a minute before their timestamp, which must be at least 3 minutes ahead |
| Orders refused by location | Polymarket lists the US, UK, Australia and others as close-only, on the API too |
Test your bot first
We found no testnet or sandbox in Polymarket's docs, so a bug there costs real money. You can run your quoting logic on Rizon's API first, which takes one call to get a key and uses practice money:
import requests
API = "https://rizon.markets/api/v1"
key = requests.post(f"{API}/accounts").json()["key"]
headers = {"Authorization": f"Bearer {key}"}
print(requests.get(f"{API}/markets").json()["markets"][0]["id"])
print(requests.post(f"{API}/orders", headers=headers, json={"market": "BTC-15s", "side": "up", "size": 10}).json())It's a different contract. Rizon's book quotes a strike and pays 2×, so you're testing your plumbing and your quoting, not Polymarket's matching. Where to test a trading bot without real money compares the options. If you prefer to work with an AI assistant, see how to build a trading bot with Claude Code.
FAQ
Does the Polymarket API need an API key?
Not for market data. Trading and account data need a wallet signature. SecureClient handles the signing and creates the API credentials for you.
What happened to py-clob-client?
Polymarket archived it on May 25, 2026. py-clob-client-v2 is archived too. The migration guide moves you to polymarket-client.
What are the API's rate limits?
Polymarket publishes them by endpoint, per IP. For example, /book allows 1,500 requests per 10 seconds, and placing orders allows 5,000 per 10 seconds in a burst. Requests over a limit are delayed rather than rejected.
Is there a Polymarket testnet?
We found none in its docs. Polymarket has no demo account either.
Can I use the Polymarket API from the US?
The global platform lists the US as close-only on both the site and the API. Polymarket US is a separate venue, with its own docs.