The Polymarket Gamma API is Polymarket Global’s read-only market discovery layer at https://gamma-api.polymarket.com — no authentication, no API key. Use /events and /markets to list, /public-search?q= for keyword search (the parameter is qquery= returns 422), and read clobTokenIds off a market to get the token IDs you need for CLOB trading. Rate limits are 4,000 requests per 10s overall, with /markets at 300/10s, /events at 500/10s, and /public-search at 350/10s. Polymarket US does not serve Gamma — its equivalent is https://gateway.polymarket.us/v1/search?query=.

curl -s "https://gamma-api.polymarket.com/markets?closed=false&active=true&limit=5"

Endpoints verified against the live API on 2026-08-30.

Overview

Gamma indexes every event, market, tag, and sports category on the platform, and you can start querying it immediately without credentials.

Use the Gamma API to:

  • Discover markets by category, volume, liquidity, or keyword search
  • Get token IDs and condition IDs needed for CLOB trading
  • Build scanners that surface high-volume or mispriced markets
  • Retrieve event structures to understand how multi-outcome questions decompose into binary markets

The Gamma API is read-only. It does not support placing orders, streaming prices, or querying on-chain balances. For those capabilities, you need the CLOB API, WebSocket feeds, or the Subgraph.


When to Use Which API

Polymarket exposes several APIs, each optimized for a different use case. The Gamma API is the starting point for nearly every workflow because it is where you discover markets and retrieve the identifiers needed by other APIs.

NeedAPIEndpoint
Find markets and eventsGamma/events, /markets, /public-search
Get real-time prices and orderbooksCLOB/price, /book
Place and manage ordersCLOB/order, /orders
Get price history for backtestingCLOB/prices-history
Get user positions and tradesData/positions, /trades
Get on-chain token balancesSubgraphGraphQL query

A typical workflow looks like this: query the Gamma API to find a market, extract its token IDs, then pass those token IDs to the CLOB API for live prices, order placement, or historical data.


Data Model

Understanding the relationship between Events and Markets is essential before working with any endpoint.

Events

Events are top-level questions. An event like “Who will win the 2024 Presidential Election?” groups together all the individual tradable outcomes under a single umbrella.

Each event has:

  • A unique numeric ID and a URL-friendly slug
  • A title and description describing the question
  • One or more Markets as sub-outcomes
  • Tags for categorization (e.g., “politics”, “crypto”, “sports”)
  • Metadata like startDate, endDate, and volume

Markets

Markets are the tradable binary outcomes within events. Each market represents a single Yes/No question with its own order book and pricing.

Each market has:

  • conditionId — the unique identifier for CTF (Conditional Token Framework) contracts on-chain
  • questionId — a hash of the market question used by the resolution oracle
  • clobTokenIds — an array of two token IDs, one for the Yes outcome and one for No (ERC-1155 tokens)
  • outcomes and outcomePrices — parallel arrays mapped 1:1, so outcomes[0] corresponds to outcomePrices[0]
  • enableOrderBook — a boolean indicating whether CLOB trading is available for this market
  • volume — total trading volume in USDC
  • liquidity — current liquidity depth

A single event can contain many markets. For example, an election event might have separate markets for each candidate, each independently tradable.

Slugs

Slugs are URL-friendly identifiers like fed-decision-in-october or bitcoin-above-100k-2025. You can use slugs to query both the /events and /markets endpoints, which is convenient when building integrations that reference markets by their human-readable names rather than numeric IDs.


Endpoint Reference

All endpoints are prefixed with https://gamma-api.polymarket.com. Responses are JSON.

GET /events

Retrieve a list of events with optional filtering, sorting, and pagination.

curl "https://gamma-api.polymarket.com/events?active=true&limit=10&order=volume_24hr&ascending=false"

Returns an array of event objects, each containing a nested markets array with full market details.

GET /events/{id}

Retrieve a single event by its numeric ID.

curl "https://gamma-api.polymarket.com/events/12345"

Returns the full event object including all child markets, tags, and metadata.

GET /markets

Retrieve a list of markets with optional filtering. Useful when you want to query across events or find specific markets by condition ID.

curl "https://gamma-api.polymarket.com/markets?active=true&limit=10"

GET /markets/{id}

Retrieve a single market by its numeric ID or condition ID.

curl "https://gamma-api.polymarket.com/markets/67890"

Returns the full market object with token IDs, prices, outcomes, volume, and all metadata.

Full-text search across events and markets. This is the best endpoint for keyword-based discovery.

The search parameter is q, not query. Verified on 2026-08-30: ?query=bitcoin returns 422 Unprocessable Entity no matter what else you send, while ?q=bitcoin returns 200. This endpoint also ignores limit — the page size parameter is limit_per_type.

curl -s "https://gamma-api.polymarket.com/public-search?q=bitcoin&limit_per_type=5"

The response has two top-level keys — events and pagination:

{
  "events": [
    { "slug": "what-price-will-bitcoin-hit-in-august-2026", "markets": [ /* 46 markets */ ] },
    { "slug": "bitcoin-above-on-august-29-2026",            "markets": [ /* 12 markets */ ] }
  ],
  "pagination": { "hasMore": true, "totalResults": 122383 }
}

Matching markets are nested inside each event’s markets array, and each one carries a populated clobTokenIds pair — so a single search call gives you everything you need to place a CLOB order. limit_per_type defaults to 5 events; use pagination.hasMore to decide whether to keep paging.

GET /v1/search — the Polymarket US equivalent

Polymarket US does not serve Gamma. Its discovery layer is the gateway host, and the search endpoint sits under a /v1 prefix — and unlike Gamma, it takes query= rather than q=:

https://gateway.polymarket.us/v1/search?query=bitcoin&limit=100

The /v1 is mandatory — https://gateway.polymarket.us/search?query=bitcoin returns 404 Not Found. Like Gamma’s /public-search, no authentication is required. Verified against the live API on 2026-08-30:

curl -s "https://gateway.polymarket.us/v1/search?query=bitcoin&limit=100" \
  | jq '{keys: (keys), events: (.events | length), first: .events[0].ticker}'
{
  "keys": ["events"],
  "events": 6,
  "first": "btc-above-yr-12-31-2026"
}

Four differences from Gamma’s /public-search that break naive ports:

  1. The parameter names differ. Gamma wants q and limit_per_type; the US gateway wants query and limit. Sending Gamma’s q= here is silently ignored rather than rejected.
  2. There is no pagination object. The response has exactly one top-level key, events, so there is no hasMore or totalResults to page against.
  3. limit counts events, not markets. limit=100 returned 6 events containing 51 markets between them; limit=2 returned 2 events. It is a cap, not a target — a narrow query returns fewer.
  4. conditionId and clobTokenIds are null on every market. All 51 markets in the response above carried nulls for both, where Gamma populates them. You cannot get CLOB token IDs out of Polymarket US search; use the event/market slug or id and resolve through the authenticated api.polymarket.us trading host instead.

query is optional. Omitting it (?limit=2) returns a default listing rather than an error.

A market object inside events[].markets[] looks like this:

{
  "id": "269271",
  "slug": "cpc-btc-above-yr-12-31-2026-200k",
  "question": "Will Bitcoin be above ___ in 2026?",
  "outcomes": "[\"Yes\",\"No\"]",
  "outcomePrices": "[\"0.0400\",\"0.0500\"]",
  "bestBidQuote": { "value": "0.0400", "currency": "USD" },
  "bestAskQuote": { "value": "0.0500", "currency": "USD" },
  "status": "MARKET_STATUS_OPEN",
  "orderPriceMinTickSize": 0.01,
  "minimumTradeQty": 0.01
}

Two parsing notes: outcomes and outcomePrices are JSON-encoded strings, so they need a second json.loads() pass — the same quirk Gamma has. And status uses a prefixed enum (MARKET_STATUS_OPEN), not the bare open/closed strings Gamma returns, so a shared status check across both hosts needs normalizing.

import json, requests

r = requests.get("https://gateway.polymarket.us/v1/search",
                 params={"query": "bitcoin", "limit": 100}, timeout=30)

for event in r.json()["events"]:
    for market in event["markets"]:
        outcomes = json.loads(market["outcomes"])        # ["Yes", "No"]
        prices   = json.loads(market["outcomePrices"])   # ["0.0400", "0.0500"]
        print(event["ticker"], market["slug"], dict(zip(outcomes, prices)))

GET /tags, /series, /sports, /teams

Categorization endpoints for browsing the market taxonomy:

  • /tags — returns all available category tags (politics, crypto, sports, etc.)
  • /series — returns event series (e.g., recurring monthly predictions)
  • /sports — returns sports categories for sports-specific markets
  • /teams — returns team entities linked to sports markets

These are useful for building navigation UIs or filtering markets by category programmatically.


Filtering, Sorting, and Pagination

The Gamma API supports a consistent set of query parameters across its listing endpoints.

Filter Parameters

ParameterTypeDescription
activebooleanFilter to only active (open) markets or events
closedbooleanFilter to only closed (resolved) markets or events
tag_idstringFilter by a specific category or tag identifier
exclude_tag_idstringExclude markets or events with a specific tag
related_tagsbooleanWhen true, include markets from related tags
slugstringFetch a specific event or market by its URL slug

Combine filters to narrow results precisely. For example, active=true&tag_id=crypto returns only open crypto markets.

Sort Options

Use the order parameter to sort results and ascending to control direction (defaults to descending).

ValueDescription
volume_24hr24-hour trading volume (most popular for scanners)
volumeTotal all-time trading volume
liquidityCurrent liquidity depth in the order book
start_dateWhen the market opened for trading
end_dateWhen the market is scheduled to close
competitiveHow competitive the market pricing is (close to 50/50)
closed_timeWhen the market was resolved (for closed markets)

Pagination

Gamma has two pagination systems, and the one nearly every tutorial shows you dies after 2,000 rows.

limit + offset works on /events and /markets, but offset is capped at 2000. Ask for one more and the request fails:

curl -s "https://gamma-api.polymarket.com/markets?limit=1&offset=2001"
{"type":"validation error","error":"offset too large, use /markets/keyset for deeper pagination"}

That is an HTTP 422, not an empty array — so the classic “loop until the response is empty” pattern does not terminate cleanly, it throws (or, if you ignore status codes, silently ends your dataset at row 2,000). /events returns the same error naming /events/keyset. offset=2000 is still fine; 2001 is not, and the cap applies to offset alone — offset=2000&limit=500 is accepted.

Also note limit is clamped to 100 on these endpoints. Requesting limit=500 or limit=1000 returns 100 rows with a 200 status, no warning. Any loop that advances offset by its requested limit rather than by len(batch) will skip records.

Keyset pagination is the supported way past 2,000 rows. Both /markets/keyset and /events/keyset accept the same filter and sort parameters as their offset-based counterparts, but return a wrapped object with a cursor instead of a bare array:

curl -s "https://gamma-api.polymarket.com/markets/keyset?limit=3&closed=false&active=true"
{
  "$schema": "https://gamma-api.polymarket.com/schemas/MarketsKeysetListResponse.json",
  "markets": [ { "id": "559651", "question": "Xi Jinping out before 2027?", ... } ],
  "next_cursor": "bjZlbp--Id8uILt2O_myoOvX-CNl-MzVGdWQ_udL8nt7InYiOjEsImsiOiJtYXJrZXRz..."
}

Feed next_cursor back as after_cursor on the next request. Three footguns here, all verified live:

  1. The parameter is after_cursor, not cursor. Gamma ignores unknown query parameters silently — it returns 200 with no warning. So ?cursor=<token> re-serves page 1 forever, and a while next_cursor: loop becomes an infinite loop that never advances. The same is true of next_cursor=, after=, and every other plausible spelling. This trips people up often enough that it has been filed as a server-side bug — Polymarket/agents#227, “cursor parameter ignored, returns same first page” (Apr 26, 2026) — and closed as off-topic rather than answered. It is not a server bug. Rename the parameter to after_cursor and paging works.
  2. offset is rejected outright on keyset endpoints{"type":"validation error","error":"offset is not allowed on keyset endpoints"} (422). You cannot mix the two schemes.
  3. limit clamps to 100 here too. limit=1000 returns 100 rows.
import requests

GAMMA = "https://gamma-api.polymarket.com"

def get_all_active_markets():
    """Paginate every active market via keyset — no 2,000-row ceiling."""
    markets = []
    cursor = None
    while True:
        params = {"active": "true", "closed": "false", "limit": 100}
        if cursor:
            params["after_cursor"] = cursor   # NOT "cursor" — that is silently ignored
        resp = requests.get(f"{GAMMA}/markets/keyset", params=params, timeout=30)
        resp.raise_for_status()
        body = resp.json()
        batch = body.get("markets", [])
        if not batch:
            break
        markets.extend(batch)
        cursor = body.get("next_cursor")
        if not cursor:
            break
    return markets

Swap markets for events (both the path and the response key) to walk events instead. Keyset endpoints also exist for /comments/keyset, /reports/keyset, and /spotlights/keyset.

On the “May 1, 2026 sunset”: Polymarket announced that the offset-based /markets and /events list endpoints would be deprecated on May 1, 2026 in favour of the keyset endpoints. As of 2026-08-30 they are still live and still return 200 — the deprecation has not been enforced, it just came with the offset ceiling. Treat them as on borrowed time and build new pagination on keyset.

Use offset paging only when you know you are staying inside the first 2,000 rows — a top-50 leaderboard, a single tag’s markets. For a full sync, backfill, or anything unbounded, start with keyset.

Endpoint behaviour verified against the live Gamma API and https://gamma-api.polymarket.com/openapi.json on 2026-08-30.


Price History for Backtesting

Price history is not available through the Gamma API. To get historical price data for backtesting, use the CLOB API’s /prices-history endpoint.

The workflow is:

  1. Use the Gamma API to discover a market and extract its clobTokenIds
  2. Pass the token ID to the CLOB API’s /prices-history endpoint
  3. Specify a time range and fidelity (resolution) for the data points
import requests

def get_price_history(token_id, start_ts=None, end_ts=None, interval="1h", fidelity=60):
    """Fetch historical price data from the CLOB API.

    Args:
        token_id: The CLOB token ID (from Gamma API's clobTokenIds field)
        start_ts: Start timestamp (Unix seconds)
        end_ts: End timestamp (Unix seconds)
        interval: Candle interval (e.g., '1h', '1d')
        fidelity: Data point resolution in minutes

    Returns:
        Dict with 'history' array of {t: timestamp, p: price} objects
    """
    params = {"market": token_id, "interval": interval, "fidelity": fidelity}
    if start_ts:
        params["startTs"] = start_ts
    if end_ts:
        params["endTs"] = end_ts

    resp = requests.get(
        "https://clob.polymarket.com/prices-history",
        params=params
    )
    return resp.json()

# Get 1-hour candles for the last 7 days
history = get_price_history("<token-id>", interval="1h")
for point in history.get("history", []):
    print(f"{point['t']}: {point['p']}")

Each data point in the history array contains:

  • t — the timestamp for the data point
  • p — the price at that timestamp (0.00 to 1.00, representing probability)

For backtesting, combine Gamma API market discovery with CLOB price history to build datasets spanning multiple markets across any time range.


Caching Strategies

Market metadata from the Gamma API — questions, token IDs, event structures, and tag lists — changes infrequently. Caching aggressively reduces your request count and keeps you well under rate limits.

import requests
import time

class GammaCache:
    """Simple TTL cache for Gamma API responses."""

    def __init__(self, ttl=300):
        self._cache = {}
        self._ttl = ttl

    def get(self, endpoint, params=None):
        """Fetch from cache or make a fresh request."""
        key = f"{endpoint}:{params}"
        if key in self._cache:
            data, ts = self._cache[key]
            if time.time() - ts < self._ttl:
                return data

        resp = requests.get(
            f"https://gamma-api.polymarket.com{endpoint}",
            params=params
        )
        data = resp.json()
        self._cache[key] = (data, time.time())
        return data

Recommended TTL values by data type:

Data TypeRecommended TTLRationale
Market metadata (questions, token IDs)5 minutesRarely changes after creation
Event structures5 minutesNew markets may be added to events
Tag and category lists1 hourTags are relatively stable
Search results1 minuteResults shift as volume changes
Prices (via CLOB, not Gamma)Do not cacheUse WebSocket for real-time data

For production scanners, consider persisting cached data to disk or a database so you can restart without re-fetching everything.


Rate Limits

The Gamma API enforces rate limits via Cloudflare throttling. Exceeding limits returns HTTP 429 responses.

EndpointLimit
General (all endpoints)4,000 / 10s
/markets + /events listing900 / 10s
/events500 / 10s
/public-search350 / 10s
/markets300 / 10s
/comments200 / 10s
/tags200 / 10s

Best practices for staying within limits:

  • Cache aggressively — most metadata does not change between requests
  • Use larger page sizes — fetching 100 items in one request is better than 10 requests of 10
  • Add backoff on 429s — if throttled, wait at least 10 seconds before retrying
  • Batch your discovery — scan markets once, store the results, then query the CLOB API for live data

For the full rate limit reference across all Polymarket APIs, see the Polymarket Rate Limits Guide.


Building a Market Scanner

Here is a complete example that queries the Gamma API for high-volume markets with active order books and extracts the data needed for further analysis or trading.

import requests
import time

def scan_top_markets(min_volume=100000, limit=20):
    """Find the highest-volume active markets with CLOB order books.

    Args:
        min_volume: Minimum total volume in USDC to include
        limit: Number of events to fetch from the Gamma API

    Returns:
        List of market dicts sorted by 24h volume descending
    """
    resp = requests.get(
        "https://gamma-api.polymarket.com/events",
        params={
            "active": "true",
            "limit": limit,
            "order": "volume_24hr",
            "ascending": "false"
        }
    )
    events = resp.json()

    results = []
    for event in events:
        for market in event.get("markets", []):
            # Skip markets without CLOB trading
            if not market.get("enableOrderBook"):
                continue

            volume = float(market.get("volume", 0))
            if volume < min_volume:
                continue

            outcomes = market.get("outcomes", [])
            prices = market.get("outcomePrices", [])

            results.append({
                "question": market.get("question"),
                "condition_id": market.get("conditionId"),
                "token_ids": [
                    market.get("clobTokenIds", [None, None])[0],
                    market.get("clobTokenIds", [None, None])[1]
                ],
                "volume_24h": volume,
                "outcomes": dict(zip(outcomes, prices))
            })

    return sorted(results, key=lambda x: x["volume_24h"], reverse=True)

# Find top markets by volume
top = scan_top_markets(min_volume=50000)
for market in top[:10]:
    print(f"{market['question']}")
    for outcome, price in market['outcomes'].items():
        print(f"  {outcome}: {price}")

This scanner gives you everything you need to:

  • Monitor market activity — run it on a schedule to track which markets are trending
  • Feed a trading bot — pass the token_ids to the CLOB API for live pricing and order placement
  • Build a dashboard — store results in a database and visualize volume trends over time
  • Backtest strategies — use the token_ids with the CLOB /prices-history endpoint to pull historical data

FAQ

What is the Polymarket Gamma API?

The Gamma API at gamma-api.polymarket.com provides market discovery and metadata for Polymarket. It indexes events, markets, tags, series, and sports data. No authentication is required. Use it to find markets, get token IDs, and discover events.

How do I get Polymarket price history for backtesting?

Price history is available via the CLOB API’s /prices-history endpoint, not the Gamma API. Pass a token_id and time range to get historical price data points. Use the Gamma API to discover markets and get token IDs, then use the CLOB API for price history.

What is the difference between Events and Markets in the Gamma API?

Events are top-level questions (e.g., “Who will win the 2024 election?”). Markets are specific tradable binary outcomes within events, each with its own token IDs and condition ID. A single event can contain multiple markets for multi-outcome predictions.

What are the rate limits for the Gamma API?

The general limit is 4,000 requests per 10 seconds. Specific endpoint limits: /events is 500/10s, /markets is 300/10s, and /public-search is 350/10s. All limits are enforced via Cloudflare throttling.


See Also


This guide is maintained by AgentBets.ai. Found an error or API change we missed? Let us know on Twitter.

Not financial advice. Built for builders.