> For the complete documentation index, see [llms.txt](https://resolvedmarkets.com/llms.txt). Every docs page is also served as Markdown: append `.md` to its URL or send `Accept: text/markdown`.

## WebSocket Streaming

Real-time orderbook data via WebSocket. Lower latency and bandwidth than REST polling. API key required.

**URL:** `wss://api.resolvedmarkets.com/ws/orderbook`

**Auth:** message-based. Open the connection without any auth, then send `{ "type": "auth", "apiKey": "rm_..." }` as the first frame. The server replies with `{ "type": "auth", "status": "ok" }`. Connections that do not authenticate within 5 seconds are closed with code `4001`. Query-string auth (`?apiKey=…`) is deprecated but still accepted for backward compatibility.

**Tier connection limits**
- Free tier: no WebSocket access (closed with code 4003)
- Pro tier: 1 concurrent connection
- Scale tier: 5 concurrent connections
- Enterprise tier: 10 concurrent connections

**Message flow**
1. Connect to `wss://api.resolvedmarkets.com/ws/orderbook`.
2. Send `{ "type": "auth", "apiKey": "rm_..." }` — wait for `{ "type": "auth", "status": "ok" }`.
3. Send `{ "type": "subscribe", "crypto": "BTC" }` to start receiving data.
4. Server pushes `{ "type": "orderbook", "markets": [...] }` every ~2 seconds with live depth data.

**JavaScript example**

```javascript
const API_KEY = "rm_your_key_here";
const ws = new WebSocket("wss://api.resolvedmarkets.com/ws/orderbook");

ws.onopen = () => {
  ws.send(JSON.stringify({ type: "auth", apiKey: API_KEY }));
};

ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);
  if (msg.type === "auth" && msg.status === "ok") {
    ws.send(JSON.stringify({ type: "subscribe", crypto: "BTC" }));
    return;
  }
  if (msg.type === "markets") console.log("Available markets:", msg.data);
  if (msg.type === "orderbook") {
    for (const market of msg.markets) {
      console.log(market.crypto, market.timeframe);
      console.log("  UP best bid:", market.up?.bestBid);
      console.log("  DOWN best ask:", market.down?.bestAsk);
    }
  }
};
```

**Python example**

```python
import asyncio, json, websockets

async def stream():
    API_KEY = "rm_your_key_here"
    async with websockets.connect("wss://api.resolvedmarkets.com/ws/orderbook") as ws:
        await ws.send(json.dumps({"type": "auth", "apiKey": API_KEY}))
        ack = json.loads(await ws.recv())
        assert ack.get("status") == "ok", ack
        await ws.send(json.dumps({"type": "subscribe", "crypto": "ETH"}))
        async for message in ws:
            data = json.loads(message)
            if data["type"] == "orderbook":
                for m in data["markets"]:
                    print(f"{m['crypto']} {m['timeframe']}: UP bid={m['up']['bestBid']}")

asyncio.run(stream())
```

**Server messages**

| Type | Frequency | Description |
| --- | --- | --- |
| `auth` | On successful auth | `{ "type": "auth", "status": "ok" }` |
| `markets` | On auth + on market rotation | Full list of active markets with crypto, timeframe, and IDs |
| `orderbook` | Every ~2 seconds | Live orderbook for subscribed crypto — UP/DOWN books with bids, asks, depth |
| `error` | On auth / tier failure | Includes numeric `code` (4001 auth timeout, 4003 tier restriction / connection limit) |
| `ping` | Every 30 seconds | Heartbeat — pong is automatic in browsers |

**Client messages**

| Type | Payload | Description |
| --- | --- | --- |
| `auth` | `{ "type": "auth", "apiKey": "rm_..." }` | Required first message. Must arrive within 5 seconds. |
| `subscribe` | `{ "type": "subscribe", "crypto": "BTC" }` | Switch subscription to a different crypto (BTC, ETH, SOL, XRP, DOGE, HYPE, BNB) |

**Notes**
- One subscription at a time — sending a new `subscribe` replaces the previous one. Open multiple connections (subject to tier cap) to monitor multiple cryptos.
- The server evicts clients that don't respond to pings within 30 seconds. Implement reconnection logic.
