La API WebSocket de Polymarket es la capa de streaming que envía datos de mercado (libros de órdenes, precios, trades) y datos privados de la cuenta (órdenes, posiciones, saldos) a un cliente conectado en lugar de requerir sondeos REST repetidos. Se organiza en endpoints separados de mercado y privados, cada uno con sus propios tipos de suscripción y requisitos de autenticación. Este artículo explica el modelo mental, muestra código de cliente ejecutable, describe cómo verificar que un stream está realmente activo y cubre los modos de fallo que importan en producción. Las elecciones concretas de endpoint y proveedor corresponden a RPC Assistant; esta página se centra en cómo funciona la integración de streaming y cómo medirla.
Para qué sirve la API WebSocket de Polymarket
La API WebSocket de Polymarket proporciona datos en streaming en tiempo real para información de mercado y datos privados de usuario. En lugar de sondear endpoints REST con un temporizador, un cliente abre una conexión persistente y se suscribe a los streams que le interesan. El servidor luego envía actualizaciones a medida que ocurren. Este es el modelo adecuado cuando se necesitan cambios en el libro de órdenes, notificaciones de trades o cambios en el estado de la cuenta con baja latencia.
La API se divide en dos superficies lógicas. Un stream de markets transporta datos públicos como instantáneas completas del libro de órdenes, datos de precios ligeros y trades. Un stream private transporta datos específicos de la cuenta como actualizaciones de órdenes, cambios de posiciones y actualizaciones de saldos. El stream privado requiere autenticación; el stream de markets es público en el modelo documentado de la API de EE. UU. pero aún puede requerir cabeceras de clave API según el despliegue con el que se integre.
Este artículo asume que ya tiene una clave API de Polymarket o credenciales del SDK, conoce los slugs de mercado o identificadores de token que desea rastrear y puede ejecutar un cliente WebSocket en Node.js o Python. No asume ningún proveedor de hosting específico. Si aún está eligiendo infraestructura para un bot sensible a la latencia, la descripción general del servicio de endpoints RPC y las opciones de nodo dedicado son los puntos de partida relevantes.
- Use WebSocket cuando necesite actualizaciones continuas del libro de órdenes o de trades.
- Use REST para consultas puntuales, datos históricos y reconciliación.
- Mantenga las suscripciones privadas y públicas en conexiones separadas.
Modelo de conexión y estructura de endpoints
Los despliegues documentados de WebSocket de Polymarket exponen rutas separadas para streams privados y de mercado. La documentación de EE. UU. describe endpoints bajo wss://api.polymarket.us/v1/ws/private y wss://api.polymarket.us/v1/ws/markets. Otras documentaciones y referencias de la comunidad describen un endpoint de suscripción CLOB en wss://ws-subscriptions-clob.polymarket.com/ws. Trate el host y la ruta exactos como específicos del despliegue y confírmelos con la documentación del entorno al que se dirige antes de escribir código de producción.
El punto arquitectónico importante no es la URL literal sino la separación de responsabilidades. Los streams privados se autentican por usuario y transportan eventos de órdenes, posiciones y saldos. Los streams de mercado son compartidos y transportan eventos de libro, precios y trades. Mezclarlos en una sola conexión complica la autenticación, el manejo de errores y la lógica de reconexión. Manténgalos separados.
# Conceptual connection layout (confirm exact hosts in current docs)
# Private stream: orders, positions, balances
wss://api.polymarket.us/v1/ws/private
# Market stream: order book, prices, trades
wss://api.polymarket.us/v1/ws/markets
# Handshake headers for authenticated streams
X-PM-Access-Key: <your-api-key-id>
X-PM-Timestamp: <timestamp-in-milliseconds>
X-PM-Signature: <base64-encoded-signature>Autenticación y el handshake firmado
Las conexiones WebSocket privadas utilizan el mismo modelo de autenticación con clave API que la API REST. El enfoque documentado pasa tres cabeceras durante el handshake de WebSocket: un ID de clave de acceso, una marca de tiempo en milisegundos y una firma codificada en base64. La firma se construye a partir de la marca de tiempo, el método HTTP y la ruta de la solicitud. Para el stream privado el método es GET y la ruta es /v1/ws/private; para el stream de markets la ruta es /v1/ws/markets.
Dado que la firma incluye una marca de tiempo, la desviación del reloj es un modo de fallo real. Si el reloj de su host se desvía, el servidor puede rechazar el handshake aunque su clave sea válida. Mantenga la máquina sincronizada con NTP y registre la marca de tiempo que envió junto con cualquier fallo de handshake. La firma debe calcularse sobre la ruta exacta a la que se conecta, por lo que una barra final o una ruta de host diferente producirá una discrepancia.
- Firme
timestamp + "GET" + path, no la URL completa. - Use marcas de tiempo en milisegundos y mantenga el reloj del host sincronizado.
- Nunca registre la clave secreta ni la firma completa en los logs de producción.
Tipos de suscripción y formato de mensaje
Todos los mensajes WebSocket son JSON con nombres de campo en snake_case. Una solicitud de suscripción incluye un request_id único, un subscription_type numérico y los slugs de mercado o identificadores que desea rastrear. Los tipos de suscripción privados documentados son actualizaciones de órdenes, cambios de posiciones y actualizaciones de saldo de cuenta. Los tipos de suscripción de mercado documentados son datos completos de mercado (libro de órdenes y estadísticas), datos de precios ligeros y notificaciones de trades en tiempo real.
Las respuestas hacen eco del request_id y subscription_type para que pueda correlacionar una respuesta con la solicitud que la produjo. Las respuestas de instantánea incluyen un flag eof para indicar el final de la instantánea inicial. Si una suscripción falla, la respuesta incluye un campo error. La cancelación de suscripción utiliza el request_id original, por lo que los IDs únicos importan: son la única forma fiable de cancelar un stream específico sin derribar la conexión.
{
"subscribe": {
"request_id": "book-btc-100k-001",
"subscription_type": 1,
"market_slugs": ["btc-100k-2025"]
}
}Un cliente mínimo en Node.js que verifica el stream
El siguiente cliente se conecta al stream de mercado, se suscribe a datos completos de mercado e imprime los primeros mensajes. Es intencionalmente pequeño para que pueda confirmar la conectividad y la forma de los mensajes antes de construir un pipeline de ingesta completo. Reemplace el host y la ruta con los valores de la documentación para su entorno, y reemplace el slug con un mercado que realmente desee rastrear.
Ejecútelo y observe una respuesta que haga eco de su request_id. Si ve un mensaje de heartbeat pero ninguna respuesta de suscripción, es probable que su payload de suscripción esté mal formado. Si la conexión se cierra inmediatamente, verifique las cabeceras del handshake y la ruta del host.
import WebSocket from "ws";
const WS_URL = process.env.POLYMARKET_WS_MARKETS; // e.g. wss://api.polymarket.us/v1/ws/markets
const MARKET_SLUG = process.env.MARKET_SLUG || "btc-100k-2025";
const ws = new WebSocket(WS_URL, {
headers: {
"X-PM-Access-Key": process.env.POLYMARKET_KEY_ID,
"X-PM-Timestamp": String(Date.now()),
"X-PM-Signature": process.env.POLYMARKET_SIGNATURE,
},
});
let received = 0;
ws.on("open", () => {
console.log("connected");
ws.send(JSON.stringify({
subscribe: {
request_id: "book-" + MARKET_SLUG,
subscription_type: 1,
market_slugs: [MARKET_SLUG],
},
}));
});
ws.on("message", (raw) => {
const msg = JSON.parse(raw.toString());
if (msg.heartbeat) {
console.log("heartbeat");
return;
}
received += 1;
console.log("message", received, Object.keys(msg));
if (received >= 5) ws.close();
});
ws.on("error", (err) => console.error("ws error", err.message));
ws.on("close", (code, reason) => console.log("closed", code, reason.toString()));Un cliente en Python para eventos de libro de órdenes y trades
Los clientes en Python siguen el mismo patrón: conectar, enviar un payload de suscripción y manejar mensajes por tipo. El ejemplo siguiente utiliza la librería websockets y demuestra las dos cosas que la mayoría de las implementaciones hacen mal: responder a los heartbeats y distinguir los mensajes de instantánea de las actualizaciones incrementales.
El flag eof en una respuesta de instantánea le indica cuándo ha terminado de cargarse el estado inicial. Solo después de eof debe tratar los mensajes posteriores como deltas incrementales. Si aplica deltas antes de que se complete la instantánea, su libro de órdenes local será inconsistente.
import asyncio, json, os
import websockets
WS_URL = os.environ["POLYMARKET_WS_MARKETS"]
SLUG = os.environ.get("MARKET_SLUG", "btc-100k-2025")
async def main():
headers = {
"X-PM-Access-Key": os.environ["POLYMARKET_KEY_ID"],
"X-PM-Timestamp": str(int(asyncio.get_event_loop().time() * 1000)),
"X-PM-Signature": os.environ["POLYMARKET_SIGNATURE"],
}
async with websockets.connect(WS_URL, extra_headers=headers) as ws:
await ws.send(json.dumps({
"subscribe": {
"request_id": f"book-{SLUG}",
"subscription_type": 1,
"market_slugs": [SLUG],
}
}))
async for raw in ws:
msg = json.loads(raw)
if "heartbeat" in msg:
continue
if "error" in msg:
print("subscription error:", msg["error"])
break
print("keys:", list(msg.keys()))
asyncio.run(main())Verificar que el stream está realmente activo
Una conexión que permanece abierta no es prueba de que los datos estén fluyendo. Para verificar un stream, registre un pequeño conjunto de señales observables durante una ventana fija y compárelas con las expectativas. La tabla siguiente es un procedimiento, no un conjunto de resultados medidos: complete la columna derecha con su propia ejecución. No publique números que no haya medido.
| Señal | Cómo capturarla | Comportamiento esperado | Su resultado |
|---|---|---|---|
| Éxito del handshake | Registrar evento de conexión abierta | Se abre sin error | (completar) |
| Confirmación de suscripción | Coincidir request_id en la respuesta | Eco con el mismo tipo | (completar) |
| Finalización de instantánea | Observar eof: true | Llega antes que los deltas | (completar) |
| Cadencia de heartbeat | Marcar cada heartbeat | Intervalo regular | (completar) |
| Tasa de mensajes | Contar mensajes por minuto | Distinta de cero durante mercado activo | (completar) |
| Recuperación tras reconexión | Matar conexión, observar | Se resuscribe y reanuda | (completar) |
Ejecute este procedimiento contra un mercado que esté operando activamente. Un mercado tranquilo puede producir largos intervalos entre mensajes, lo que parece un stream muerto pero no lo es. Si necesita una línea base para comparar, ejecute el mismo procedimiento en dos momentos del día diferentes y registre ambos.
Reconexión, heartbeats y recuperación de estado
El servidor envía mensajes de heartbeat periódicos para mantener viva la conexión. Los clientes deben responder a los heartbeats o implementar su propio mecanismo de keep-alive. En la práctica, el diseño más seguro es tratar cada desconexión como un reinicio completo del estado: reconectar, resuscribirse, esperar una nueva instantánea y solo entonces reanudar la aplicación de deltas. Intentar parchear un libro de órdenes local obsoleto después de una reconexión es una fuente común de liquidez fantasma.
La reconexión debe usar backoff exponencial con jitter. Un bucle de reconexión ajustado contra un endpoint con límite de tasa empeorará una interrupción. Registre el código de cierre y el motivo de cada desconexión para poder distinguir fallos de red de fallos de autenticación y errores de suscripción.
- Reinicie el estado local en cada reconexión.
- Espere a
eofantes de aplicar deltas. - Aplique backoff con jitter; limite el retraso máximo.
- Alerte sobre fallos de autenticación repetidos por separado de los fallos de red.
Solución de problemas comunes
La mayoría de los problemas de integración se engloban en un pequeño número de categorías. Un handshake que falla inmediatamente suele indicar una discrepancia en la firma, una marca de tiempo obsoleta o una ruta incorrecta. Una conexión que se abre pero nunca recibe una respuesta de suscripción suele indicar un payload mal formado o un tipo de suscripción no soportado. Un stream que entrega una instantánea pero no actualizaciones suele significar que el mercado está inactivo o que la suscripción está limitada al identificador equivocado.
Si ejecuta el cliente detrás de un proxy o balanceador de carga, confirme que las cabeceras de actualización de WebSocket se reenvían y que los tiempos de espera de inactividad son más largos que el intervalo de heartbeat. Muchos incidentes en producción que parecen fallos de API son en realidad tiempos de espera de la infraestructura. Para obtener orientación más amplia sobre la fiabilidad de nodos y endpoints, consulte la guía de endpoints RPC y la guía de endpoints RPC multicadena.
- Discrepancia de firma: verifique método, ruta y marca de tiempo.
- Sin respuesta de suscripción: valide la forma JSON y el valor del tipo.
- Stream silencioso: confirme que el mercado está activo y el slug es correcto.
- Desconexiones frecuentes: revise el tiempo de espera de inactividad del proxy y el manejo de heartbeats.
Limitaciones y compensaciones
El streaming por WebSocket no es un reemplazo de REST. Las instantáneas pueden ser grandes, y una suscripción completa al libro de órdenes para muchos mercados a la vez consumirá memoria y ancho de banda. Las suscripciones de precios ligeros existen precisamente para reducir ese coste cuando solo necesita el top-of-book o el último precio. Elija el tipo de suscripción que coincida con su lógica de decisión en lugar de optar por defecto por el stream más pesado.
El streaming también introduce cuestiones de orden y consistencia que el sondeo evita. Debe decidir cómo manejar mensajes fuera de orden, actualizaciones duplicadas y huecos después de una reconexión. Un patrón común es mantener un libro local a partir del stream y reconciliarlo periódicamente contra una instantánea REST. Ese paso de reconciliación es barato y detecta desviaciones antes de que afecten a las decisiones de trading.
Por último, el host, la ruta y los detalles de autenticación exactos del endpoint varían según el despliegue. No codifique de forma fija un host que encontró en una publicación de blog. Lea la documentación actual del entorno con el que se integra y mantenga el host en la configuración para poder cambiarlo sin un despliegue de código.
Próximos pasos para una integración en producción
Una vez que el cliente mínimo funcione, los siguientes pasos son operativos: agregue logging estructurado para eventos del ciclo de vida de la conexión, agregue métricas para la tasa de mensajes y el recuento de reconexiones, y agregue alertas para fallos de autenticación. Mantenga los streams privados y de mercado en conexiones separadas con políticas de reintento separadas. Persista los mensajes sin procesar durante una ventana corta para poder reproducir un fallo.
Si su bot también necesita datos on-chain de Polygon u otra red, combine la integración de streaming con una capa RPC fiable. La página de precios de RPC y la lista de redes soportadas describen las opciones, y la guía de hosting de nodos blockchain cubre el lado de la infraestructura. Para obtener una recomendación concreta de endpoint en lugar de una metodología, use RPC Assistant.
- Agregue métricas antes de agregar funcionalidades.
- Reconcilie el estado del stream contra REST periódicamente.
- Mantenga las credenciales y los hosts en la configuración, no en el código.