Polymarket Bot Tutorial · Chapter 7 of 32

Polymarket Gamma API deep dive: /events and /markets endpoints, pagination, tag IDs (864 Tennis, 745 NBA, etc), filtering by 24h volume, rate limits, and Python and Node code samples.

What this chapter covers

Gamma is Polymarket's catalog API - it lists every event, every market, tag, image, and resolved outcome the front-end displays. The CLOB API trades; Gamma describes what is tradeable. Most bot bugs at the discovery layer come from confusing the two, or from missing the pagination contract. This chapter is the field reference for Gamma's main endpoints with the exact parameter behavior our production fetchers depend on.

  • Gamma vs CLOB: when to use which
  • /events endpoint anatomy
  • /markets endpoint anatomy
  • Tags and tag IDs (verified list)
  • Filtering: active, closed, volume24hr ordering
  • Pagination and limits
  • Rate limits and caching
  • Code: fetch top 24h-volume markets

Gamma vs CLOB: when to use which

Two different services for two different jobs.

Gamma (gamma-api.polymarket.com): catalog. Lists events, markets, tags, descriptions, images, resolved outcomes, 24-hour volume, total volume, end dates. Read-only HTTP. No authentication required for most reads. Updated continuously but eventually consistent - a market just closed may still show closed: false for a few seconds.

CLOB (clob.polymarket.com): trading + order book. Lists current best bid/ask, top-N book depth, recent trades. Authenticated for write endpoints (order placement, cancellation). Real-time WebSocket channels available for book updates.

Rule of thumb: use Gamma to find what to trade; use CLOB to trade it. A bot that reads prices from Gamma is using stale data - Gamma's price fields update less frequently than CLOB's order book. A bot that reads market metadata from CLOB is making more requests than needed.

/events endpoint anatomy

GET /events returns event-level data. An "event" is a Polymarket page; a single event may contain multiple markets (e.g. the 2024 Presidential Election event has one market per candidate).

Key fields:

  • slug: URL-safe identifier, stable for the life of the event.
  • title, description: human display.
  • endDate (ISO 8601): when the event closes.
  • active, closed: booleans; combine in a query with ?active=true&closed=false for live events.
  • volume, volume24hr: USD totals.
  • tags: array of tag objects (see tags section below).
  • markets: array of child market objects (see /markets anatomy).

The single most common discovery pattern: GET /events?active=true&closed=false&order=volume24hr&ascending=false&limit=100. Returns the 100 highest-volume currently-live events.

/markets endpoint anatomy

GET /markets returns market-level data. A market is one Y/N or multi-outcome contract; it lives inside an event.

Key fields:

  • slug: URL-safe identifier.
  • question: the title displayed on the trading page (e.g. "Will Trump be president on January 1, 2027?").
  • outcomes: JSON string of outcome names, e.g. '["Yes","No"]'. Always two elements for binary; more for NegRisk.
  • outcomePrices: JSON string of current prices as decimals, e.g. '["0.62","0.38"]'. Both sides sum to ~1.0 minus spread.
  • clobTokenIds: JSON string of ERC-1155 token IDs aligned with outcomes. These are the tokens you actually buy/sell.
  • negRisk: boolean; true for multi-outcome sum-to-1 markets. Matters for order placement (chapter 11).

The outcomes / outcomePrices / clobTokenIds fields arrive as JSON strings, not parsed arrays - JSON-decode them before using.

Tags and tag IDs (verified list)

Tags are categorical labels (Sports, Crypto, Tennis, NBA, etc.). The verified production tag IDs for the most-used categories:

TagIDTagID
Sports1NBA745
Crypto21NFL450
Politics2Tennis864
Bitcoin100196Esports702
Ethereum100383Soccer1059
Election3EPL739
Middle East1432UCL2186

Filter by tag with ?tag_id=745 for a specific tag, or ?tag_slug=nba using the slug. Slug-based filtering is more readable in code but slightly slower; ID-based is the production default.

Filtering: active, closed, volume24hr ordering

The four filter dimensions you will use 95% of the time.

  • active=true|false: true excludes markets that the Polymarket team has hidden from the UI.
  • closed=true|false: false excludes resolved markets. The combination active=true&closed=false is the most common live filter.
  • order=volume24hr, order=volume, order=endDate: sort key. Most useful is volume24hr for finding currently-active markets.
  • ascending=true|false: defaults to true on most endpoints; you almost always want false for volume orderings.

Caveat: filtering by tag_id combined with order=volume24hr and ascending=false sometimes returns an empty page when the tag has very few live markets. Always over-fetch (request more than you display) and post-filter to handle this.

Pagination and limits

The limit parameter accepts 1 to 500 per call. If you leave it off, the default is only 20 results, so always set it explicitly or you will silently miss most of the catalog. Above 500 the server caps quietly: you receive 500 with no flag telling you there was more.

Pagination is offset-based: ?limit=500&offset=500 for the second page. There is no cursor-based pagination, so concurrent inserts can cause page boundaries to shift between calls. For most bot discovery purposes this is acceptable; for archive scrapes, sort by a stable field (endDate or createdAt) and detect overlap by slug.

Practical pattern for "all live markets": fetch limit=500&order=volume24hr&ascending=false. That covers the top 500 by volume which is essentially every market with non-trivial activity. Going beyond page 1 is rarely useful - markets in the tail of the volume distribution are by definition not where the action is.

Rate limits and caching

Gamma is fronted by Cloudflare and has soft rate limits per IP. Empirical thresholds observed under production load:

  • Up to ~30 req/sec from one IP sustained: fine.
  • Bursts of 100+ requests per second: you occasionally get a 429 (the HTTP "Too Many Requests" response), but a retry a moment later succeeds.
  • ~500 req/sec sustained: rate-limit page or temporary block (10-60s).

The published response headers include Cache-Control values of 30-60 seconds for most endpoints. Honor them - there is no benefit to re-fetching the same event 10 times in a minute. Production caching pattern: in-process LRU + TTL keyed on the full URL, 30s TTL. Saves request count and reduces latency.

For high-frequency strategies, mirror Gamma data into a local store updated by a single fetcher process; have multiple consumers read from that store. One fetcher × many consumers > many fetchers × Gamma.

Code: fetch top 24h-volume markets

Reference fetcher in three languages, returning the top 50 live markets by 24-hour volume.

Python:

import requests
r = requests.get("https://gamma-api.polymarket.com/events",
                 params={"active":"true","closed":"false",
                         "order":"volume24hr","ascending":"false","limit":50},
                 timeout=10)
for ev in r.json()[:50]:
    print(ev["slug"], ev.get("volume24hr"))

Node:

const url = "https://gamma-api.polymarket.com/events?active=true&closed=false" +
            "&order=volume24hr&ascending=false&limit=50";
const events = await fetch(url).then(r => r.json());
for (const ev of events) console.log(ev.slug, ev.volume24hr);

Curl:

curl -s "https://gamma-api.polymarket.com/events?active=true&closed=false&order=volume24hr&ascending=false&limit=50" \
  | jq '.[].slug'

The Polymarket gamma /events endpoint does NOT support a free-text search parameter - adding ?q=foo or ?search=foo silently returns the default ordering. Filter by slug or tag instead.

Frequently asked questions

What is the Polymarket Gamma API?
Gamma is Polymarkets metadata and discovery API. It serves event/market lists, titles, descriptions, end dates, tags, and aggregate volume. It does NOT serve real-time order book data - that lives on the CLOB API. For most discovery and analytics use cases, you start with Gamma.
What is the difference between an event and a market on Polymarket?
An event groups one or more markets that share a question theme. A market is a specific Yes/No outcome with its own order book and token IDs. A "2026 NBA Champion" event contains 30 markets (one per team). For binary Yes/No events, the event has 1 underlying market.
How do I find Polymarket tag IDs?
Tags are exposed via the gamma /tags endpoint. Verified IDs we use in production: 864 Tennis, 745 NBA. Tag slugs in URLs (?tag_slug=tennis) and free-text query (?q=tennis) are unreliable - use numerical tag_id only. Check /tags before relying on a slug.
How do I sort Polymarket events by 24h volume?
/events?order=volume24hr&ascending=false - this is what powers most "trending markets" widgets. Combine with active=true&closed=false to filter out expired or paused markets. Limit defaults to 20 (set it explicitly); max is 500 per call.
Are there pagination limits on Gamma?
Yes. The /events and /markets endpoints accept limit (max 500 per call) and offset for pagination. Most libraries do not paginate automatically - if you need the full universe, loop with offset until you get a page with fewer than `limit` results.
Does Gamma support WebSocket?
No. Gamma is REST-only. For real-time updates (price changes, new markets, order book) use the CLOB WebSocket - covered in chapter 8 of this series.