Polymarket API: A Complete Developer Guide
How Polymarket's API actually works — the Gamma and CLOB surfaces, the identifier chain that trips everyone up, and the schema quirks worth knowing before you write any code.
Polymarket doesn't have one API — it has four
This is the first thing that confuses people, and it's worth getting straight before you write any code.
Polymarket splits its API across several hosts, each with its own base URL and its own data model. The two you'll spend nearly all your time in are Gamma and CLOB.
Gamma, at gamma-api.polymarket.com, is the discovery layer. It answers questions like "what markets exist?", "what's this event about?", and "what's the current rough price?" It's completely public — no API key, no wallet, no signature.
CLOB, at clob.polymarket.com, is the exchange layer. It serves order books, live prices, spreads and historical price series, and it handles order placement. The read endpoints are public too. Only trading requires authentication.
There's also a Data API covering on-chain user positions and trades, a leaderboard surface, and a WebSocket feed for live updates. Most projects don't need those on day one.
The practical takeaway: for a read-only integration — a dashboard, a scanner, a research tool — you need no credentials at all. That's genuinely unusual among prediction market platforms and it's the best thing about building on Polymarket.
Starting with Gamma: finding markets
Gamma is where every integration begins, because it's the only place that tells you what exists.
The main endpoint is /markets, which supports filtering on things like whether a market is active or closed, plus a limit for how many records to return. There's a parallel /events endpoint — an event is a container that groups related markets, so a single election event might hold a dozen individual outcome markets underneath it.
Both also support slug-based lookup, which is handy when you already know the market you want from its Polymarket URL.
# Five markets that are currently open
curl "https://gamma-api.polymarket.com/markets?active=true&closed=false&limit=5"
# Look one up by its slug, straight from the Polymarket URL
curl "https://gamma-api.polymarket.com/markets/slug/your-market-slug"
# Events group related markets together
curl "https://gamma-api.polymarket.com/events?limit=5"The identifier chain, which is where most people get stuck
Polymarket uses three different identifiers and they are not interchangeable. Getting this wrong is the single most common reason a first integration doesn't work.
The slug is the human-readable name from the URL. Use it for lookups and for linking users back to Polymarket. It is not useful for pricing.
The conditionId is the on-chain identifier for the market as a whole — the question being asked. It's what you'd use to join against on-chain data.
The clobTokenIds are what you actually need for prices. Every market has one token per outcome, so a simple Yes/No market has two token IDs. A price is always the price of one specific outcome token, never of the market as a whole.
So the flow is: discover the market on Gamma, pull its clobTokenIds, then take those token IDs to the CLOB API for anything price-related. Almost every "why is the CLOB API returning nothing?" problem comes down to passing a conditionId where a token ID was expected.
The schema quirk nobody warns you about
Gamma returns several fields as JSON strings rather than as native arrays. clobTokenIds, outcomes and outcomePrices all come back as strings that happen to contain JSON.
This catches almost everyone once. Your code reads market.clobTokenIds[0] expecting a token ID, gets back the character [, and fails somewhere much further downstream where the cause isn't obvious.
You have to parse them explicitly. It's a two-line fix once you know, and an hour of confusion if you don't.
const res = await fetch(
"https://gamma-api.polymarket.com/markets?active=true&limit=1"
);
const [market] = await res.json();
// These three arrive as JSON strings, not arrays.
const tokenIds = JSON.parse(market.clobTokenIds); // ["7183...", "4492..."]
const outcomes = JSON.parse(market.outcomes); // ["Yes", "No"]
const prices = JSON.parse(market.outcomePrices); // ["0.62", "0.38"]
// Outcomes and tokens line up by index.
const yesToken = tokenIds[outcomes.indexOf("Yes")];Prices and order books from CLOB
Once you have a token ID, the CLOB API gives you several views of the same market, and which one you want depends on what you're building.
Use /price when you need the best available price on a given side — it takes a side parameter because buying and selling are not the same number. Use /midpoint when you want a single reference probability and don't care about execution; this is usually the right choice for a dashboard or a research tool. Use /book when you need real depth, which matters for anything sizing a position. And /spread tells you how wide the market is, which is a decent liquidity proxy.
A useful mental model: midpoint is what the market thinks, and the book is what you can actually get.
TOKEN=7183... # a clobTokenId, not a conditionId
# Best price on one side of the book
curl "https://clob.polymarket.com/price?token_id=$TOKEN&side=BUY"
# Single reference probability — usually what dashboards want
curl "https://clob.polymarket.com/midpoint?token_id=$TOKEN"
# Full order book, when depth matters
curl "https://clob.polymarket.com/book?token_id=$TOKEN"
# Spread, as a rough liquidity signal
curl "https://clob.polymarket.com/spread?token_id=$TOKEN"Historical prices
For backtesting or charting, CLOB exposes a price history endpoint. It takes a token ID, an interval covering how far back to look, and a fidelity controlling how granular the returned points are.
The thing to understand is that fidelity and interval interact. Ask for a long window at fine granularity and you'll either get throttled or get a response large enough to be awkward to work with. Start coarse and tighten only where you actually need the resolution.
It's also worth being clear-eyed about scope: this gives you Polymarket's price history for Polymarket's markets. If you're training a model and want resolved outcomes across several venues, you'll be assembling that from multiple sources yourself, or taking it from one cross-platform historical archive.
# One week of history at hourly granularity
curl "https://clob.polymarket.com/prices-history?market=$TOKEN&interval=1w&fidelity=60"Rate limits and practical constraints
Polymarket's rate limits are enforced at the edge by Cloudflare and are global rather than per-account, which has two consequences worth planning around.
The first is that the limits are generous — high enough that a normal dashboard or scanner will never come close. The second is that because they're global, your headroom depends partly on what everyone else is doing. Don't design something that only works if you get the full quota to yourself.
The limits also differ per surface, with the discovery and data endpoints allowing meaningfully less than the CLOB endpoints. Since exact numbers get revised, check the official docs rather than hardcoding a figure you read in a blog post — including this one.
In practice the sensible pattern is the same one that applies to any market data source: cache market metadata aggressively because it barely changes, poll prices at whatever interval your use case genuinely needs, and back off when you see a 429 rather than retrying immediately.
Where a single-platform integration stops being enough
Everything above is a perfectly good afternoon's work, and for plenty of projects it's the whole job. If you only care about Polymarket, go direct — you'll have something running quickly and you won't be paying anyone.
The difficulty starts when you add a second platform.
Kalshi identifies markets with tickers, not condition IDs and token pairs. Its auth model is different, its schema is different, and its notion of what constitutes a single market doesn't always line up with Polymarket's. Novig is different again. There is no shared event identifier across the industry, so "the same question on two platforms" is something you have to determine yourself, market by market, and then keep correct as both platforms add and retire markets.
That matching problem is the part people consistently underestimate. Writing the second integration is tedious but tractable. Maintaining the mapping between them — as titles change, as resolution criteria differ in small but meaningful ways, as one platform splits a market the other keeps whole — is ongoing work that doesn't end.
This is the specific problem a unified prediction market data API exists to remove. One schema, one set of identifiers, and cross-platform entity resolution already done, so the same underlying event comes back under one ID regardless of which venues carry it. You write one integration instead of five, and you stop owning the matching logic.
The honest framing: if Polymarket alone answers your question, this guide is all you need. If you're comparing prices, building a consensus probability, or scanning for divergence, the integration work grows faster than the number of platforms — and that's the point where it stops being worth doing yourself. There's a free tier if you want to test that before committing to anything.
Related reading