Skip to content

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:

EndpointPathAuthenticationPurpose
Trade WebSocket/v1/ws/tradeRequiredReal-time trading operations
Info WebSocket/v1/ws/infoNonePublic market data streams
Subscriptions WebSocket/v1/ws/subscriptionsRequiredReal-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

IssueCauseSolution
Connection dropsNetwork instabilityImplement auto-reconnect
No heartbeat responseConnection staleReconnect immediately
Auth failuresInvalid signatureCheck signing implementation
Missing messagesNo error handlingAdd comprehensive error handlers

Next Steps