Polymarket API Python Tutorial: Prices, Books, and History (2026)

The Polymarket API and Python are a natural fit: every read endpoint is free, keyless, and returns clean JSON, which means you can go from empty script to live market data in about five minutes. This tutorial does exactly that, step by step: discover markets, fetch live prices and order books, pull price history, and stream real-time updates, all with plain Python and no account. It also settles the Polymarket SDK question, which is where most of the confusion lives, because the Python client has been replaced twice and only one of the packages you will find on PyPI can still trade.
Every URL, parameter and field name below was checked against Polymarket's own documentation and a live response in August 2026, after the CLOB V2 cutover on 28 April 2026. CLOB is the central limit order book, Polymarket's order-matching engine, and the reason the old Python package was named py-clob-client. That cutover date matters more here than in most tutorials: V2 shipped with no backward compatibility, so a guide written before it describes an exchange that no longer exists. Endpoints, limits and SDK versions still move, so confirm anything you build on against Polymarket's docs first.
Key Takeaways
- All reads are free with no API key: market discovery on the Gamma API, prices and books on the CLOB API, and the streaming WebSocket market channel.
- One mapping causes most early bugs: markets are found by slug or condition ID, but prices are fetched by each outcome's token ID, which you get from Gamma's
clobTokenIdsfield. That field arrives as a JSON-encoded string, so it needsjson.loads()before you can index it. - The Polymarket SDK question has one answer for new projects:
polymarket-client, installed under that name but imported aspolymarket.py-clob-client-v2is superseded, and the originalpy-clob-clientis archived.
Polymarket API Python Setup: One Install, No Keys
pip install requests websocket-client
That's the entire setup for reading data. No account, no API key, no wallet. Three base URLs carry everything below:
https://gamma-api.polymarket.comserves market discovery, metadata and token IDs.https://clob.polymarket.comserves prices, order books and price history.wss://ws-subscriptions-clob.polymarket.com/ws/marketstreams live updates, with no auth.
Step 1: Find Markets and Get Token IDs
Start at the Gamma API. This pulls the currently active events with their markets:
import requests, json
events = requests.get(
"https://gamma-api.polymarket.com/events",
params={"active": "true", "closed": "false", "limit": 5},
timeout=10,
).json()
for event in events:
print(event["title"])
for market in event.get("markets", []):
token_ids = json.loads(market["clobTokenIds"])
outcomes = json.loads(market["outcomes"])
print(" ", market["question"])
for name, token_id in zip(outcomes, token_ids):
print(f" {name:>4}: {token_id}")
The field that matters most is clobTokenIds, one long decimal token ID per outcome. Every price, book, and streaming call below takes those token IDs. Condition IDs identify the market; token IDs identify each outcome, and confusing the two is the classic first-hour bug. The symptom is an empty response or a 404 reading No orderbook exists for the requested token id.
Two details in that snippet are load-bearing. clobTokenIds and outcomes arrive as JSON-encoded strings, not arrays, so indexing them without json.loads() hands you a single character, and that character is a square bracket. Meanwhile bestBid and bestAsk on the same object are plain numbers, so no blanket rule about Gamma's types will save you. The second detail is the filters: they are passed as the strings "true" and "false", because requests serialises a Python False to the literal False, capital F, which no API is obliged to read as a boolean.
Step 2: Live Price and Order Book
TOKEN_ID = token_ids[0] # from Step 1; pin it by name once you know which is which
price = requests.get(
"https://clob.polymarket.com/price",
params={"token_id": TOKEN_ID, "side": "BUY"},
timeout=10,
).json()
print("best ask:", float(price["price"]))
book = requests.get(
"https://clob.polymarket.com/book",
params={"token_id": TOKEN_ID},
timeout=10,
).json()
print("top bids:", book["bids"][:3])
print("top asks:", book["asks"][:3])
The price endpoint returns the best price for the side you ask about, and side takes exactly BUY or SELL in capitals. The two answer different questions: what this outcome costs to buy right now, versus what selling it would pay you. Wrap the result in float(), because the value comes back quoted, as {"price": "0.62"}. Comparing that string against a number raises, and sorting by it gives you alphabetical order.
Since prices are probabilities, 0.62 literally means the market says 62 percent. Ask for both outcomes on the BUY side and they will sum to a little over a dollar rather than exactly one; that gap is the spread, not a bug. The book endpoint returns full depth, which you need before trading any size: a great price with three dollars behind it is not a great price.
Step 3: Price History
history = requests.get(
"https://clob.polymarket.com/prices-history",
params={"market": TOKEN_ID, "interval": "1w", "fidelity": 60},
timeout=10,
).json()
for point in history["history"][-5:]:
print(point["t"], point["p"])
This one call hides two naming traps. The parameter is called market, but it wants the CLOB token ID, the same long number you just used for /price; pass an actual condition ID and the call errors. The allowed intervals are 1h, 6h, 1d, 1w, 1m and max, where 1m means one month, not one minute. For an absolute window instead, pass startTs and endTs in unix seconds.
fidelity is the candle resolution in minutes and defaults to 1, so a week at the default is a lot of points you will never read. Each entry comes back as {"t": ..., "p": ...}, oldest first, where t is unix seconds and p is a bare float, which is worth noting after /price handed you the same concept as a string.
One gotcha worth designing around: once a market resolves, its history drops to coarse 12-hour granularity. If you are building a research dataset, capture fine-grained history while the markets are still live, because it is gone once they close. Check the current behaviour on Polymarket's prices and order books reference before you plan a pipeline around it.
Step 4: Stream Live Updates
Polling is fine for dashboards; anything reacting to price changes wants the WebSocket, which is public and needs no key:
import websocket, json, threading
def keep_alive(ws):
ws.send("PING")
threading.Timer(10, keep_alive, [ws]).start()
def on_open(ws):
ws.send(json.dumps({
"type": "market",
"assets_ids": [TOKEN_ID],
"custom_feature_enabled": True,
}))
keep_alive(ws)
def on_message(ws, message):
if message == "PONG":
return
for update in json.loads(message):
print(update.get("event_type"), update)
ws = websocket.WebSocketApp(
"wss://ws-subscriptions-clob.polymarket.com/ws/market",
on_open=on_open, on_message=on_message,
)
ws.run_forever()
You'll receive book, price_change, last_trade_price and tick_size_change events as they happen, plus richer ones like best_bid_ask with the feature flag on, which was the documented behaviour at the time of writing. Two optional fields are worth knowing: initial_dump sends a snapshot of the book before live updates begin and is already the default, and level controls detail. Read assets_ids twice, by the way, because it is plural on both words; get it wrong and the connection opens perfectly and then sends you nothing forever.
The keep-alive is not optional: the client sends a literal PING text frame every 10 seconds and the server answers PONG, which is why the message handler skips those replies before parsing JSON. The automatic ping your websocket library sends at the protocol level does not count, so a script without this line dies quietly after a while and looks exactly like a flaky network.
If you poll instead of streaming, the public read limits are generous but real. Checked on 7 August 2026, the CLOB allowed roughly 1,500 requests per 10 seconds each to /price, /book and /midpoint, while Gamma's market endpoints were tighter at 300 per 10 seconds. Those are IP-based and enforced at the edge, so exceeding them usually queues your requests. Numbers like these move, so check Polymarket's rate limits page before you tune a loop.
Which Polymarket SDK to Install
Nothing above needed an SDK, and that is deliberate: the read endpoints are plain HTTP and JSON, so a client library would hide the parts you are trying to learn. That is a teaching choice, and it should not leave you with the wrong impression: a wrapper for these reads does exist. It ships in the official SDK, and there is a working example of it further down this section.
Orders are the harder case. The moment you want to place one you need a Polymarket SDK, and this is where a search goes wrong, because the Python client has been replaced twice and the retired packages still rank.
| Package | Language | Status |
|---|---|---|
polymarket-client | Python | Current unified SDK, use for new projects |
py-clob-client-v2 | Python | Superseded, existing integrations only |
py-clob-client | Python | Archived, pre-V2, cannot trade |
@polymarket/client | TypeScript | Current unified SDK |
polymarket-us | Python and TypeScript | Separate US exchange only |
So: pip install polymarket-client, then import polymarket. The install name and the import name differ, which trips people up before their first line of real work. It wants Python 3.11 or newer and sat at 0.5.0 on 9 August 2026, still labelled beta, so check PyPI for a newer release rather than pinning the version in this sentence.
Here is the same kind of read you hand-rolled in Steps 1 and 2, done through the client. The package README gives this as its quickstart:
from polymarket import Market, PublicClient
with PublicClient() as client:
market: Market = client.get_market(
url="https://polymarket.com/event/example-market"
)
Two things to notice. PublicClient needs no key, exactly like the raw calls above, so a wrapper is available for reads even though this tutorial does them by hand. And get_market accepts the market's Polymarket URL, which saves you the slug-to-condition-ID-to-token-ID walk that Step 1 spells out. You get back a typed Market model instead of a raw dict.
There is an AsyncPublicClient with the same method under await if you are streaming or fanning out across many markets. Authenticated work, order placement and wallet operations live in the same package behind an authenticated client, and the exact constructor and order signatures are the part most likely to shift between betas, so take those from the official docs at the version you install rather than from any tutorial, including this one.
py-clob-client deserves its own note, because it is what nearly every pre-2026 tutorial imports and it is still the top result for plenty of searches. Its repository is archived and its README points you elsewhere. It targets the V1 exchange that CLOB V2 replaced on 28 April 2026, so it cannot trade against production no matter how carefully you follow an old guide. If you have inherited code calling client.set_api_creds(client.create_or_derive_api_creds()), that line is obsolete too: the current clients derive credentials during construction.
Trading from Python: What You Need First
Placing an order needs three things this tutorial never touched: a Polygon wallet, pUSD collateral, and EIP-712 signing. pUSD is Polymarket's dollar-pegged ERC-20, backed 1:1 by USDC, and it replaced USDC.e at the V2 cutover, so a balance check written against USDC reports zero and an otherwise correct script concludes it is unfunded. Auth arrives in two layers, a one-time wallet signature that derives your API credentials and a per-request signature computed from them, and the SDK implements both so you will not be writing either by hand.
Two costs are easy to miss, and neither appears anywhere in your code. Gas is the first: the gasless relayer that makes the Polymarket app feel free covers the standard app flow, not an API trader signing from their own wallet. Fees are the second. You are the taker when your order crosses the spread and fills against an order already resting on the book, and the maker when you post the order that someone else hits; taker fees rolled out across most categories during early 2026 while makers trade free, and the fee is applied at match time rather than fixed when you sign. Rates differ by category and change, so pull the current figures from our Polymarket fees breakdown instead of hardcoding them.
There is also no sandbox. Polymarket runs no fake-money mirror of the exchange, so testing means real pUSD on Polygon mainnet, which is why a dry-run flag and a position cap are not polish you add later. And whether you may trade at all depends on where you live: the restricted-jurisdiction list passed 30 countries during 2026, including the UK and Singapore, with several Canadian provinces treated as close-only (see Polymarket's geoblock reference). US residents are restricted on the global platform under a 2022 CFTC settlement. Polymarket US is a separate CFTC-regulated exchange with an entirely separate API, not a configuration flag on the code above. None of this is financial, tax or legal advice.
From Tutorial to Trading Bot
If you came here for a Polymarket API trading bot guide, Steps 1 to 4 are most of the data layer already: the reads, the stream that replaces polling, and the SDK to place the order. The plumbing is an afternoon. The rule in the middle is yours to write.
The work that turns a script into a bot sits around the loop: retry handling for 429s and for the 503 the exchange returns in cancel-only mode, when it accepts cancellations but no new orders; a cap on total exposure rather than per order; sizing against book depth; and a kill switch you have tested. Our Polymarket trading bot guide builds that line by line, and the complete Polymarket API guide covers the authentication model and order types in full.
Frequently Asked Questions
Do I need an API key to use the Polymarket API with Python?
Not for market data: discovery, prices, order books, history, and the streaming market channel are all public. Keys and wallet signatures only enter the picture when you place orders or query your own account.
How do I get a market's token ID?
From the Gamma API: each market object carries a clobTokenIds field with one long decimal token ID per outcome. It arrives as a JSON-encoded string rather than an array, so parse it with json.loads() before indexing, then use those token IDs for every CLOB price, book, and WebSocket call.
Which Polymarket SDK should I use in 2026?
For new projects, polymarket-client, the unified Python SDK. Install it under that name but import it as polymarket, because the two differ. It requires Python 3.11 or newer and was at 0.5.0 on 9 August 2026. TypeScript has the equivalent @polymarket/client.
Is py-clob-client still usable?
No. The original py-clob-client sits in an archived repository and targets the V1 exchange that CLOB V2 replaced on 28 April 2026, so it cannot trade against production. The interim py-clob-client-v2 still works but is superseded, so use it only to maintain an existing integration.
Can I build a Polymarket trading bot with this?
Yes. Steps 1 to 4 give you the data layer and the official SDK places the orders, so what you still have to write is the decision rule and the safety rails around it. Budget most of your time for the rails, because they are what separates a bot you can leave running from a script you have to watch. None of this is financial advice.
Conclusion
Five minutes of Python gets you live probabilities, order books, history, and a real-time stream, free and without an account. The traps are few and cheap to learn: parse the stringified fields, keep condition IDs and token IDs in separate variables, remember that 1m means a month, and send the PING. When you move from reading to trading, install polymarket-client and nothing older. From here the paths fork. Deeper into execution with our Polymarket API guide, or toward strategy with our arbitrage guide, which works through what to do when two venues price the same event differently. Running Kalshi alongside Polymarket is the usual second integration, and the Kalshi Python tutorial mirrors this one.