Tutorial de bot de Polymarket · Capítulo 7 de 32
Análisis profundo de la API Gamma de Polymarket: endpoints /events y /markets, paginación, IDs de tags (864 Tennis, 745 NBA, etc.), filtrado por volumen de 24 h, rate limits y ejemplos de código en Python y Node.
Qué cubre este capítulo
Gamma es la API de catálogo de Polymarket: lista cada evento, cada mercado, tag, imagen y resultado resuelto que muestra el front-end. La API CLOB negocia; Gamma describe lo que se puede negociar. La mayoría de los bugs de bots en la capa de descubrimiento surgen por confundir ambas, o por perder el contrato de paginación. Este capítulo es la referencia de campo para los principales endpoints de Gamma, con el comportamiento exacto de parámetros del que dependen nuestros fetchers en producción.
- Gamma vs CLOB: cuándo usar cada uno
- Anatomía del endpoint /events
- Anatomía del endpoint /markets
- Tags e IDs de tags (lista verificada)
- Filtrado: active, closed, orden por volume24hr
- Paginación y límites
- Rate limits y caché
- Código: obtener los mercados con mayor volumen en 24 h
Gamma vs CLOB: cuándo usar cada uno
Dos servicios distintos para dos tareas distintas.
Gamma (gamma-api.polymarket.com): catálogo. Lista eventos, mercados, tags, descripciones, imágenes, resultados resueltos, volumen de 24 horas y volumen total. HTTP de solo lectura. No requiere autenticación para la mayoría de las lecturas. Se actualiza continuamente, pero eventualmente consistente: un mercado que acaba de cerrarse puede seguir mostrando closed: false durante unos segundos.
CLOB (clob.polymarket.com): trading + order book. Lista el mejor bid/ask actual, la profundidad top-N del libro, y trades recientes. Requiere autenticación para endpoints de escritura (colocación y cancelación de órdenes). Hay canales WebSocket en tiempo real disponibles para actualizaciones del libro.
Regla general: usa Gamma para encontrar qué tradear; usa CLOB para tradearlo. Un bot que lee precios desde Gamma está usando datos desactualizados: los campos de precio de Gamma se actualizan con menos frecuencia que el order book de CLOB. Un bot que lee metadata de mercado desde CLOB está haciendo más requests de los necesarios.
Anatomía del endpoint /events
GET /events devuelve datos a nivel de evento. Un "evento" es una página de Polymarket; un solo evento puede contener múltiples mercados (por ejemplo, el evento de la Elección Presidencial 2024 tiene un mercado por candidato).
Campos clave:
slug: identificador seguro para URL, estable durante la vida del evento.title,description: visualización para humanos.endDate(ISO 8601): cuándo cierra el evento.active,closed: booleanos; combínalos en una query con?active=true&closed=falsepara eventos en vivo.volume,volume24hr: totales en USD.tags: array de objetos tag (ver la sección de tags abajo).markets: array de objetos de mercados hijo (ver anatomía de/markets).
El patrón de descubrimiento más común: GET /events?active=true&closed=false&order=volume24hr&ascending=false&limit=100. Devuelve los 100 eventos activos con mayor volumen.
Anatomía del endpoint /markets
GET /markets devuelve datos a nivel de mercado. Un mercado es un contrato de Sí/No o de múltiples resultados; vive dentro de un evento.
Campos clave:
slug: identificador seguro para URL.question: el título que se muestra en la página de trading (por ejemplo, "Will Trump be president on January 1, 2027?").outcomes: string JSON con los nombres de los resultados, por ejemplo'["Yes","No"]'. Siempre tiene dos elementos para mercados binarios; más para NegRisk.outcomePrices: string JSON con los precios actuales como decimales, por ejemplo'["0.62","0.38"]'. Ambos lados suman ~1.0 menos el spread.clobTokenIds: string JSON con IDs de tokens ERC-1155 alineados con los resultados. Estos son los tokens que realmente compras/vendes.negRisk: booleano; true para mercados multi-outcome que suman 1. Importa para la colocación de órdenes (capítulo 11).
Los campos outcomes / outcomePrices / clobTokenIds llegan como strings JSON, no como arrays ya parseados: decodifica JSON antes de usarlos.
Tags e IDs de tags (lista verificada)
Los tags son etiquetas categóricas (Sports, Crypto, Tennis, NBA, etc.). Los IDs de tags verificados en producción para las categorías más usadas:
| Tag | ID | Tag | ID |
|---|---|---|---|
| Sports | 1 | NBA | 745 |
| Crypto | 21 | NFL | 450 |
| Politics | 2 | Tennis | 864 |
| Bitcoin | 100196 | Esports | 702 |
| Ethereum | 100383 | Soccer | 1059 |
| Election | 3 | EPL | 739 |
| Middle East | 1432 | UCL | 2186 |
Filtra por tag con ?tag_id=745 para un tag específico, o ?tag_slug=nba usando el slug. Filtrar por slug es más legible en código, pero un poco más lento; basarse en IDs es el default en producción.
Filtrado: active, closed, orden por volume24hr
Las cuatro dimensiones de filtrado que usarás el 95% del tiempo.
active=true|false:trueexcluye mercados que el equipo de Polymarket ha ocultado de la UI.closed=true|false:falseexcluye mercados resueltos. La combinaciónactive=true&closed=falsees el filtro live más común.order=volume24hr,order=volume,order=endDate: clave de ordenamiento. La más útil esvolume24hrpara encontrar mercados activos actualmente.ascending=true|false: por defecto es true en la mayoría de los endpoints; casi siempre querrásfalsepara ordenamientos por volumen.
Advertencia: filtrar por tag_id combinado con order=volume24hr y ascending=false a veces devuelve una página vacía cuando el tag tiene muy pocos mercados activos. Siempre solicita de más (pide más de lo que mostrarás) y post-filtra para manejar esto.
Paginación y límites
El parámetro limit acepta de 1 a 500 por llamada. El valor por defecto es 100 si no se especifica. Por encima de 500 el servidor recorta sin avisar: recibes 500, pero la respuesta no indica que haya más.
La paginación es por offset: ?limit=500&offset=500 para la segunda página. No hay paginación basada en cursor, así que las inserciones concurrentes pueden mover los límites de página entre llamadas. Para la mayoría de los propósitos de descubrimiento de un bot esto es aceptable; para scrapes de archivo, ordena por un campo estable (endDate o createdAt) y detecta solapamiento por slug.
Patrón práctico para "todos los mercados activos": haz fetch con limit=500&order=volume24hr&ascending=false. Eso cubre los 500 primeros por volumen, que en la práctica es casi todo mercado con actividad no trivial. Ir más allá de la página 1 rara vez es útil: los mercados en la cola de la distribución de volumen, por definición, no son donde está la acción.
Rate limits y caché
Gamma está detrás de Cloudflare y tiene soft rate limits por IP. Umbrales empíricos observados bajo carga de producción:
- Hasta ~30 req/sec sostenidas desde una IP: bien.
- Ráfagas de 100+ req/sec: ocasionalmente 429s, los reintentos funcionan.
- ~500 req/sec sostenidas: página de rate limit o bloqueo temporal (10-60s).
Los headers de respuesta publicados incluyen valores de Cache-Control de 30 a 60 segundos para la mayoría de los endpoints. Respétalos: no hay beneficio en volver a pedir el mismo evento 10 veces por minuto. Patrón de caché en producción: LRU en proceso + TTL, claveado por la URL completa, TTL de 30s. Ahorra requests y reduce latencia.
Para estrategias de alta frecuencia, replica los datos de Gamma en un store local actualizado por un único proceso fetcher; haz que múltiples consumers lean desde ese store. Un fetcher × muchos consumers > muchos fetchers × Gamma.
Código: obtener los mercados con mayor volumen en 24 h
Fetcher de referencia en tres lenguajes, devolviendo los 50 mercados activos con mayor volumen de 24 horas.
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'
El endpoint /events de Gamma de Polymarket NO soporta un parámetro de búsqueda por texto libre: agregar ?q=foo o ?search=foo devuelve silenciosamente el ordenamiento por defecto. Filtra por slug o por tag en su lugar.










