Skip to main content

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.

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:
StreamSubscribe withWhat 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

TypeBoundaryPrice source
5minEvery 5 minutesChainlink Data Streams
15minEvery 15 minutesChainlink Data Streams
1hourTop of every hourBinance aggTrades
4hour00:00, 04:00, 08:00, 12:00, 16:00, 20:00 ETBinance aggTrades
dailyMidnight ETBinance aggTrades
Supported assets: btc, eth, sol, xrp, hype, doge, bnb

Connection flow

1. Connect

On connect the server immediately sends a connected event:
{
  "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:
{
  "type": "subscribe",
  "asset": "btc",
  "marketTypes": ["5min", "15min"]
}
FieldTypeDescription
type"subscribe"Required.
assetstringOne of the supported asset symbols.
marketTypesstring[]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.
{ "event": "subscribed", "asset": "btc", "marketTypes": ["5min", "15min"] }

Hyperliquid mark price

Send a JSON message to subscribe to the underlying perp mark-price stream:
{
  "type": "subscribe",
  "channel": "hyperliquid_mark_price",
  "asset": "btc"
}
FieldTypeDescription
type"subscribe"Required.
channel"hyperliquid_mark_price"Required for the Hyperliquid mark-price stream.
assetstringOne of the supported asset symbols.
The server confirms with:
{ "event": "subscribed", "channel": "hyperliquid_mark_price", "asset": "btc" }
It then immediately pushes a snapshot:
{
  "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:
{
  "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:
{
  "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

{ "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

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

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

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

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

EventDirectionDescription
connectedServer → ClientSent immediately on connect with supported assets and types.
subscribedServer → ClientConfirms a successful subscribe. Followed by current-window snapshots.
unsubscribedServer → ClientConfirms unsubscribe.
window_openServer → ClientTarget price pushed at every window boundary. Also sent as snapshot on subscribe.
window_open_errorServer → ClientUpstream price fetch failed after all retries.
hyperliquid_mark_priceServer → ClientLive Hyperliquid underlying perp mark price, including oraclePrice, midPrice, funding, and openInterest.
errorServer → ClientInvalid JSON, unknown asset, or unknown message type.
subscribeClient → ServerSubscribe to either Vatic target-price market types or a named real-time channel like hyperliquid_mark_price.
unsubscribeClient → ServerRemove all subscriptions for this connection.