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.

APIBase URLPurposeAuth Required
CLOB APIclob.polymarket.comColocar, cancelar y seguir órdenes. Leer libros de órdenes. Consultar posiciones.Sí (para operar)
Gamma APIgamma-api.polymarket.comExplorar mercados, obtener metadatos, imágenes, precios de resultados, volumen, vencimiento y etiquetas.No (pública)
Data APIdata-api.polymarket.comOperaciones 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-dotenv

Configuració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

ParameterWhat it does
tag_slugFiltra por categoría (política, deportes, cripto, cultura, etc.)
active=trueSolo mercados que aceptan operaciones actualmente
closed=falseOculta mercados ya resueltos
order=volume24hrOrdena por volumen reciente (señal de liquidez)
end_date_minFecha ISO: omite mercados que se resuelven demasiado pronto
limitHasta 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.

EndpointMethodWhat it returns
/marketsGETTodos los mercados (paginados mediante next_cursor). Incluye condition_id, tokens[], minimum_tick_size, neg_risk.
/sampling-marketsGETSolo mercados con un libro de órdenes activo: la forma más rápida de encontrar token_ids negociables.
/book?token_id=GETLibro de órdenes completo: bids[] y asks[] con precio + tamaño.
/price?token_id=&side=buyGETMejor 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=GETPuntos de precio históricos. interval = 1m,1h,6h,1d,1w,max.
/tradesGETOperaciones recientes (con autenticación para las tuyas; público para el mercado).
/orderPOSTColoca una orden firmada (requiere autenticación).
/orderDELETECancela una orden por id (autenticación).
/ordersGETTus órdenes abiertas pendientes (autenticación).
/balance-allowance?asset_type=GETTu 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:

HeaderWhat it carries
POLY_ADDRESSLa dirección de tu wallet firmante
POLY_API_KEYLa clave de API de create_or_derive_api_creds()
POLY_PASSPHRASELa passphrase de la misma llamada de derivación
POLY_TIMESTAMPSegundos UNIX actuales (deben coincidir con el reloj del servidor; consulta el consejo sobre sincronización de reloj)
POLY_NONCENonce por solicitud
POLY_SIGNATUREHMAC-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 market

Compra

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

FieldMeaningNotes
token_idEl resultado con el que operasNo condition_id; consulta la Parte 5
sideBUY o SELLBUY requiere USDC; SELL requiere participaciones
price0.001-0.999Debe ser múltiplo del tamaño de tick
sizeNúmero de participacionesValor mínimo de la orden: ~1 USD; costo = price x size
tipo de ordenGTC / GTD / FOK / FAKSe 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

TypeCodeBehaviourWhen to use
Válida hasta cancelaciónGTCPermanece en el libro hasta ejecutarse o hasta que la cancelesOpción predeterminada. La mayoría de estrategias de creación de mercado y de órdenes limitadas.
Válida hasta una fechaGTDSe cancela automáticamente en un timestamp especificadoBasada en eventos: "cancelar 5 min antes de la publicación de la Fed"
Todo o nadaFOKDebe ejecutar el tamaño completo de inmediato o cancelarse por completoTramos de arbitraje en los que las ejecuciones parciales arruinan la operación
Ejecutar y cancelarFAKEjecuta todo lo que pueda al precio límite y cancela el restoToma 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 classLimitBurst
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 / minutomayor, varía
Gamma APIGenerosos; respeta los 429-
Mensajes WebSocketSin 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.

ComponentResponsibilityAPIs used
EscánerTarea programada: obtiene mercados que coinciden con tus criterios (etiquetas, volumen, días hasta el vencimiento)Gamma
Motor de preciosMantiene libros de órdenes locales en tiempo real mediante WebSocketCLOB WS
Generador de señalesFunción pura: estado del libro + metadatos → posición objetivo- (en memoria)
Gestor de órdenesCompara las órdenes actuales con el objetivo y coloca/cancela lo mínimo necesarioCLOB REST
Gestor de riesgosAplica límites por mercado, límites de pérdida diaria y cortacircuitos- (en memoria + DB)
Registro y libro contablePersiste 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_size del mercado. Consúltalo mediante /tick-size?token_id= y redondea antes de firmar, o la orden será rechazada.
  • 404 "No orderbook exists" - consultaste /book, /price o /midpoint en un token cerrado/resuelto. Usa /sampling-markets para 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.closed antes 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:

  1. Lee el libro de órdenes del mercado objetivo
  2. Calcula un punto medio justo (por ejemplo, el VWAP de los 3 primeros niveles de cada lado)
  3. Publica una bid en mid − spread_target/2 y una ask en mid + spread_target/2
  4. En cada actualización de WebSocket, reajusta el precio si tu cotización se desvía del objetivo en más de un tick
  5. 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

SituationActionWhy
401 "invalid api key" en la primera llamadaComprueba que signature_type coincida con el origen de la wallet y que funder sea la dirección del proxyLa 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 localmenteCLOB 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 /orderAplica backoff con jitter: 2^attempt + random(), con límite de 30 sCloudflare limita el ritmo en vez de rechazar; un reintento ingenuo amplifica la cola acumulada
WebSocket desconectado en mitad de una operaciónObtén una instantánea del libro por REST, concilia el estado local y vuelve a suscribirteLos 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ónConsulta /data/order/{id} en un plazo de 5 s; si está pendiente, espera; si no se encuentra, reemplázalaEs poco frecuente, pero recuperable; por defecto, comprueba el estado y luego actúa
Mercado resuelto mientras había una cotización activaCancela todas las órdenes abiertas de ese conditionId al recibir el evento de resoluciónLas ó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ónCotiza dentro de un rango de 2 centavos respecto del punto medio, con un tamaño de 100 o más participacionesLa 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 resultadosUsa FOK para cada tramo, no GTCEjecuciones parciales en el tramo A con un tramo B completo = exposición sin cobertura y pérdida inmediata
Primera vez que construyes un botConstruye primero el escáner, luego el motor de precios y después la señal; nunca empieces por la señalLas 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:00Configura reinicio automático con systemd + alerta de Telegram + estado persistenteCualquier bot no supervisado acabará fallando; la única pregunta es si se reinicia limpiamente

¿Qué sigue?

Comprobación rápida