Where Polymarket bet/event categorization actually comes from, and the non-obvious fact that the /markets endpoint cannot supply it. Verified live 2026-06-03 against the gamma API.
The data source
Categorization comes from the gamma /events?slug=<event_slug> endpoint, which returns a tags array of {label, slug} objects. These tags are the only reliable category signal.
The
/marketsendpoint is useless for categorizationThe
/marketsendpoint (used byGammaClient::market_metadata) returnscategory = nulland no tags. Do not try to derive a bucket from market metadata — you must query/eventsby event slug.
Multi-slug batching works: /events?slug=a&slug=b returns both events in one call. Use this to bound request fan-out when priming the category cache.
Verified tag examples (live 2026-06-03)
| Event | tags slugs |
|---|---|
| MLB game | sports, games, mlb, baseball |
| LA mayoral race | politics, elections, … |
| IEM Cologne (CS2) | iem-cologne, sports, counter-strike-2, esports, major |
| Crypto market | crypto |
Esports events are ALSO tagged
sportsAn IEM/CS2 esports event carries both
esportsandsportstags. Esport detection MUST take precedence over sport detection, or every esports event mis-buckets as a generic sport.
Bucket mapping precedence
The category bucket is resolved by checking tag slugs in strict precedence order (first match wins):
- esport —
esports(and friends likecounter-strike-2) - sport —
sports,games, league/sport slugs - geo —
politics,elections,geopolitics,world - crypto —
crypto - other — fallback
The esport→sport ordering is load-bearing (see warning above). Geo collapses the politics/elections/geopolitics/world family into one bucket.
Where it’s used
run_category_syncpopulates thepmv2_event_categorycache table from this endpoint (best-effort, bounded per-chunktokio::timeout). See pmv2-topbets-categories-filters./topbetscategory filters (eq:/neq:) read the cache. Filters are inert until the cache is primed by a scheduled run.