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.
| Parameter | Type | Description |
|---|---|---|
{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
}| Field | Type | Description |
|---|---|---|
type | string | Always "orderbook" |
symbol | string | Symbol name |
bids | array | Bid price levels — each entry is [price, size] (strings), sorted best-first |
asks | array | Ask price levels — each entry is [price, size] (strings), sorted best-first |
priceDecimals | number | Decimal precision for prices on this symbol |
timestamp | number | Server timestamp in ms |
stale | boolean | True if the upstream feed is currently disconnected |
displayName | string | Human-readable symbol name |
feed_connected | boolean | Whether 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:GOLDSpot Order Book Stream
WS wss://openapi.gaiaex.com/ws/market/spot/{symbol}
Real-time L2 order book updates for spot tokens. No authentication required.
| Parameter | Type | Description |
|---|---|---|
{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 disconnectLive 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 ──────────────────────│