Versión resumida
Polymarket te ofrece tres APIs públicas (una API permite que los programas lean datos o ejecuten operaciones automáticamente): CLOB (trading), Gamma (descubrimiento de mercados) y Data (análisis). El SDK oficial de Python es py-clob-client 0.34.6. La autenticación usa una clave de API + firma ECDSA, y las órdenes se firman mediante EIP-712 a través de una wallet proxy en Polygon. Los límites de tasa te restringen a unas 60 órdenes por minuto por clave. El principal obstáculo para los desarrolladores nuevos es el problema de asignación condition_id → token_id entre Gamma y CLOB. Resuélvelo primero y lo demás será mucho más sencillo. En Polymarket se generan aproximadamente 40 millones de dólares al mes en recompensas de liquidez y spread capturado por bots, casi todo obtenido por usuarios de la API.
Parte 1: Las tres APIs
Polymarket divide el trabajo de forma clara entre tres servicios independientes. Usar la API adecuada para cada tarea mantiene tu bot rápido, sencillo y dentro de los límites de tasa.
| API | Base URL | Purpose | Auth Required |
|---|---|---|---|
| CLOB API | clob.polymarket.com | Colocar, cancelar y seguir órdenes. Leer libros de órdenes. Consultar posiciones. | Sí (para operar) |
| Gamma API | gamma-api.polymarket.com | Explorar mercados, obtener metadatos, imágenes, precios de resultados, volumen, vencimiento y etiquetas. | No (pública) |
| Data API | data-api.polymarket.com | Operaciones históricas, instantáneas de posiciones, analítica de usuarios y datos de clasificación. | No (pública) |
Un ciclo típico de bot usa Gamma para encontrar mercados, CLOB para obtener libros de órdenes y ejecutar operaciones, y Data para hacer pruebas retrospectivas 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».
Parte 2: Autenticación y modelo de wallet proxy
Polymarket no firma operaciones con la clave privada de tu wallet principal. En su lugar, usa una wallet proxy de estilo Gnosis Safe. Tu wallet principal autoriza una proxy, y la proxy ejecuta todas las operaciones en Polygon. Tu bot de API se comunica con esa proxy.
Qué necesitas
- Clave de API - genérala en Settings → Developer de Polymarket
- Clave privada - la clave de tu wallet de trading (NO la frase semilla de tu MetaMask principal)
- Dirección funder - la dirección de tu wallet proxy (se muestra en Settings → Wallet)
- Chain ID -
137(red principal de Polygon) - signature_type -
1(POLY_PROXY, estándar para usuarios minoristas)
Parte 3: Instalación de py-clob-client
El SDK oficial de Python es la forma más rápida de pasar de cero a enviar tu primera orden. Usaremos la versión 0.34.6: la versión actual en PyPI (febrero de 2026) y la que ejecutan casi todos los bots en producción.
# 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 create_or_derive_api_creds() firma un mensaje con tu clave privada. A cambio obtiene una clave de API, un secreto y una frase de contraseña. Guárdalos en caché en tu .env después de la primera ejecución, para no llamar al endpoint de derivación en cada arranque.
Parte 4: Descubrir mercados mediante Gamma
Antes de poder operar, necesitas encontrar mercados en los que valga la pena hacerlo. Gamma devuelve JSON con todo lo que muestra la interfaz de Polymarket: la pregunta, los resultados, los precios, el volumen de 24 h, el vencimiento, las etiquetas y las 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 de consulta útiles de Gamma
| Parameter | What it does |
|---|---|
tag_slug | Filtra por categoría (política, deportes, cripto, cultura, etc.) |
active=true | Solo mercados que aceptan operaciones actualmente |
closed=false | Oculta mercados ya 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 principal punto de fricción al crear bots para Polymarket. Gamma devuelve un condition_id (uno por mercado). Las operaciones de CLOB usan un token_id (uno por resultado). Siempre necesitas ambos.
# 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']Detalle importante sobre el orden de los resultados
El array outcomes de Gamma y el array clobTokenIds están emparejados por índice. Lee siempre la etiqueta del resultado. No des por hecho que el índice 0 es "Yes". En mercados con varios resultados (NegRisk, Oscars, elecciones), el índice 0 podría ser "Kamala Harris" o "Taylor Swift". El orden es fijo, 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 en orden descendente, asks en orden ascendente). Cada nivel tiene un price y un size. Para estimar el deslizamiento en una orden más grande, recorre el libro y suma el nocional hasta completar el tamaño objetivo.
Parte 6b: Endpoints REST de CLOB v2 (directos, sin SDK)
El SDK los encapsula, pero conocer los endpoints directos te permite depurar, usar otro lenguaje o crear un cliente ligero. URL base: https://clob.polymarket.com. Todas las lecturas siguientes son públicas: no requieren autenticación. Se verificaron en vivo en junio de 2026.
| Endpoint | Method | What it returns |
|---|---|---|
/markets | GET | Todos los mercados (paginados mediante next_cursor). Incluye condition_id, tokens[], minimum_tick_size, neg_risk. |
/sampling-markets | GET | Solo mercados con un libro de órdenes activo: la forma más rápida de encontrar token_ids negociables. |
/book?token_id= | GET | Libro de órdenes completo: bids[] y asks[] con precio + tamaño. |
/price?token_id=&side=buy | GET | Mejor precio para un lado. side no distingue entre mayúsculas y minúsculas (buy/BUY). Devuelve {"price":"0.14"}. |
/midpoint?token_id= | GET | {"mid":"0.21"}: punto medio entre la mejor oferta de compra y la mejor oferta de venta. |
/spread?token_id= | GET | {"spread":"0.14"}: mejor oferta de venta menos mejor oferta de compra. |
/tick-size?token_id= | GET | {"minimum_tick_size":0.01}: el incremento de precio mínimo permitido para ese token. |
/prices-history?market=&interval= | GET | Puntos de precio históricos. interval = 1m,1h,6h,1d,1w,max. |
/trades | GET | Operaciones recientes (con autenticación para las tuyas; público para el mercado). |
/order | POST | Coloca una orden firmada (requiere autenticación). |
/order | DELETE | Cancela una orden por id (autenticación). |
/orders | GET | Tus órdenes abiertas pendientes (autenticación). |
/balance-allowance?asset_type= | GET | Tu saldo de USDC y tu allowance on-chain (autenticación). Compruébalo antes de cada orden. |
Respuestas verificadas, directamente desde la API en vivo:
$ curl "https://clob.polymarket.com/price?token_id=7347...&side=buy"
{"price":"0.14"}
$ curl "https://clob.polymarket.com/midpoint?token_id=7347..."
{"mid":"0.21"}
$ curl "https://clob.polymarket.com/spread?token_id=7347..."
{"spread":"0.14"}
$ curl "https://clob.polymarket.com/tick-size?token_id=7347..."
{"minimum_tick_size":0.01}Encabezados de autenticación L2 (para REST directo sin el SDK)
Los endpoints de lectura son públicos. Para colocar o cancelar órdenes mediante REST directo, debes firmar cada solicitud con tus credenciales de API. El SDK lo hace por ti; esto es lo que construye internamente:
| Header | What it carries |
|---|---|
POLY_ADDRESS | La dirección de tu wallet firmante |
POLY_API_KEY | La clave de API de create_or_derive_api_creds() |
POLY_PASSPHRASE | La passphrase de la misma llamada de derivación |
POLY_TIMESTAMP | Segundos UNIX actuales (deben coincidir con el reloj del servidor; consulta el consejo sobre sincronización de reloj) |
POLY_NONCE | Nonce por solicitud |
POLY_SIGNATURE | HMAC-SHA256 de timestamp + method + path + body, con tu secret de API como clave, codificado en base64-url |
Parte 7: Colocar órdenes: compra y venta
Orden limitada (GTC, el valor predeterminado)
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 create_order firma un mensaje estructurado EIP-712 con tu clave privada. Después, post_order lo envía a CLOB. Nunca envías claves privadas sin procesar por la red: solo órdenes firmadas.
Alinea primero el precio al tick
Todo precio de orden debe ser un múltiplo exacto del minimum_tick_size del mercado (0,01 en la mayoría de los mercados; 0,001 en los de tick más estrecho). Un precio fuera de tick se rechaza. Obtén el tick una vez y redondea a ese incremento:
from py_clob_client.clob_types import OrderArgs, OrderType
tick = float(client.get_tick_size(token_id=yes_token)) # e.g. 0.01
def to_tick(p, tick): return round(round(p / tick) * tick, 4)
price = to_tick(0.453, tick) # -> 0.45 on a 0.01 marketCompra
side="BUY", size está en acciones (no en dólares). 100 acciones a 0,45 USD cuestan como máximo 45 USD y pagan 100 USD si el resultado gana. El valor mínimo de la orden es de aproximadamente 1 USD.
buy = OrderArgs(token_id=yes_token, price=to_tick(0.45, tick), size=100, side="BUY")
resp = client.post_order(client.create_order(buy), OrderType.GTC)
print(resp) # {'success': True, 'orderID': '0x...', 'status': 'live', ...}Venta
Vender usa la misma llamada con side="SELL". Solo puedes vender participaciones que ya tienes: si intentas vender más de tu posición, la orden se rechaza con un error "insufficient balance". Para cerrar una posición, vende el mismo token_id que compraste.
sell = OrderArgs(token_id=yes_token, price=to_tick(0.62, tick), size=100, side="SELL")
resp = client.post_order(client.create_order(sell), OrderType.GTC)Resumen de parámetros de la orden
| Field | Meaning | Notes |
|---|---|---|
token_id | El resultado con el que operas | No condition_id; consulta la Parte 5 |
side | BUY o SELL | BUY requiere USDC; SELL requiere participaciones |
price | 0.001-0.999 | Debe ser múltiplo del tamaño de tick |
size | Número de participaciones | Valor mínimo de la orden: ~1 USD; costo = price x size |
| tipo de orden | GTC / GTD / FOK / FAK | Se pasa a post_order(...) |
Todo junto: tu primera operación vía API (un único script ejecutable)
Este es el flujo completo de principio a fin: conectarte, encontrar un mercado líquido, leer el libro, ajustar al tick, colocar una orden real pequeña y luego cancelarla. Introduce tus dos secretos y ejecútalo. En tu primera ejecución, empieza con un tamaño muy pequeño (unos pocos dólares).
import os, json, requests
from dotenv import load_dotenv
from py_clob_client.client import ClobClient
from py_clob_client.constants import POLYGON
from py_clob_client.clob_types import OrderArgs, OrderType
load_dotenv()
# 1) Connect (signing key in your EOA, funds in your proxy/funder)
client = ClobClient(
"https://clob.polymarket.com",
key=os.environ["POLY_PRIVATE_KEY"],
chain_id=POLYGON, # 137
signature_type=1, # 1 = email/Magic proxy, 2 = browser-wallet proxy
funder=os.environ["POLY_FUNDER"],
)
client.set_api_creds(client.create_or_derive_api_creds()) # cache these after first run
# 2) Find the most-traded open market (Gamma, no auth)
m = requests.get(
"https://gamma-api.polymarket.com/markets",
params={"active": "true", "closed": "false", "order": "volume24hr",
"ascending": "false", "limit": 1}, timeout=10,
).json()[0]
token_id = json.loads(m["clobTokenIds"])[0] # index 0 = first outcome (read the label!)
print("Trading:", m["question"])
# 3) Read the book + the tick size
tick = float(client.get_tick_size(token_id))
book = client.get_order_book(token_id)
best_ask = float(book.asks[0].price)
print("best ask", best_ask, "| tick", tick)
# 4) Place a small BUY at the ask (tiny size to start)
price = round(round(best_ask / tick) * tick, 4) # snap to tick
order = OrderArgs(token_id=token_id, price=price, size=5, side="BUY") # 5 shares
resp = client.post_order(client.create_order(order), OrderType.GTC)
print(resp) # {'success': True, 'orderID': '0x...', ...}
# 5) Cancel it (clean up)
# client.cancel(order_id=resp["orderID"])Tipos de orden
| Type | Code | Behaviour | When to use |
|---|---|---|---|
| Válida hasta cancelación | GTC | Permanece en el libro hasta ejecutarse o hasta que la canceles | Opción predeterminada. La mayoría de estrategias de creación de mercado y de órdenes limitadas. |
| Válida hasta una fecha | GTD | Se cancela automáticamente en un timestamp especificado | Basada en eventos: "cancelar 5 min antes de la publicación de la Fed" |
| Todo o nada | FOK | Debe ejecutar el tamaño completo de inmediato o cancelarse por completo | Tramos de arbitraje en los que las ejecuciones parciales arruinan la operación |
| Ejecutar y cancelar | FAK | Ejecuta todo lo que pueda al precio límite y cancela el resto | Toma agresiva de liquidez: actúa como una orden de mercado con precio tope |
Cancelación
# 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 con WebSocket
Consultar Gamma cada segundo es ineficiente, y alcanzarás los límites de frecuencia rápidamente. El feed WebSocket transmite actualizaciones del libro de órdenes y de operaciones en tiempo real con latencia inferior a un 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)Hay dos feeds. El feed /market incluye el libro de órdenes público y las operaciones. El feed /user incluye tus propios eventos de órdenes y ejecuciones (autenticado). Los bots de producción se conectan a ambos, se reconectan automáticamente al desconectarse y tratan WebSocket como la fuente de referencia para el libro actual.
Parte 9: límites de frecuencia y backoff
| Endpoint class | Limit | Burst |
|---|---|---|
| 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 | Generosos; respeta los 429 | - |
| Mensajes WebSocket | Sin límite práctico de entrada | - |
Cuando recibes un HTTP 429, el servidor devuelve un encabezado Retry-After. Usa 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: arquitectura de referencia para un bot
Todo bot fiable de Polymarket tiene los mismos seis componentes. Crea cada uno como su propio módulo y mantenlos poco acoplados.
| Component | Responsibility | APIs used |
|---|---|---|
| Escáner | Tarea programada: obtiene mercados que coinciden con tus criterios (etiquetas, volumen, días hasta el vencimiento) | Gamma |
| Motor de precios | Mantiene libros de órdenes locales en tiempo real mediante 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 y coloca/cancela lo mínimo necesario | CLOB REST |
| Gestor de riesgos | Aplica límites por mercado, límites de pérdida diaria y cortacircuitos | - (en memoria + DB) |
| Registro y libro contable | Persiste cada decisión, ejecución y cancelación. Sirve de base para informes fiscales y depuración. | SQLite / Postgres |
Parte 11: Modos de fallo habituales
- Rechazo por precio fuera del tick - el precio debe ser un múltiplo exacto del
minimum_tick_sizedel mercado. Consúltalo mediante/tick-size?token_id=y redondea antes de firmar, o la orden será rechazada. - 404 "No orderbook exists" - consultaste
/book,/priceo/midpointen un token cerrado/resuelto. Usa/sampling-marketspara encontrar tokens con un libro activo. - Datos obsoletos de WebSocket - registra la hora del último mensaje por activo; si no hay actualizaciones durante >30 s en un mercado activo, fuerza una actualización por REST.
- Colisiones de nonce - py-clob-client gestiona los nonces de las órdenes por ti, pero si estás implementando tu propio firmador, incrementa el nonce en cada orden.
- Saldo insuficiente - comprueba siempre el saldo de USDC antes de colocar una orden; el libro podría mostrar tu orden, pero el emparejamiento la rechazará.
- Mercado pausado o en resolución - comprueba
market.active && !market.closedantes de operar. Las actualizaciones de Gamma van unos segundos por detrás de CLOB alrededor de la resolución. - Incompatibilidad con el adaptador NegRisk - los mercados con varios resultados se enrutan mediante un adaptador NegRisk independiente. El SDK lo gestiona, pero confirma que tu orden llegó al venue correcto.
Parte 12: Recompensas de liquidez mediante API
Polymarket reparte ~5 millones de dólares al mes en recompensas generales de liquidez, además de más de 5 millones de dólares al mes en recompensas específicas de deportes (consulta Liquidity Rewards). La mayor parte va a creadores de mercado que operan mediante API. Mantienen cotizaciones ajustadas en ambos lados en miles de mercados.
La fórmula de recompensas favorece las órdenes cercanas al punto medio, con tamaño y tiempo en el libro. Este es un bucle mínimo de creación de mercado:
- Lee el libro de órdenes del mercado objetivo
- Calcula un punto medio justo (por ejemplo, el VWAP de los 3 primeros niveles de cada lado)
- Publica una bid en
mid − spread_target/2y una ask enmid + spread_target/2 - En cada actualización de WebSocket, reajusta el precio si tu cotización se desvía del objetivo en más de un tick
- Cancela y sal si el libro pierde profundidad o aparece una noticia relevante
Parte 13: Paso a producción
- Hosting: un VPS de 6 dólares al mes (Hetzner, DigitalOcean) en Europa o US-East es suficiente para la mayoría de bots. Colócalo cerca del RPC de Polygon si necesitas latencia inferior a 10 ms.
- RPC: usa Alchemy, Infura o QuickNode para un RPC de Polygon fiable. Los planes gratuitos bastan hasta que coloques 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íes y cada ejecución que recibas.
- Copias de seguridad: persiste el estado cada minuto. Si el VPS falla en mitad de una ejecución, querrás reanudar en segundos, no reconciliar a mano.
- Impuestos: tu registro también es tu rastro de auditoría; consulta Tax Guide.
Parte 14 - Consejos profesionales validados para la API de Polymarket
Chuleta de situación → acción
| Situation | Action | Why |
|---|---|---|
| 401 "invalid api key" en la primera llamada | Comprueba que signature_type coincida con el origen de la wallet y que funder sea la dirección del proxy | La discrepancia entre Type 1 y Type 2 causa el 80 % de los errores 401; el resto se debe a usar una EOA como funder |
| Órdenes rechazadas con "insufficient balance" | Consulta /balance-allowance antes de cada orden y reserva localmente | CLOB reserva el colateral en cuanto publicas la orden; dos órdenes simultáneas pueden reservar los mismos fondos dos veces |
| Limitación 429 en el endpoint /order | Aplica backoff con jitter: 2^attempt + random(), con límite de 30 s | Cloudflare limita el ritmo en vez de rechazar; un reintento ingenuo amplifica la cola acumulada |
| WebSocket desconectado en mitad de una operación | Obtén una instantánea del libro por REST, concilia el estado local y vuelve a suscribirte | Los deltas durante la interrupción se pierden; la instantánea vuelve a sincronizar los niveles de precios |
| Orden enviada, pero sin confirmación de ejecución | Consulta /data/order/{id} en un plazo de 5 s; si está pendiente, espera; si no se encuentra, reemplázala | Es poco frecuente, pero recuperable; por defecto, comprueba el estado y luego actúa |
| Mercado resuelto mientras había una cotización activa | Cancela todas las órdenes abiertas de ese conditionId al recibir el evento de resolución | Las órdenes posteriores a la resolución pueden quedar como ejecuciones zombi si se activan peculiaridades del adaptador |
| Bot de creación de mercado en ejecución | Cotiza dentro de un rango de 2 centavos respecto del punto medio, con un tamaño de 100 o más participaciones | La fórmula de recompensas pondera cercanía + tamaño + tiempo en el libro; gana cotizar ajustado, con tamaño y de forma persistente |
| Bot de arbitraje en mercados de varios resultados | Usa FOK para cada tramo, no GTC | Ejecuciones parciales en el tramo A con un tramo B completo = exposición sin cobertura y pérdida inmediata |
| Primera vez que construyes un bot | Construye primero el escáner, luego el motor de precios y después la señal; nunca empieces por la señal | Las señales sin un estado limpio del libro son trampas de correlación; haz que primero funcione el flujo de datos |
| El bot de producción se cayó a las 3:00 | Configura reinicio automático con systemd + alerta de Telegram + estado persistente | Cualquier bot no supervisado acabará fallando; la única pregunta es si se reinicia limpiamente |
¿Qué sigue?
- Herramientas y recursos - paneles de terceros, analítica y feeds de datos que complementan la API
- Estrategias avanzadas - arbitraje multipata y construcciones similares a opciones adecuadas para bots
- Recompensas de liquidez - fórmulas exactas para ganar reembolsos de creación de mercado
- Guía del libro de órdenes - intuición más profunda para leer el libro antes de integrarte con él
- Glosario - definiciones en lenguaje sencillo de todos los términos de esta guía











