GaiaExGaiaEx
API

Public WebSocket Streams

Public WebSocket streams for real-time order book depth and spot order book data on GaiaEx. No authentication required.

Perp Order Book Stream

WS wss://openapi.gaiaex.com/ws/market/{symbol}

Real-time L2 order book updates for perpetual symbols. No authentication required.

ParameterTypeDescription
{symbol}string (path)Trading symbol — BTC, ETH, xyz:AAPL, vntl:OPENAI, etc.

Message: orderbook

Sent on every order book change. Contains the full L2 snapshot (the server always sends full snapshots, never incremental deltas).

{
  "type": "orderbook",
  "symbol": "BTC",
  "bids": [
    ["84250.00", "1.2340"],
    ["84249.50", "0.5000"]
  ],
  "asks": [
    ["84251.00", "0.8000"],
    ["84252.00", "2.1000"]
  ],
  "priceDecimals": 2,
  "timestamp": 1743508800000,
  "stale": false,
  "displayName": "BTC",
  "feed_connected": true
}
FieldTypeDescription
typestringAlways "orderbook"
symbolstringSymbol name
bidsarrayBid price levels — each entry is [price, size] (strings), sorted best-first
asksarrayAsk price levels — each entry is [price, size] (strings), sorted best-first
priceDecimalsnumberDecimal precision for prices on this symbol
timestampnumberServer timestamp in ms
stalebooleanTrue if the upstream feed is currently disconnected
displayNamestringHuman-readable symbol name
feed_connectedbooleanWhether the upstream data feed is connected

Message: heartbeat

{
  "type": "heartbeat",
  "symbol": "BTC",
  "timestamp": 1743508800000,
  "stale": false
}

Message: stale / recovered

Sent when the upstream data feed disconnects or reconnects:

{ "type": "stale", "symbol": "BTC", "reason": "upstream_disconnected", "timestamp": 1743508800000 }
{ "type": "recovered", "symbol": "BTC", "timestamp": 1743508800000 }

Error (invalid symbol)

If the symbol is not supported, the server sends an error and closes the connection:

{
  "type": "error",
  "code": "SYMBOL_NOT_SUPPORTED",
  "message": "Symbol 'INVALID' not supported. Use /symbols/list for available symbols."
}

Python Example

import asyncio
import websockets
import json

async def stream_orderbook(symbol: str):
    uri = f"wss://openapi.gaiaex.com/ws/market/{symbol}"
    async with websockets.connect(uri) as ws:
        async for message in ws:
            data = json.loads(message)
            if data["type"] == "orderbook":
                bids = data.get("bids", [])
                asks = data.get("asks", [])
                top_bid = bids[0] if bids else None
                print(f"{symbol} top bid: {top_bid}")
            elif data["type"] == "heartbeat":
                await ws.send("ping")

asyncio.run(stream_orderbook("BTC"))

JavaScript Example

const symbol = 'BTC';
const ws = new WebSocket(`wss://openapi.gaiaex.com/ws/market/${symbol}`);

ws.onmessage = (event) => {
  const data = JSON.parse(event.data);
  if (data.type === 'orderbook') {
    console.log(`${data.symbol} bids:`, data.bids.length, 'asks:', data.asks.length);
  }
};

ws.onclose = () => console.log('Disconnected, reconnecting...');
ws.onerror = (err) => console.error('WS error:', err);

wscat Example

# Connect to BTC perp order book
wscat -c wss://openapi.gaiaex.com/ws/market/BTC

# Connect to RWA Gold order book
wscat -c wss://openapi.gaiaex.com/ws/market/xyz:GOLD

Spot Order Book Stream

WS wss://openapi.gaiaex.com/ws/market/spot/{symbol}

Real-time L2 order book updates for spot tokens. No authentication required.

ParameterTypeDescription
{symbol}string (path)Spot token name — BTC, ETH, PURR, HYPE, etc.

The message format is identical to the perp order book stream above.

Error (invalid symbol)

{
  "type": "error",
  "code": "SPOT_SYMBOL_NOT_SUPPORTED",
  "message": "Spot symbol 'INVALID' not found. Use /spot/symbols/list."
}

Python Example

import asyncio
import websockets
import json

async def stream_spot(symbol: str):
    uri = f"wss://openapi.gaiaex.com/ws/market/spot/{symbol}"
    async with websockets.connect(uri) as ws:
        async for message in ws:
            data = json.loads(message)
            if data["type"] == "orderbook":
                print(f"Spot {symbol}: {len(data['bids'])} bids, {len(data['asks'])} asks")

asyncio.run(stream_spot("HYPE"))

JavaScript Example

const ws = new WebSocket('wss://openapi.gaiaex.com/ws/market/spot/HYPE');

ws.onmessage = (event) => {
  const data = JSON.parse(event.data);
  if (data.type === 'orderbook') {
    console.log('Spot bids:', data.bids.length, 'asks:', data.asks.length);
  }
};

Ping / Pong

To keep a public WebSocket connection alive, send a text message "ping". The server responds with "pong".

→ "ping"
← "pong"

TIP

Most WebSocket libraries handle protocol-level ping/pong frames automatically. The text-based ping/pong above is an additional application-level keepalive mechanism.

Rate Limits

There is no explicit rate limit on public WebSocket connections. However:

  • Each connection consumes server resources. Do not open more connections than necessary.
  • Use one connection per symbol. Do not rapidly open/close connections.
  • If you need multiple symbols, open one connection per symbol and reuse them.

WARNING

Clients that abuse connections (e.g. opening hundreds per second) may be temporarily blocked.

Terminal Walkthrough

Live Test: BTC Perpetual Order Book

# Terminal 1: Connect to BTC
$ wscat -c wss://openapi.gaiaex.com/ws/market/BTC
Connected (press CTRL+C to quit)
< {"type":"orderbook","symbol":"BTC","bids":[["84250.00","1.234"],["84249.50","0.500"]],"asks":[["84251.00","0.800"],["84252.00","2.100"]],"priceDecimals":2,"timestamp":1712345678000,"stale":false,"displayName":"BTC","feed_connected":true}

# Send keepalive
> ping
< pong

# The stream continues with every orderbook change (always full snapshots)...
# Press CTRL+C to disconnect

Live Test: Spot Order Book

# Terminal 2: Connect to ETH spot
$ wscat -c wss://openapi.gaiaex.com/ws/market/spot/ETH
Connected (press CTRL+C to quit)
< {"type":"orderbook","symbol":"ETH","bids":[...],"asks":[...],"timestamp":...}

Live Test: RWA Symbol (Apple Stock)

# RWA symbols use the xyz: prefix
$ wscat -c wss://openapi.gaiaex.com/ws/market/xyz:AAPL
Connected (press CTRL+C to quit)
< {"type":"orderbook","symbol":"xyz:AAPL","bids":[["195.50","10.0"]],"asks":[["195.75","5.0"]],"timestamp":...}

Python: Build a Real-Time Spread Monitor

import asyncio
import json
import websockets

async def spread_monitor(symbol: str):
    uri = f"wss://openapi.gaiaex.com/ws/market/{symbol}"
    async with websockets.connect(uri) as ws:
        async for raw in ws:
            msg = json.loads(raw)
            if msg["type"] != "orderbook":
                continue

            bids = msg.get("bids", [])
            asks = msg.get("asks", [])

            if bids and asks:
                # bids and asks are [[price, size], ...] arrays sorted best-first
                best_bid_px = float(bids[0][0])
                best_ask_px = float(asks[0][0])
                spread = best_ask_px - best_bid_px
                print(f"{symbol} | Bid: {bids[0][0]} | Ask: {asks[0][0]} | Spread: {spread:.2f}")

asyncio.run(spread_monitor("ETH"))

Message Flow Diagram

Client                                Server
  │                                      │
  │──── WSS Connect ────────────────────▶│
  │                                      │
  │◀──── orderbook (full snapshot) ──────│
  │◀──── orderbook (full snapshot) ──────│
  │◀──── orderbook (full snapshot) ──────│
  │                                      │
  │──── "ping" ─────────────────────────▶│
  │◀──── "pong" ─────────────────────────│
  │                                      │
  │◀──── heartbeat ──────────────────────│  (no changes for a while)
  │◀──── orderbook (full snapshot) ──────│
  │                                      │
  │──── Close ──────────────────────────▶│
  │◀──── Close ACK ──────────────────────│