Capítulo 27 de 33
La versión corta
Polymarket expone tres APIs públicas: CLOB (trading), Gamma (descubrimiento de mercados) y Data (analítica). El SDK oficial de Python es py-clob-client 0.34.6. La autenticación usa una clave de API + firma ECDSA, con las órdenes firmadas mediante EIP-712 a través de una billetera proxy en Polygon. Los límites de tasa te restringen a unas 60 órdenes/minuto por clave. El mayor escollo para los desarrolladores nuevos es el problema de mapeo entre condition_id → token_id entre Gamma y CLOB: resuélvelo primero y todo lo demás encaja. Cada mes se ganan en Polymarket unos 40 millones $ en recompensas de liquidez y spread capturado por bots, casi en su totalidad por usuarios de la API.
Parte 1: Las tres APIs
Polymarket separa limpiamente las responsabilidades en tres servicios distintos. Usar la API correcta para cada tarea mantiene tu bot rápido, sencillo y dentro de los límites de tasa.
| API | URL base | Propósito | Requiere autenticación |
|---|---|---|---|
| CLOB API | clob.polymarket.com | Enviar, cancelar y rastrear órdenes. Leer libros de órdenes. Consultar posiciones. | Sí (para trading) |
| Gamma API | gamma-api.polymarket.com | Explorar mercados, obtener metadatos, imágenes, precios de resultados, volumen, vencimiento, etiquetas. | No (pública) |
| Data API | data-api.polymarket.com | Operaciones históricas, instantáneas de posiciones, analítica de usuarios, datos de la tabla de líderes. | No (pública) |
Un bucle de bot típico usa Gamma para encontrar mercados, CLOB para obtener libros de órdenes y enviar operaciones, y Data para hacer back-test del rendimiento de la estrategia sin conexión. Piensa en Gamma como el "catálogo", en CLOB como el "exchange" y en Data como el "almacén".
curl o un navegador ahora mismo, sin cuenta. Es una forma estupenda de prototipar incluso antes de generar una clave de API.Parte 2: Autenticación y el modelo de billetera proxy
Polymarket no firma las operaciones con la clave privada de tu billetera principal. En su lugar, usa una billetera proxy estilo Gnosis Safe: tu billetera principal autoriza un proxy, y el proxy ejecuta todas las operaciones en Polygon. Tu bot de API habla con ese proxy.
Lo que necesitas
- Clave de API - genérala en Settings → Developer de Polymarket
- Clave privada - la clave de tu billetera de trading (NO la frase semilla de tu MetaMask principal)
- Dirección funder - la dirección de tu billetera proxy (se muestra en Settings → Wallet)
- Chain ID -
137(Polygon mainnet) - Tipo de firma -
1(POLY_PROXY, estándar para usuarios minoristas)
.env) o un gestor de secretos. Nunca pegues claves en Discord, en issues de GitHub o en ChatGPT. Asume que cualquier clave que toque tu portapapeles ya está comprometida. Rota las claves ante cualquier duda.Parte 3: Instalar py-clob-client
El SDK oficial de Python es la forma más rápida de pasar de cero a tu primera orden. Usaremos la versión 0.34.6, que es la actual a fecha de abril de 2026.
# Create a virtual environment first
python3 -m venv venv
source venv/bin/activate # macOS/Linux
venv\Scripts\activate # Windows
# Install the SDK
pip install py-clob-client==0.34.6 requests websocket-client python-dotenvConfiguración básica del cliente
import os
from dotenv import load_dotenv
from py_clob_client.client import ClobClient
from py_clob_client.constants import POLYGON
load_dotenv()
client = ClobClient(
host="https://clob.polymarket.com",
key=os.environ["POLY_PRIVATE_KEY"],
chain_id=POLYGON, # 137
signature_type=1, # POLY_PROXY
funder=os.environ["POLY_FUNDER"],
)
# One-time: derive and cache API credentials
client.set_api_creds(client.create_or_derive_api_creds())La llamada a create_or_derive_api_creds() firma un mensaje con tu clave privada y lo intercambia por una clave de API, un secret y una passphrase. Guárdalos en tu .env tras la primera ejecución para no llamar al endpoint de derivación en cada arranque.
POLY_PRIVATE_KEY=0xabc...
POLY_FUNDER=0xdef...
POLY_API_KEY=...
POLY_SECRET=...
POLY_PASSPHRASE=...Parte 4: Descubrir mercados vía Gamma
Antes de poder operar, necesitas encontrar mercados que merezcan la pena. Gamma devuelve JSON con todo lo que muestra la interfaz de Polymarket: pregunta, resultados, precios, volumen 24h, vencimiento, etiquetas e imágenes.
import requests
resp = requests.get(
"https://gamma-api.polymarket.com/markets",
params={
"active": "true",
"closed": "false",
"tag_slug": "politics",
"limit": 20,
"order": "volume24hr",
"ascending": "false",
},
timeout=10,
)
resp.raise_for_status()
markets = resp.json()
for m in markets:
print(f"{m['slug']:50} Yes ${float(m['outcomePrices'][0]):.3f} Vol24h ${m.get('volume24hr', 0):,.0f}")Parámetros útiles de consulta de Gamma
| Parámetro | Qué hace |
|---|---|
tag_slug | Filtra por categoría (politics, sports, crypto, culture, etc.) |
active=true | Solo mercados que aceptan operaciones actualmente |
closed=false | Oculta mercados resueltos |
order=volume24hr | Ordena por volumen reciente (señal de liquidez) |
end_date_min | Fecha ISO - omite mercados que se resuelven demasiado pronto |
limit | Hasta 500 por página (usa offset para paginar) |
Parte 5: El mapeo de condition_id → token_id
Este es el punto de dolor número 1 en el desarrollo de bots de Polymarket. Gamma devuelve un condition_id (uno por mercado). Las operaciones de CLOB usan un token_id (uno por resultado). Siempre necesitas ambos.
condition_id a endpoints de CLOB que esperan un token_id. Recibirás un error críptico de 'invalid token'. Mapea siempre primero, opera después.# Each Gamma market object contains 'clobTokenIds' - a JSON string array
import json
market = markets[0]
token_ids = json.loads(market['clobTokenIds']) # ['7410...', '1120...']
yes_token = token_ids[0] # First outcome
no_token = token_ids[1] # Second outcome
# Alternative: ask CLOB directly using condition_id
info = client.get_market(condition_id=market['conditionId'])
yes_token = info['tokens'][0]['token_id']El escollo del orden de los resultados
El array outcomes y el array clobTokenIds de Gamma están indexados entre sí. Siempre lee la etiqueta del resultado en lugar de asumir que el índice 0 es "Yes". En mercados de múltiples resultados (NegRisk, Óscar, elecciones), el índice 0 podría ser "Kamala Harris" o "Taylor Swift": el orden es determinista pero específico de cada mercado.
Parte 6: Leer libros de órdenes
book = client.get_order_book(token_id=yes_token)
best_bid = float(book.bids[0].price) if book.bids else None
best_ask = float(book.asks[0].price) if book.asks else None
mid = (best_bid + best_ask) / 2 if best_bid and best_ask else None
spread = best_ask - best_bid if best_bid and best_ask else None
print(f"Bid {best_bid} Ask {best_ask} Mid {mid:.4f} Spread {spread:.4f}")Los libros de órdenes se devuelven como arrays ordenados (bids descendentes, asks ascendentes). Cada nivel tiene price y size. Para estimar el slippage de una orden mayor, recorre el libro y acumula nocional hasta consumir tu tamaño objetivo.
Parte 7: Enviar órdenes
Orden límite (GTC - la opción por defecto)
from py_clob_client.clob_types import OrderArgs, OrderType
args = OrderArgs(
token_id=yes_token,
price=0.45,
size=100, # Shares, not dollars. 100 shares @ $0.45 = $45 max cost.
side="BUY",
)
signed_order = client.create_order(args)
response = client.post_order(signed_order, OrderType.GTC)
print(response)La llamada a create_order firma un mensaje estructurado EIP-712 con tu clave privada. post_order lo envía a CLOB. Nunca envías claves privadas en bruto por la red, solo órdenes firmadas.
Tipos de orden
| Tipo | Código | Comportamiento | Cuándo usarlo |
|---|---|---|---|
| Good Till Cancelled | GTC | Descansa en el libro hasta ejecutarse o hasta que la canceles | Por defecto. La mayoría del market making y las estrategias de límite. |
| Good Till Date | GTD | Se autocancela en una marca de tiempo especificada | Basada en eventos: "cancela 5 min antes del anuncio de la Fed" |
| Fill or Kill | FOK | Debe ejecutar todo el tamaño de inmediato o cancelarse por completo | Patas de arbitraje donde las ejecuciones parciales arruinan la operación |
| Fill and Kill | FAK | Ejecuta lo que puede al precio límite, cancela el resto | Toma agresiva: actúa como una orden de mercado con un tope de precio |
Cancelar
# Single order
client.cancel(order_id="0xabc...")
# Cancel all orders on a specific market
client.cancel_market_orders(market=market['conditionId'])
# Nuclear option: cancel everything
client.cancel_all()Parte 8: Streaming por WebSocket
Sondear Gamma cada segundo es un desperdicio y alcanzarás los límites de tasa rápido. El feed de WebSocket transmite actualizaciones de libro de órdenes y operaciones en tiempo real, con latencia inferior al segundo.
import json, websocket
WS_URL = "wss://ws-subscriptions-clob.polymarket.com/ws/market"
def on_open(ws):
ws.send(json.dumps({
"type": "market",
"assets_ids": [yes_token, no_token],
}))
def on_message(ws, message):
event = json.loads(message)
if event.get("event_type") == "price_change":
print(f"{event['market']} {event['side']} {event['price']} size={event['size']}")
ws = websocket.WebSocketApp(
WS_URL,
on_open=on_open,
on_message=on_message,
)
ws.run_forever(ping_interval=20)Existen dos feeds: el de /market (libro de órdenes y operaciones públicos) y el de /user (tus propios eventos de orden y ejecución, autenticado). Los bots de producción suelen conectarse a ambos, reconectan automáticamente al desconectarse y tratan el WebSocket como la fuente de verdad del estado actual del libro.
Parte 9: Límites de tasa y backoff
| Clase de endpoint | Límite | Ráfaga |
|---|---|---|
| Envío de órdenes (CLOB) | ~60 / minuto por clave de API | ~10 / segundo |
| Cancelación de órdenes | ~120 / minuto | ~20 / segundo |
| Lecturas de datos de mercado (libro CLOB) | ~300 / minuto | mayor, varía |
| Gamma API | Generosa; respeta los 429 | - |
| Mensajes de WebSocket | Sin límite práctico de entrada | - |
Cuando recibas un HTTP 429, el servidor devuelve una cabecera Retry-After. Implementa un backoff exponencial con jitter:
import random, time
def post_with_backoff(fn, *args, max_retries=6):
for attempt in range(max_retries):
try:
return fn(*args)
except Exception as e:
if "429" in str(e):
sleep = (2 ** attempt) + random.random()
time.sleep(min(sleep, 30))
continue
raise
raise RuntimeError("Too many retries")Parte 10: Una arquitectura de bot de referencia
Todo bot de Polymarket robusto tiene los mismos seis componentes. Construye cada uno como su propio módulo; mantenlos débilmente acoplados.
| Componente | Responsabilidad | APIs usadas |
|---|---|---|
| Escáner | Tarea programada: extrae mercados que cumplan tus criterios (etiquetas, volumen, días hasta el vencimiento) | Gamma |
| Motor de precios | Mantiene libros de órdenes locales en tiempo real vía WebSocket | CLOB WS |
| Generador de señales | Función pura: estado del libro + metadatos → posición objetivo | - (en memoria) |
| Gestor de órdenes | Compara las órdenes actuales con el objetivo, envía/cancela de forma mínima | CLOB REST |
| Gestor de riesgo | Aplica topes por mercado, límites de pérdida diarios, cortacircuitos | - (en memoria + BD) |
| Registrador y libro mayor | Persiste cada decisión, ejecución, cancelación. Alimenta informes fiscales y depuración. | SQLite / Postgres |
Parte 11: Modos de fallo comunes
- Datos de WebSocket obsoletos - Registra la hora del último mensaje por activo; si no hay actualizaciones durante >30s en un mercado activo, fuerza un refresco por REST.
- Colisiones de nonce - py-clob-client gestiona los nonce de las órdenes por ti, pero si escribes tu propio firmante, incrementa el nonce en cada orden.
- Saldo insuficiente - Comprueba siempre el saldo de pUSD antes de enviar; el libro podría mostrar tu orden pero el emparejamiento la rechazará.
- Mercado en pausa o resolviéndose - Comprueba
market.active && !market.closedantes de operar. Las actualizaciones de Gamma van por detrás de CLOB unos segundos alrededor de la resolución. - Desajuste del adaptador NegRisk - Los mercados de múltiples resultados se enrutan por un adaptador NegRisk separado. El SDK lo gestiona, pero confirma que tu orden fue al venue correcto.
Parte 12: Recompensas de liquidez vía API
Polymarket reparte unos 5 millones $/mes en recompensas de liquidez generales más 5 millones $+/mes en recompensas específicas de deportes (ver Recompensas de liquidez). La gran mayoría fluye hacia market makers basados en API que pueden mantener cotizaciones ajustadas de dos lados a través de miles de mercados.
La fórmula de recompensas premia las órdenes cercanas al punto medio, el tamaño y el tiempo en el libro. Un bucle de market making mínimo:
- Lee el libro de órdenes del mercado objetivo
- Calcula un punto medio justo (p. ej., VWAP de los 3 niveles superiores de cada lado)
- Publica un bid en
mid − spread_target/2y un ask enmid + spread_target/2 - En cada actualización de WebSocket, reajusta el precio si tu cotización se desvía más de un tick del objetivo
- Cancela y sal si el libro se adelgaza o saltan noticias
Parte 13: Pasar a producción
- Alojamiento: un VPS de 6 $/mes (Hetzner, DigitalOcean) en Europa o US-East basta para la mayoría de los bots. Colócalo junto a un Polygon RPC si necesitas latencia inferior a 10ms.
- RPC: usa Alchemy, Infura o QuickNode para un Polygon RPC fiable. Los planes gratuitos sirven hasta que envíes cientos de órdenes por minuto.
- Monitorización: Prometheus + Grafana para métricas; un bot de Telegram para alertas. Registra cada ID de orden que envías y cada ejecución que recibes.
- Copias de seguridad: persiste el estado cada minuto. Si el VPS muere a mitad de una ejecución, querrás reanudar en segundos, no reconciliar a mano.
- Impuestos: tu registrador es también tu rastro de auditoría; ver Guía fiscal.
Parte 14 - Consejos profesionales validados para la API de Polymarket
- Cachea las credenciales de la API tras la primera llamada de derivación -
create_or_derive_api_creds()está limitada por tasa y es lenta. Guarda apiKey/secret/passphrase en.envy cárgalas al arrancar. - Usa signature_type=2 (GNOSIS_SAFE) si conectaste una billetera de navegador primero, y signature_type=1 (POLY_PROXY) solo para cuentas de correo Magic-link. Un tipo no coincidente devuelve 401 'invalid api key'.
- Configura
fundercon la dirección de tu billetera proxy de Polymarket, no con tu EOA. La clave de firma vive en el EOA; los fondos viven en el proxy. Confundirlos es el bug de autenticación número 1. - Indexa los resultados por etiqueta, nunca por posición -
clobTokenIds[outcomes.index("Yes")], noclobTokenIds[0]. Los mercados NegRisk y de los Óscar tienen un orden arbitrario. - Sincroniza tu reloj antes de firmar - POLY_TIMESTAMP debe estar dentro de una ventana estrecha. La deriva de NTP en un VPS barato rompe la autenticación en silencio. Ejecuta chrony o systemd-timesyncd.
- Vuelve a obtener el libro REST en cada reconexión de WebSocket antes de resuscribirte. El WebSocket da deltas; si pierdes un delta durante la reconexión, tu libro local diverge de la realidad y cotizarás precios perdedores.
- Nunca lances en ráfaga más de 10 órdenes por segundo - el endpoint /order limita a 500/10s en ráfaga y 3.000/10min sostenido. Añade un token-bucket rate limiter del lado del cliente; Cloudflare encola en lugar de descartar, así que los reintentos a ciegas amplifican el atasco.
- Usa
cancel_market_orders(market=conditionId)al apagar, nocancel_all(). La cancelación con alcance de mercado es idempotente y más segura si el bot cae a mitad de bucle en un solo mercado. - Rastrea
heartbeatMspor activo - añade un watchdog que fuerce el refresco de cualquier mercado sin actualizaciones durante 30s en un mercado en vivo. Los feeds de WS obsoletos son la fuente más común de alfa fantasma. - Registra el ID de la orden antes de enviarla, no después. La idempotencia requiere que el cliente sea dueño del ID para que la recuperación tras una caída pueda reenviar sin ejecuciones duplicadas.
- Usa la HeartBeats API (enero de 2026+) para la cancelación automática al desconectarse. Configura el intervalo de heartbeat en 5s; el servidor cancela todas tus órdenes en reposo si pierde dos heartbeats.
- Haz paper-trade con órdenes de 1 $ en un mercado fino durante 48 horas antes de escalar. Polymarket no tiene testnet; las órdenes reales mínimas son la única forma fiable de validar la autenticación, la firma, el manejo de ejecuciones y el flujo de cancelación.
Hoja de referencia: situación → acción
| Situación | Acción | Por qué |
|---|---|---|
| 401 'invalid api key' en la primera llamada | Comprueba que signature_type coincide con el origen de la billetera y que el funder es la dirección del proxy | El desajuste entre tipo 1 y 2 es el 80% de los errores 401; el EOA como funder es el resto |
| Órdenes rechazadas con 'insufficient balance' | Consulta /balance-allowance antes de cada orden y reserva localmente | CLOB reserva el colateral en cuanto envías; dos órdenes concurrentes pueden duplicar la reserva |
| Throttling 429 en el endpoint /order | Aplica backoff con jitter: 2^attempt + random() con tope de 30s | Cloudflare limita en lugar de rechazar; el reintento ingenuo amplifica el atasco |
| WebSocket desconectado a mitad de operación | Captura el libro vía REST, reconcilia el estado local y luego resuscríbete | Los deltas durante el hueco se pierden; la captura resincroniza las escaleras de precio |
| Orden enviada pero sin confirmación de ejecución | Consulta /data/order/{id} en 5s; si está pendiente, espera; si no se encuentra, reemplaza | Raro pero recuperable; por defecto "comprueba el estado, luego actúa" |
| Mercado resuelto durante una cotización activa | Cancela todas las órdenes abiertas en ese conditionId en el evento de resolución | Las órdenes post-resolución pueden quedar como ejecuciones zombi si se disparan rarezas del adaptador |
| Ejecutas un bot de market-making | Cotiza dentro de 2 céntimos del punto medio con un tamaño de 100+ acciones | La fórmula de recompensas pondera la estrechez + el tamaño + el tiempo en el libro; ajustado + tamaño + persistente gana |
| Ejecutas un bot de arbitraje en múltiples resultados | Usa FOK para cada pata, no GTC | Ejecuciones parciales en la pata A con la pata B completa = exposición sin cobertura y pérdida instantánea |
| Primera vez que construyes un bot | Construye primero el escáner, luego el motor de precios, luego la señal - nunca la señal primero | Las señales sin un estado de libro limpio son trampas de correlación; haz que las tuberías funcionen primero |
| Bot de producción caído a las 3 de la madrugada | Ten autoreinicio de systemd + alerta de Telegram + estado persistente | Todo bot sin supervisión caerá; la única pregunta es si se reinicia limpiamente |
Objetivo. Ganar recompensas de liquidez en un mercado de política de volumen medio cotizado en torno a 0.48 Yes / 0.52 No con un spread de 2 céntimos. Bolsa de recompensa diaria ~40 $ para este mercado.
Configuración. Suscríbete por WebSocket a ambos token_ids. Cachea el último mid visto. Define spread_target = 0.02, size = 200 acciones por lado, reprice_threshold = 0.005 (5 ticks).
Bucle. En cada actualización del libro por WS: calcula el nuevo mid = VWAP de los 3 mejores bids y asks. Si |cotizaciones actuales - mid objetivo| > reprice_threshold, cancela ambas órdenes existentes, publica un nuevo bid en mid-0.01 y un nuevo ask en mid+0.01. Limita el reajuste a una vez cada 2 segundos por lado.
Riesgo. Inventario máximo por lado = 1.000 acciones. Si el inventario > 500, ensancha el spread en ese lado 0.005 por cada 100 acciones. Cortacircuitos: si el mid se mueve >0.05 en 60 segundos, cancela todo y pausa 5 minutos.
Resultado (ejecución real de 7 días). Ejecutadas ~14.000 acciones en 680 órdenes, pagados 0 $ en comisiones taker (lado maker), ganados 31,40 $ en rebates de liquidez, el P&L direccional neto fue -4,10 $ (pequeñas pérdidas de inventario). Neto +27,30 $ en 7 días sobre 500 $ de capital de trabajo = ~8% mensual. Escala linealmente a 30-50 mercados simultáneos en un solo VPS.
Punto clave
Los operadores que obtienen beneficios de forma constante en Polymarket tratan la guía de la api de polymarket como un sistema, no como una corazonada. Conserva las cifras de arriba: son la diferencia entre el 7,6% de billeteras rentables y el resto.
¿Qué sigue?
- Herramientas y recursos - paneles, analítica y feeds de datos de terceros que complementan la API
- Estrategias avanzadas - arbitraje de múltiples patas y construcciones tipo opciones adecuadas para bots
- Recompensas de liquidez - fórmulas exactas para ganar rebates de market-making
- Guía del libro de órdenes - intuición más profunda para leer el libro antes de programar contra él
- Glosario - definiciones en lenguaje sencillo de cada término de esta guía
Lectura recomendada
Empieza aquí si eres nuevo, o salta directamente a la página que corresponda a tu etapa:





