WebSocket API
The Synthetix API provides real-time, bidirectional communication for both trading operations and market data streaming.
Overview
The WebSocket API offers the following endpoints:
| Endpoint | Path | Authentication | Purpose |
|---|---|---|---|
| Trade WebSocket | /v1/ws/trade | Required | Real-time trading operations |
| Info WebSocket | /v1/ws/info | None | Public market data streams |
| Subscriptions WebSocket | /v1/ws/subscriptions | Required | Real-time account data streams |
For connection URLs, see Environments.
Features
- Lower latency through persistent connections
- Real-time order status updates and market data
- Reduced overhead with no HTTP headers per message
- Bidirectional communication with server-pushed updates
- Single connection for multiple operations
Connection Management
Establishing Connection
// Trade WebSocket (authenticated)
const tradeWs = new WebSocket('wss://papi.synthetix.io/v1/ws/trade');
// Info WebSocket (public)
const infoWs = new WebSocket('wss://papi.synthetix.io/v1/ws/info');
// Subscriptions WebSocket (authenticated)
const subscriptionsWs = new WebSocket('wss://papi.synthetix.io/v1/ws/subscriptions');
// Connection handlers
ws.onopen = () => {
console.log('WebSocket connected');
// Send authentication or subscribe to channels
};
ws.onclose = (event) => {
console.log('WebSocket disconnected:', event.code, event.reason);
// Implement reconnection logic
};
ws.onerror = (error) => {
console.error('WebSocket error:', error);
};
ws.onmessage = (event) => {
const message = JSON.parse(event.data);
// Handle incoming messages
};Message Format
All WebSocket messages use JSON format:
Client to Server
{
"id": "unique-request-id",
"method": "post",
"params": {
"action": "placeOrders",
"nonce": 1704067200000,
"signature": { "v": 27, "r": "0x...", "s": "0x..." },
"subAccountId": "123456789",
// Method-specific parameters follow
"orders": [{ /* order details */ }]
}
}Server to Client (Response)
{
"id": "unique-request-id",
"status": "ok",
"response": {
// Method-specific response data
},
"request_id": "5ccf215d37e3ae6d",
"timestamp": 1704067200000
}Server to Client (Error)
{
"id": "unique-request-id",
"status": "error",
"error": {
"code": "ERROR_CODE",
"message": "Error description",
"details": { /* optional context */ }
},
"request_id": "5ccf215d37e3ae6d",
"timestamp": 1704067200000
}Server to Client (Push Notification)
{
"method": "notificationType",
"data": {
// Event-specific data
},
"timestamp": 1704067200000
}Authentication
The Trade WebSocket requires authentication on connection:
{
"id": "auth-1",
"method": "auth",
"params": {
"message": "{\"types\":{...},\"primaryType\":\"AuthMessage\",\"domain\":{...},\"message\":{\"subAccountId\":\"12345\",\"timestamp\":1640995200,\"action\":\"websocketAuth\"}}",
"signature": "0x1234567890abcdef..."
}
}Heartbeat & Keep-Alive
WebSocket connections require periodic heartbeats:
- Ping Interval: Send ping every 30 seconds
- Pong Timeout: Expect pong within 10 seconds
- Auto-disconnect: After 24 hours (reconnect required)
// Heartbeat implementation
const HEARTBEAT_INTERVAL = 30000; // 30 seconds
function startHeartbeat(ws) {
const interval = setInterval(() => {
if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({
id: "ping-" + Date.now(),
method: "ping",
params: {}
}));
}
}, HEARTBEAT_INTERVAL);
ws.addEventListener('close', () => {
clearInterval(interval);
});
}Reconnection Strategy
Implement automatic reconnection with exponential backoff:
class ReconnectingWebSocket {
constructor(url) {
this.url = url;
this.reconnectAttempts = 0;
this.maxReconnectAttempts = 5;
this.connect();
}
connect() {
this.ws = new WebSocket(this.url);
this.ws.onopen = () => {
console.log('Connected');
this.reconnectAttempts = 0;
this.onopen?.();
};
this.ws.onclose = () => {
this.reconnect();
};
this.ws.onerror = (error) => {
console.error('WebSocket error:', error);
this.onerror?.(error);
};
this.ws.onmessage = (event) => {
this.onmessage?.(event);
};
}
reconnect() {
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
console.error('Max reconnection attempts reached');
return;
}
const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);
this.reconnectAttempts++;
console.log(`Reconnecting in ${delay}ms...`);
setTimeout(() => this.connect(), delay);
}
send(data) {
if (this.ws.readyState === WebSocket.OPEN) {
this.ws.send(data);
} else {
throw new Error('WebSocket not connected');
}
}
}Message Examples
{
"id": "sub-1",
"method": "subscribe",
"params": {
"type": "orderbook",
"symbol": "BTC-USDT"
}
}Error Handling
WebSocket errors require special handling:
function handleWebSocketError(error) {
switch (error.code) {
case 1006:
console.error('Abnormal closure - network issue');
break;
case 1008:
console.error('Policy violation');
break;
case 1011:
console.error('Server error');
break;
default:
console.error('Unknown error:', error);
}
}Security
- All connections use WSS (WebSocket Secure)
- Authentication required for trading endpoints
- Rate limits apply to prevent abuse
- IP-based connection limits
Testing
Test WebSocket connections using wscat:
# Install wscat
npm install -g wscat
# Connect to info endpoint
wscat -c wss://papi.synthetix.io/v1/ws/info
# Send subscription
> {"id":"sub-1","method":"subscribe","params":{"type":"orderbook","symbol":"BTC-USDT"}}Common Issues
| Issue | Cause | Solution |
|---|---|---|
| Connection drops | Network instability | Implement auto-reconnect |
| No heartbeat response | Connection stale | Reconnect immediately |
| Auth failures | Invalid signature | Check signing implementation |
| Missing messages | No error handling | Add comprehensive error handlers |
Next Steps
- Trade WebSocket - Real-time trading
- Info WebSocket - Market data streams
- Timeouts & Heartbeats - Connection management
- Authentication - Signing implementation