WebSocket API
Real-time cryptocurrency price updates streamed directly to your application via WebSocket.
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.
| Tier | Connections | Subscriptions / Connection | Price |
|---|---|---|---|
| Free | Not available | - | Free |
| Pro | 5 | 50 | €20/mo |
| Enterprise | 25 | 500 | Custom |
Quick Start
The easiest way to use the WebSocket API is through the TypeScript SDK, which handles reconnection, heartbeats, and serialization automatically.
import { Luzia } from '@luziadev/sdk'const luzia = new Luzia({apiKey: 'lz_your_api_key',})// Create and open a WebSocket connectionconst ws = luzia.createWebSocket()ws.on('connected', (info) => {console.log('Connected!', info.tier)console.log('Max subscriptions:', info.limits.maxSubscriptions)// Subscribe to channelsws.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 connectionws.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/wsHeader: Authorization: Bearer lz_your_api_key
// Without the SDK - server-side (Node.js, Bun, wscat)// Pass API key via Authorization headerconst 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 confirmedws.send(JSON.stringify({type: 'subscribe',channels: ['ticker:binance:BTC/USDT', 'ticker:binance:ETH/USDT'],}))breakcase 'ticker':console.log(`${msg.exchange} ${msg.symbol}: $${msg.data.last}`)breakcase 'subscribed':console.log('Subscribed to:', msg.channel)breakcase 'error':console.error(`[${msg.code}] ${msg.message}`)break}}// Keep connection alive with periodic pingssetInterval(() => {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.
| Channel | Description |
|---|---|
ticker:binance:BTC/USDT | BTC/USDT updates from Binance only |
ticker:coinbase:ETH/USDT | ETH/USDT updates from Coinbase only |
ticker:binance | All ticker updates from Binance (counts as 1 subscription) |
// Subscribe to specific symbolsws.subscribe(['ticker:binance:BTC/USDT', // Single symbol on Binance'ticker:coinbase:ETH/USDT', // Single symbol on Coinbase])// Subscribe to ALL tickers from an exchangews.subscribe(['ticker:binance', // All Binance tickers])// Unsubscribews.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.
| Code | Description |
|---|---|
CONNECTION_REJECTED | Connection was rejected (wrong tier or connection limit exceeded) |
SUBSCRIPTION_LIMIT | Maximum subscription count reached for your tier |
INVALID_CHANNEL | Channel format is invalid |
INVALID_JSON | Message is not valid JSON |
INVALID_REQUEST | Message is missing required fields |
UNKNOWN_TYPE | Unrecognized message type |
SERVER_SHUTDOWN | Server 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 behaviorconst 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.