> ## Documentation Index
> Fetch the complete documentation index at: https://polyquantlab.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# WebSocket — live snapshots

> Subscribe to sub-second Polymarket orderbook updates over a single persistent connection.

The HTTP endpoints return historical and current state. For a live
feed — used by the dashboard, paper trader, and arb tracker — connect
to the WebSocket at:

```
wss://api.polyquantlab.com/v1/stream
```

One connection per client is plenty. The server multiplexes every
subscribed market across the single socket.

## Authentication

Pass your API key as the `token` query parameter on the WebSocket URL.
(Browsers don't allow `Authorization` headers on `WebSocket()` calls,
so we accept the token in the URL — same secret, different transport.)

```
wss://api.polyquantlab.com/v1/stream?token=pql_live_…
```

## Subscribe

Once the socket opens, send a subscribe frame:

```json theme={null}
{
  "type": "subscribe",
  "tickers": ["BTC", "ETH", "SOL"],
  "event_types": ["5m", "15m"]
}
```

You'll receive an `ack`:

```json theme={null}
{"type": "ack", "subscribed": {"tickers": ["BTC","ETH","SOL"], "event_types": ["5m","15m"]}}
```

Then **snapshot events** start flowing whenever the collector receives
an update for any market matching the filter.

## Snapshot frame

```json theme={null}
{
  "type": "snapshot",
  "market_id": "0x198001cb01bbcd…",
  "ts": "2026-06-05T02:14:31.892",
  "yes_bids": [{"price": 0.41, "size": 1200}, ...],
  "yes_asks": [{"price": 0.45, "size":  800}, ...],
  "no_bids":  [{"price": 0.54, "size":  900}, ...],
  "no_asks":  [{"price": 0.58, "size": 1100}, ...],
  "underlying_ticker": "BTC",
  "underlying_price": 75123.45
}
```

Same shape as [`/v1/snapshots`](/docs/api-reference/snapshots/list-snapshots),
just delivered via push instead of pull.

## Keep-alive

The server sends a `{"type": "ping"}` every 25 s. If you don't reply
with `{"type": "pong"}` within 30 s, the connection closes. Your
WebSocket library probably does this for you (ws on Node, `websockets`
on Python both handle it transparently).

## Reconnect

If you drop, simply reconnect and re-send your subscribe frame.
There's no replay window — snapshots missed during the gap are gone,
so backfill via [`GET /v1/snapshots`](/docs/api-reference/snapshots/list-snapshots)
if you need them.

## Example — Python

```python theme={null}
import asyncio, json, os
import websockets

URI = f"wss://api.polyquantlab.com/v1/stream?token={os.environ['POLYQUANTLAB_API_KEY']}"

async def main():
    async with websockets.connect(URI, ping_interval=25, ping_timeout=30) as ws:
        await ws.send(json.dumps({
            "type": "subscribe",
            "tickers": ["BTC"],
            "event_types": ["5m"],
        }))
        async for raw in ws:
            msg = json.loads(raw)
            if msg["type"] == "snapshot":
                print(msg["market_id"][:12], msg["ts"], msg.get("underlying_price"))

asyncio.run(main())
```

## Example — Node

```javascript theme={null}
import WebSocket from "ws";

const ws = new WebSocket(
  `wss://api.polyquantlab.com/v1/stream?token=${process.env.POLYQUANTLAB_API_KEY}`
);

ws.on("open", () => {
  ws.send(JSON.stringify({
    type: "subscribe",
    tickers: ["BTC"],
    event_types: ["5m"],
  }));
});

ws.on("message", (raw) => {
  const msg = JSON.parse(raw);
  if (msg.type === "snapshot") {
    console.log(msg.market_id.slice(0, 12), msg.ts, msg.underlying_price);
  }
});
```

## Rate limit

WebSocket connections count as 1 connection toward your tier's cap (no
per-message rps cost). Free tier allows 1 concurrent connection, Pro
allows 4, Plus allows 16.

If you need a guaranteed-delivery feed beyond Plus,
[contact us](https://polyquantlab.com/contact) — we can set up a
dedicated NATS / Redis Streams bridge for Enterprise customers.
