Products
Datacenter proxies from $19/mo Rotating proxies from $49/mo ISP proxies from $33/mo Dedicated proxies from $3.50/mo UDP proxies from $5/mo Try proxies
Use cases
Data & scraping AI services Social media & messaging E-commerce & finance Media & entertainment Marketing & ads Automation & tools All use cases →
Pricing
Full pricing table All 20 locations Money-back guarantee
Resources
Blog Proxy API MCP server Setup guides FAQ For business About us Affiliate program
English
English Русский
← PapaProxy.net Blog

How the three Polymarket APIs divide the work

Polymarket runs three REST APIs on three hosts, and picking the wrong one is the most common reason a first integration stalls. Gamma describes what exists, the CLOB prices and trades it, the Data API answers for a wallet.

API & data August 8, 2026 3 min read Alex Young Alex Young Technical specialist
Key takeawaysThree hosts, three jobs: gamma-api for market and event metadata, clob for books, prices and trading, data-api for anything keyed by a wallet address.
  • Three hosts, three jobs: gamma-api for market and event metadata, clob for books, prices and trading, data-api for anything keyed by a wallet address.
  • In Gamma market objects, outcomes, outcomePrices and clobTokenIds are typed as strings and arrive JSON-encoded — decode before indexing, or you'll be comparing characters.
  • Join keys hold the pipeline together: conditionId links Gamma to the Data API, and the decoded clobTokenIds are the CLOB's token_id and the Data API's asset.
  • Query events rather than markets where you can — an event carries its markets nested, halving the round trips.
  • Page large sweeps with /markets/keyset and /events/keyset — cursor-based, limit max 100, offset rejected with 422, next_cursor absent on the last page; the offset endpoints still work but are being deprecated.
  • In a CLOB order book, bids are sorted price descending and asks ascending, so the best quote on each side is index 0.
  • Budgets are separate and IP-based: Gamma 4,000 req/10s, CLOB 9,000, Data API 1,000 (with /positions at 150), under a 15,000 global ceiling — so metadata enrichment doesn't compete with position polling.
  • Only trading and your own ledger need credentials: L1 signature to derive an API key, then L2 HMAC headers, with the signature type and funder address matched to the account type.

This summary was created with AI.

What Gamma covers

The Gamma API base URL is https://gamma-api.polymarket.com, and its job is the catalogue: everything the front end needs to display a market before anyone trades it. The OpenAPI spec marks its routes as public, so no key, wallet or signature is involved.

Two endpoints carry most of the traffic. The Gamma API markets endpoint, GET /markets, lists markets with filters that cover nearly every discovery need: slug, condition_ids, clob_token_ids, id and question_ids for direct lookups; tag_id with related_tags for category browsing; liquidity_num_min/max and volume_num_min/max for size thresholds; start_date_min/max and end_date_min/max for time windows; plus limit, offset, order (a comma-separated list of fields) and ascending for paging and sorting. The closed parameter defaults to false, so resolved markets are already excluded unless you ask for them.

The Gamma API events endpoint, GET /events, is the one to reach for first in practice. An event is the container — "Fed decision in October" — and it arrives with its markets array nested inside, so one event call replaces a market call plus a loop. Polymarket's own guidance says the same: work backwards from events to reduce the number of calls. For direct lookups by URL, GET /events/slug/{slug} takes the slug straight out of a Polymarket link.

One field-level trap deserves naming before you write a parser, because it's in the schema rather than in the prose. In the market object, outcomes, outcomePrices and clobTokenIds are typed as strings, not arrays — they arrive JSON-encoded, so market["outcomePrices"][0] gives you the character [ rather than a price. Decode them first. Everything else in the object is what you'd expect from a catalogue: conditionId, question, slug, bestBid, bestAsk, lastTradePrice, spread, volumeNum, liquidityNum, volume24hr, price-change fields by horizon, plus operational flags like enableOrderBook, acceptingOrders, orderPriceMinTickSize and orderMinSize.

What CLOB covers

The CLOB API base URL is https://clob.polymarket.com, and this is the trading surface — the live order book plus everything you do against it. Market data here is free to read and needs no credentials: /book and /books for order books, /price and /prices for a single side's price, /midpoint and /midpoints, /prices-history for a price series, and a tick-size lookup per market.

Note the plural forms. Batch variants exist precisely so you don't loop: /books and /prices take a set of tokens in one request. Their request-rate allowance is lower — 500 requests per 10 seconds against 1,500 for the single-item versions — but each request covers many tokens, so for any multi-token workload they cut the number of requests you need rather than the other way round. How much you save depends on how many tokens you pack per call.

The CLOB API endpoints for account state and trading sit behind CLOB API authentication, which runs on two levels. L1 is a signature from your private key — an EIP-712 signature — used once to create or derive an API key. L2 is that key, sent as HMAC headers on subsequent requests. Two account details decide whether this works: the signature type matching how the account was created (a plain EOA, or the POLY_PROXY, GNOSIS_SAFE, POLY_1271 types), and the funder address that actually holds the money, which for an EOA account is the EOA itself. Authenticated routes then cover order placement and cancellation plus your own ledger through /data/orders and /data/trades — your orders and your fills, not an arbitrary wallet's.

Two rate-limit details matter here more than elsewhere. The API key endpoints have their own tight limit of 100 requests per 10 seconds, and orders and cancellations are governed by both IP-based Cloudflare limits and separate per-signer token buckets — so a single account can't burst through by spreading across addresses, and shouldn't try.

What the Data API covers

The Data API at https://data-api.polymarket.com answers questions keyed by address. /positions and /closed-positions for holdings, /activity for the chronological on-chain feed, /trades for fills, /value for a portfolio total, /holders for the largest holders of a given market, plus leaderboard surfaces.

Worth stating plainly because it's a common search: there is no Data API markets endpoint in the sense of a market catalogue. Market metadata lives on Gamma, and the Data API's market-shaped surface is /holders plus the market and eventId filters on the address-scoped endpoints. If you're looking for a market's question, slug or token IDs, you're on the wrong host — and if you're looking for who holds it, you're on the right one.

Choosing an API for the job

The decision collapses to one question: what is your key? Each API is indexed by a different identifier, and knowing how to use the Gamma API mostly means knowing how to translate between them.

You have You want Host Endpoint
A Polymarket URL Market metadata Gamma /events/slug/{slug}
A conditionId The market's question, dates, flags Gamma /markets?condition_ids=...
A token ID Live order book or price CLOB /book, /price
A wallet address Positions, activity, fills Data /positions, /activity, /trades
A market Its largest holders Data /holders

The join keys are the practical core. conditionId links Gamma to the Data API — it appears in both a market object and a position object. clobTokenIds on the Gamma market decodes to the two outcome token IDs, which are what the CLOB calls token_id and what the Data API returns as asset on a position. So the canonical pipeline is: discover on Gamma, price on the CLOB, attribute on Data — and carry conditionId and the token IDs along as you go.

There's a budget argument for this split too, and it works in your favor. The three hosts have separate allowances: Gamma is 4,000 requests per 10 seconds, the CLOB 9,000, the Data API 1,000 — under a global ceiling of 15,000. Polymarket's documentation says Cloudflare delays and queues excess requests, but our August 2026 test found immediate 429 responses with no gradual slowdown. Because the budgets are separate, enriching positions with market metadata doesn't spend the allowance your position polling needs. The Data API is the tightest of the three and the one to design around: /positions allows 150 requests per 10 seconds and /trades 200.

The Gamma API markets endpoint parameters are what keep you inside those budgets. Filtering server-side with tag_id, date bounds and volume thresholds beats fetching broadly and filtering in your own code, and asking for events instead of markets collapses two round trips into one. Cache aggressively while you're at it: a market's question, slug and token IDs don't change, so they belong in a local store rather than in a polling loop.

For sweeps across large result sets, new code should page with cursors rather than offsets. Polymarket added /markets/keyset and /events/keyset as the cursor-based replacement for the offset endpoints: you read next_cursor from each response and pass it back as after_cursor, with limit capped at 100 and a default of 20. The contract is strict in a helpful way — offset is explicitly rejected with a 422 on these routes, and next_cursor is present only while more pages remain, so its absence is your stop condition rather than a guess. The offset-based endpoints still work today, but they're on a deprecation path; anything built now belongs on keyset.

Calling them from Python

Here's the whole pipeline in one CLOB API Python example, starting from a URL slug and ending with a live book — no credentials anywhere, since every read below is public:

Python
import json, requests

GAMMA = "https://gamma-api.polymarket.com"
CLOB  = "https://clob.polymarket.com"
DATA  = "https://data-api.polymarket.com"
HEAD  = {"User-Agent": "market-tracker/1.0"}

def event_by_slug(slug):                      # 1. discovery: one call, markets nested
    r = requests.get(f"{GAMMA}/events/slug/{slug}", headers=HEAD, timeout=15)
    r.raise_for_status()
    return r.json()

def book(token_id):                           # 2. pricing: CLOB is keyed by token, not market
    r = requests.get(f"{CLOB}/book", params={"token_id": token_id}, headers=HEAD, timeout=15)
    r.raise_for_status()
    return r.json()

def holders(condition_id):                    # 3. attribution: Data is keyed by address/market
    r = requests.get(f"{DATA}/holders", params={"market": condition_id}, headers=HEAD, timeout=15)
    r.raise_for_status()
    return r.json()

event = event_by_slug("fed-decision-in-october")     # replace with any live slug
for market in event.get("markets", []):
    outcomes  = json.loads(market["outcomes"])       # JSON-encoded strings, not arrays
    token_ids = json.loads(market["clobTokenIds"])
    prices    = json.loads(market["outcomePrices"])
    print(market["question"], "|", market["conditionId"])

    for name, token, cached in zip(outcomes, token_ids, prices):
        b = book(token)
        # bids are sorted price DESCENDING, asks ASCENDING — best quote is index 0
        best_bid = b["bids"][0]["price"] if b.get("bids") else None
        best_ask = b["asks"][0]["price"] if b.get("asks") else None
        print(f"  {name:<6} gamma={cached:<8} bid={best_bid} ask={best_ask}")

    top = holders(market["conditionId"])              # 3rd host: same conditionId as the key
    print(f"  holders returned: {len(top)}")

What to look at, and what this deliberately doesn't do. The three json.loads calls are the point of the example — skip them and every downstream comparison silently operates on characters. Book ordering is the second trap: the schema documents bids as sorted by price descending and asks ascending, so the best quote on each side is index 0 — reaching for [-1] gets you the worst resting order in the book, which looks plausible enough in a log to survive review. The User-Agent header is set because requests without a recognisable one have been reported to hit Cloudflare more often. Prices from Gamma are catalogue values and can lag the book, which is why the loop prints both side by side: use Gamma's number for a listing page, the CLOB's for anything you'll trade on. The example has no retry policy, no batching (/books would replace the per-token loop in a real tracker), no WebSocket for live updates, and no pagination.

One last operational note that costs people an afternoon: Gamma and the docs don't fully agree on filters. Polymarket's guide recommends active=true&closed=false for live markets, while the current /markets OpenAPI card lists closed (defaulting to false) among its documented parameters without active. Send what the card documents, verify the rest against live responses, and don't assume a filter works because a tutorial used it.