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

# WebSocket API

> Real-time Vatic target prices and Hyperliquid mark-price monitoring over a single WebSocket connection.

## Endpoint

```
wss://api.vatic.trading/ws
```

No authentication required. Connect once and subscribe to any supported asset / stream combination.

***

## Available streams

The WebSocket currently supports two real-time streams:

| Stream                  | Subscribe with                                                          | What you receive                                                                       |
| ----------------------- | ----------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
| Vatic target prices     | `{"type":"subscribe","asset":"btc","marketTypes":["5min","15min"]}`     | `window_open` events at each Polymarket/Vatic market boundary                          |
| Hyperliquid mark prices | `{"type":"subscribe","channel":"hyperliquid_mark_price","asset":"btc"}` | `hyperliquid_mark_price` snapshots and live updates for the underlying perp mark price |

***

## Why WebSocket over polling?

REST polling at window boundaries causes two problems:

1. **502 errors** — Chainlink/Binance takes 1–5s to publish a price after a window opens. Polling at `:00` means your request arrives before the price exists.
2. **Rate limits** — Hammering the endpoint while waiting burns your 120 req/min budget instantly.

The WebSocket absorbs the publish lag server-side, retries the upstream fetch automatically, and pushes the resolved price to all subscribers the instant it's ready. For Hyperliquid, it also gives you live access to the underlying `markPrice` that matters for prediction-market settlement.

***

## Market types

| Type    | Boundary                                    | Price source           |
| ------- | ------------------------------------------- | ---------------------- |
| `5min`  | Every 5 minutes                             | Chainlink Data Streams |
| `15min` | Every 15 minutes                            | Chainlink Data Streams |
| `1hour` | Top of every hour                           | Binance aggTrades      |
| `4hour` | 00:00, 04:00, 08:00, 12:00, 16:00, 20:00 ET | Binance aggTrades      |
| `daily` | Midnight ET                                 | Binance aggTrades      |

Supported assets: `btc`, `eth`, `sol`, `xrp`, `hype`, `doge`, `bnb`

***

## Connection flow

### 1. Connect

On connect the server immediately sends a `connected` event:

```json theme={null}
{
  "event": "connected",
  "supportedAssets": ["btc", "eth", "sol", "xrp", "hype", "doge", "bnb"],
  "supportedMarketTypes": ["5min", "15min", "1hour", "4hour", "daily"],
  "supportedChannels": ["hyperliquid_mark_price"],
  "usage": [
    "Send {\"type\":\"subscribe\",\"asset\":\"btc\",\"marketTypes\":[\"5min\",\"15min\"]}",
    "Send {\"type\":\"subscribe\",\"channel\":\"hyperliquid_mark_price\",\"asset\":\"btc\"}"
  ]
}
```

### 2. Subscribe

#### Vatic target prices

Send a JSON message to subscribe to one asset across one or more market types:

```json theme={null}
{
  "type": "subscribe",
  "asset": "btc",
  "marketTypes": ["5min", "15min"]
}
```

| Field         | Type          | Description                                              |
| ------------- | ------------- | -------------------------------------------------------- |
| `type`        | `"subscribe"` | Required.                                                |
| `asset`       | string        | One of the supported asset symbols.                      |
| `marketTypes` | string\[]     | One or more market types. Omit to subscribe to all five. |

The server confirms with a `subscribed` event and **immediately pushes a `window_open` snapshot for the current window** of each subscribed type — so you always have the latest price on connect, without waiting for the next boundary.

```json theme={null}
{ "event": "subscribed", "asset": "btc", "marketTypes": ["5min", "15min"] }
```

#### Hyperliquid mark price

Send a JSON message to subscribe to the underlying perp mark-price stream:

```json theme={null}
{
  "type": "subscribe",
  "channel": "hyperliquid_mark_price",
  "asset": "btc"
}
```

| Field     | Type                       | Description                                     |
| --------- | -------------------------- | ----------------------------------------------- |
| `type`    | `"subscribe"`              | Required.                                       |
| `channel` | `"hyperliquid_mark_price"` | Required for the Hyperliquid mark-price stream. |
| `asset`   | string                     | One of the supported asset symbols.             |

The server confirms with:

```json theme={null}
{ "event": "subscribed", "channel": "hyperliquid_mark_price", "asset": "btc" }
```

It then immediately pushes a snapshot:

```json theme={null}
{
  "event": "hyperliquid_mark_price",
  "channel": "hyperliquid_mark_price",
  "asset": "btc",
  "coin": "BTC",
  "source": "hyperliquid",
  "markPrice": 78669,
  "oraclePrice": 78700,
  "midPrice": 78669.5,
  "prevDayPx": 78333,
  "change24h": 0.4289379954808319,
  "openInterest": 29626.19272,
  "funding": 0.0000058782,
  "volume": 921508494.6523755,
  "observedAt": "2026-05-03T13:38:56.147Z",
  "snapshot": true
}
```

After the snapshot, the server keeps broadcasting updates whenever the upstream Hyperliquid perp context changes.

### 3. Receive price events

At every window boundary the server pushes a `window_open` event:

```json theme={null}
{
  "event": "window_open",
  "asset": "btc",
  "marketType": "5min",
  "windowStart": 1744027500,
  "windowStartIso": "2025-04-07T12:05:00.000Z",
  "price": 82450.75,
  "source": "chainlink"
}
```

The initial snapshot after subscribe includes `"snapshot": true`.

If the upstream price fetch fails after all retries, you receive `window_open_error` instead:

```json theme={null}
{
  "event": "window_open_error",
  "asset": "btc",
  "marketType": "5min",
  "windowStart": 1744027500,
  "windowStartIso": "2025-04-07T12:05:00.000Z",
  "error": "Price unavailable — upstream fetch failed after retries"
}
```

### 4. Unsubscribe

```json theme={null}
{ "type": "unsubscribe" }
```

Removes all subscriptions for the current connection.

***

## Heartbeat

The server sends a WebSocket **ping every 30 seconds**. If your client does not respond with a pong, the connection is terminated. Most WebSocket libraries handle this automatically.

***

## Examples

### Node.js

```js theme={null}
import WebSocket from 'ws';

function connect() {
  const ws = new WebSocket('wss://api.vatic.trading/ws');

  ws.on('open', () => console.log('connected'));

  ws.on('message', (raw) => {
    const text = typeof raw === 'string' ? raw : raw.toString();
    const msg = JSON.parse(text);

    if (msg.event === 'connected') {
      ws.send(JSON.stringify({
        type: 'subscribe',
        asset: 'btc',
        marketTypes: ['5min', '15min'],
      }));
    }

    if (msg.event === 'window_open') {
      const label = msg.snapshot ? 'snapshot' : 'LIVE';
      console.log(`[${label}] ${msg.asset} ${msg.marketType} — $${msg.price} (${msg.windowStartIso})`);
    }
  });

  ws.on('close', () => {
    console.log('disconnected, reconnecting in 3s...');
    setTimeout(connect, 3000);
  });

  ws.on('error', (err) => console.error('ws error:', err.message));
}

connect();
```

### TypeScript — Hyperliquid mark price

```ts theme={null}
import WebSocket, { type RawData } from 'ws';

function connect() {
  const ws = new WebSocket('wss://api.vatic.trading/ws');

  ws.on('message', (raw: RawData) => {
    const text = typeof raw === 'string' ? raw : raw.toString();
    const msg = JSON.parse(text);

    if (msg.event === 'connected') {
      ws.send(JSON.stringify({
        type: 'subscribe',
        channel: 'hyperliquid_mark_price',
        asset: 'btc',
      }));
    }

    if (msg.event === 'hyperliquid_mark_price') {
      const label = msg.snapshot ? 'snapshot' : 'LIVE';
      console.log(
        `[${label}] ${msg.asset} mark=${msg.markPrice} oracle=${msg.oraclePrice} funding=${msg.funding} at ${msg.observedAt}`
      );
    }
  });

  ws.on('close', () => setTimeout(connect, 3000));
}

connect();
```

### Python

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

async def connect():
    uri = "wss://api.vatic.trading/ws"
    while True:
        try:
            async with websockets.connect(uri, ping_interval=30) as ws:
                print("connected")
                async for raw in ws:
                    msg = json.loads(raw)

                    if msg["event"] == "connected":
                        await ws.send(json.dumps({
                            "type": "subscribe",
                            "asset": "btc",
                            "marketTypes": ["5min", "15min"],
                        }))

                    elif msg["event"] == "window_open":
                        label = "snapshot" if msg.get("snapshot") else "LIVE"
                        print(f"[{label}] {msg['asset']} {msg['marketType']} — ${msg['price']} ({msg['windowStartIso']})")

        except Exception as e:
            print(f"disconnected ({e}), reconnecting in 3s...")
            await asyncio.sleep(3)

asyncio.run(connect())
```

### TypeScript

```ts theme={null}
import WebSocket, { type RawData } from 'ws';

interface WindowOpenEvent {
  event: 'window_open';
  asset: string;
  marketType: string;
  windowStart: number;
  windowStartIso: string;
  price: number;
  source: string;
  snapshot?: boolean;
}

function connect(): void {
  const ws = new WebSocket('wss://api.vatic.trading/ws');

  ws.on('message', (raw: RawData) => {
    const text = typeof raw === 'string' ? raw : raw.toString();
    const msg = JSON.parse(text);

    if (msg.event === 'connected') {
      ws.send(JSON.stringify({
        type: 'subscribe',
        asset: 'btc',
        marketTypes: ['5min', '15min'],
      }));
    }

    if (msg.event === 'window_open') {
      const e = msg as WindowOpenEvent;
      const label = e.snapshot ? 'snapshot' : 'LIVE';
      console.log(`[${label}] ${e.asset} ${e.marketType} — $${e.price} (${e.windowStartIso})`);
    }
  });

  ws.on('close', () => setTimeout(connect, 3000));
  ws.on('error', (err) => console.error('ws error:', err.message));
}

connect();
```

***

## Event reference

| Event                    | Direction       | Description                                                                                                      |
| ------------------------ | --------------- | ---------------------------------------------------------------------------------------------------------------- |
| `connected`              | Server → Client | Sent immediately on connect with supported assets and types.                                                     |
| `subscribed`             | Server → Client | Confirms a successful subscribe. Followed by current-window snapshots.                                           |
| `unsubscribed`           | Server → Client | Confirms unsubscribe.                                                                                            |
| `window_open`            | Server → Client | Target price pushed at every window boundary. Also sent as snapshot on subscribe.                                |
| `window_open_error`      | Server → Client | Upstream price fetch failed after all retries.                                                                   |
| `hyperliquid_mark_price` | Server → Client | Live Hyperliquid underlying perp mark price, including `oraclePrice`, `midPrice`, `funding`, and `openInterest`. |
| `error`                  | Server → Client | Invalid JSON, unknown asset, or unknown message type.                                                            |
| `subscribe`              | Client → Server | Subscribe to either Vatic target-price market types or a named real-time channel like `hyperliquid_mark_price`.  |
| `unsubscribe`            | Client → Server | Remove all subscriptions for this connection.                                                                    |
