WebSocket API

Real-time cryptocurrency price updates streamed directly to your application via WebSocket.

Pro Plan Required

Overview

The WebSocket API provides real-time ticker updates for Pro plan subscribers and above. Instead of polling the REST API, you receive price updates as they happen, with sub-second latency from exchange to your application.

Low Latency

Price updates are pushed as soon as they arrive from exchanges, typically under 100ms.

Channel Subscriptions

Subscribe to specific symbols or entire exchanges. Only receive what you need.

Auto Reconnect

The SDK handles reconnection automatically with exponential backoff and jitter.

Connection Limits

WebSocket access is available for Pro plan and above. Free tier users should use the REST API instead.

TierConnectionsSubscriptions / ConnectionPrice
FreeNot available-Free
Pro550€20/mo
Enterprise25500Custom

Quick Start

The easiest way to use the WebSocket API is through the TypeScript SDK, which handles reconnection, heartbeats, and serialization automatically.

sdk-example.ts
import { Luzia } from '@luziadev/sdk'
const luzia = new Luzia({
apiKey: 'lz_your_api_key',
})
// Create and open a WebSocket connection
const ws = luzia.createWebSocket()
ws.on('connected', (info) => {
console.log('Connected!', info.tier)
console.log('Max subscriptions:', info.limits.maxSubscriptions)
// Subscribe to channels
ws.subscribe([
'ticker:binance:BTC/USDT',
'ticker:coinbase:ETH/USDT',
])
})
ws.on('ticker', (data) => {
console.log(`${data.exchange} ${data.symbol}: $${data.data.last}`)
})
ws.on('error', (err) => {
console.error(`[${err.code}] ${err.message}`)
})
ws.on('disconnected', ({ code, reason }) => {
console.log(`Disconnected: ${code} ${reason}`)
})
// Open the connection
ws.connect()

Native WebSocket

You can also connect directly using the native WebSocket API from any language or platform. Authenticate by passing your API key in the Authorization header:

wss://api.luzia.dev/v1/ws

Header: Authorization: Bearer lz_your_api_key

native-ws.js
// Without the SDK - server-side (Node.js, Bun, wscat)
// Pass API key via Authorization header
const ws = new WebSocket('wss://api.luzia.dev/v1/ws', {
headers: { Authorization: 'Bearer lz_your_api_key' },
})
ws.onopen = () => {
console.log('Connected')
}
ws.onmessage = (event) => {
const msg = JSON.parse(event.data)
switch (msg.type) {
case 'connected':
// Subscribe to channels after connection is confirmed
ws.send(JSON.stringify({
type: 'subscribe',
channels: ['ticker:binance:BTC/USDT', 'ticker:binance:ETH/USDT'],
}))
break
case 'ticker':
console.log(`${msg.exchange} ${msg.symbol}: $${msg.data.last}`)
break
case 'subscribed':
console.log('Subscribed to:', msg.channel)
break
case 'error':
console.error(`[${msg.code}] ${msg.message}`)
break
}
}
// Keep connection alive with periodic pings
setInterval(() => {
if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({ type: 'ping' }))
}
}, 30000)

Channel Format

Channels follow the pattern ticker:{exchange}:{symbol} for specific symbols, or ticker:{exchange} for all tickers from an exchange.

ChannelDescription
ticker:binance:BTC/USDTBTC/USDT updates from Binance only
ticker:coinbase:ETH/USDTETH/USDT updates from Coinbase only
ticker:binanceAll ticker updates from Binance (counts as 1 subscription)
 
// Subscribe to specific symbols
ws.subscribe([
'ticker:binance:BTC/USDT', // Single symbol on Binance
'ticker:coinbase:ETH/USDT', // Single symbol on Coinbase
])
// Subscribe to ALL tickers from an exchange
ws.subscribe([
'ticker:binance', // All Binance tickers
])
// Unsubscribe
ws.unsubscribe(['ticker:binance:BTC/USDT'])

Protocol Reference

Client → Server Messages

All messages sent to the server must be valid JSON with a type field.

 
// Subscribe to channels
{ "type": "subscribe", "channels": ["ticker:binance:BTC/USDT"] }
// Unsubscribe from channels
{ "type": "unsubscribe", "channels": ["ticker:binance:BTC/USDT"] }
// Ping (heartbeat)
{ "type": "ping" }

Server → Client Messages

 
// Connection established
{ "type": "connected", "message": "Connected to Luzia WebSocket API", "tier": "pro", "limits": { "maxSubscriptions": 50 } }
// Subscription confirmed
{ "type": "subscribed", "channel": "ticker:binance:BTC/USDT" }
// Unsubscription confirmed
{ "type": "unsubscribed", "channel": "ticker:binance:BTC/USDT" }
// Pong (heartbeat response)
{ "type": "pong", "timestamp": "2024-01-23T10:13:20.000Z" }
// Error
{ "type": "error", "code": "SUBSCRIPTION_LIMIT", "message": "Subscription limit exceeded (max 50 for pro tier)" }

Ticker Payload

The data field in ticker messages contains the same structure as the REST API ticker response.

 
// Ticker message payload
{
"type": "ticker",
"exchange": "binance",
"symbol": "BTC/USDT",
"data": {
"symbol": "BTC/USDT",
"exchange": "binance",
"last": 67432.10,
"bid": 67430.00,
"ask": 67434.20,
"high": 68100.00,
"low": 66800.00,
"open": 67000.00,
"close": 67432.10,
"volume": 12345.67,
"quoteVolume": 832456789.12,
"change": 432.10,
"changePercent": 0.645,
"timestamp": "2024-01-23T10:13:20.000Z"
},
"timestamp": "2024-01-23T10:13:20.050Z"
}

Error Codes

Error messages include a code field to help you handle specific error conditions.

CodeDescription
CONNECTION_REJECTEDConnection was rejected (wrong tier or connection limit exceeded)
SUBSCRIPTION_LIMITMaximum subscription count reached for your tier
INVALID_CHANNELChannel format is invalid
INVALID_JSONMessage is not valid JSON
INVALID_REQUESTMessage is missing required fields
UNKNOWN_TYPEUnrecognized message type
SERVER_SHUTDOWNServer is shutting down, reconnect shortly

Reconnection

The SDK handles reconnection automatically with exponential backoff and jitter. Subscriptions are automatically restored after reconnecting.

 
// Configure auto-reconnect behavior
const ws = luzia.createWebSocket({
autoReconnect: true, // Enable auto-reconnect (default: true)
maxReconnectAttempts: 10, // Max reconnect attempts (default: 10)
reconnectDelayMs: 1000, // Initial delay (default: 1000ms)
maxReconnectDelayMs: 30000, // Max delay (default: 30000ms)
heartbeatIntervalMs: 30000, // Heartbeat interval (default: 30000ms)
})
ws.on('reconnecting', ({ attempt, delayMs }) => {
console.log(`Reconnecting (attempt ${attempt}) in ${delayMs}ms...`)
})
ws.connect()
// Manually disconnect (disables auto-reconnect)
ws.disconnect()

Best Practices

  • Subscribe only to what you need. Use symbol-specific channels instead of exchange-level channels when possible to reduce bandwidth.
  • Handle reconnection gracefully. Use the SDK's built-in auto-reconnect or implement your own exponential backoff for native WebSocket.
  • Send periodic pings. The SDK sends heartbeats automatically every 30 seconds. For native WebSocket, implement your own ping interval.
  • Unsubscribe when done. Free up subscription slots by unsubscribing from channels you no longer need.
  • Use REST as fallback. If the WebSocket connection drops, you can always fall back to the REST API for the latest prices.