Skip to main content

Connection Management

Best practices for maintaining reliable WebSocket connections.

Subscription Management

Subscribe

Subscribe to one or multiple streams:
{
  "method": "subscribe",
  "subscription": [
    {"type": "ticker", "symbol": "BTC-USD"},
    {"type": "trades", "symbol": "ETH-USD"},
    {"type": "account", "user": "FuueqefENiGEW6uMqZQgmwjzgpnb85EgUcZa5Em4PQh7"}
  ]
}
Response:
{
  "type": "subscriptionResponse",
  "topics": [
    "ticker.BTC-USD",
    "trades.ETH-USD",
    "account.FuueqefENiGEW6uMqZQgmwjzgpnb85EgUcZa5Em4PQh7"
  ]
}
Save the topic strings for unsubscription.

Unsubscribe

Unsubscribe using the topic string:
{
  "method": "unsubscribe",
  "topic": "ticker.BTC-USD"
}
Response:
{
  "type": "unsubscribeResponse",
  "topic": "ticker.BTC-USD"
}

Reconnection Strategy

WebSocket connections can drop due to network issues. Always implement reconnection logic.
class BulkWebSocket {
  constructor(url) {
    this.url = url;
    this.ws = null;
    this.reconnectDelay = 1000;
    this.maxReconnectDelay = 30000;
    this.subscriptions = [];
    this.topics = [];
  }
  
  connect() {
    this.ws = new WebSocket(this.url);
    
    this.ws.on('open', () => {
      console.log('Connected');
      this.reconnectDelay = 1000; // Reset delay
      this.resubscribe();
    });
    
    this.ws.on('message', (data) => {
      const message = JSON.parse(data);
      this.handleMessage(message);
    });
    
    this.ws.on('close', () => {
      console.log('Disconnected. Reconnecting...');
      this.reconnect();
    });
    
    this.ws.on('error', (error) => {
      console.error('WebSocket error:', error);
    });
  }
  
  reconnect() {
    setTimeout(() => {
      console.log(`Reconnecting in ${this.reconnectDelay}ms...`);
      this.connect();
      
      // Exponential backoff
      this.reconnectDelay = Math.min(
        this.reconnectDelay * 2,
        this.maxReconnectDelay
      );
    }, this.reconnectDelay);
  }
  
  subscribe(subscription) {
    this.subscriptions.push(subscription);
    
    if (this.ws && this.ws.readyState === WebSocket.OPEN) {
      this.ws.send(JSON.stringify({
        method: 'subscribe',
        subscription: [subscription]
      }));
    }
  }
  
  resubscribe() {
    if (this.subscriptions.length > 0) {
      this.ws.send(JSON.stringify({
        method: 'subscribe',
        subscription: this.subscriptions
      }));
    }
  }
  
  handleMessage(message) {
    if (message.type === 'subscriptionResponse') {
      this.topics = message.topics;
      console.log('Subscribed to:', this.topics);
      return;
    }
    
    // Handle data messages
    console.log('Received:', message);
  }
}

// Usage
const ws = new BulkWebSocket('wss://exchange-ws1.bulk.trade');
ws.connect();
ws.subscribe({ type: 'ticker', symbol: 'BTC-USD' });

Ping-Pong Keepalive (Required)

The server uses an explicit WebSocket ping/pong liveness check. You must respond to server pings with pongs or the connection will be closed.
BehaviorDetail
Ping intervalServer sends a WebSocket ping frame every 30 seconds
Pong requirementClient must reply with a WebSocket pong frame
TimeoutIf a pong is still missing 10 seconds after a ping, the server disconnects the connection
Frame typePing/pong are transport-level WebSocket frames, not application (JSON) messages
Client recommendations:
  • Respond immediately to ping frames with pong. Many WebSocket client libraries (e.g. Node.js ws) do this automatically; if yours does not, handle the ping event and send a pong.
  • Do not treat ping/pong as application messages—they are separate from subscribe, post, and data messages.
  • If you are disconnected due to a missed pong, reconnect and resubscribe to your topics.
const WebSocket = require('ws');

const ws = new WebSocket('wss://exchange-ws1.bulk.trade');

// Node.js 'ws' automatically replies to ping with pong by default.
// To handle explicitly (e.g. for logging or custom behavior):
ws.on('ping', () => {
  ws.pong(); // optional: library often does this automatically
});

ws.on('pong', () => {
  // Server may send pong in response to client ping; keepalive is server-initiated
});

Rate Limits

Connection Limits
  • Maximum 100 subscriptions per connection
  • Maximum 1000 messages per second
  • Exceeding limits will result in disconnection
If you need more subscriptions, open multiple connections.

Best Practices

Network issues are inevitable. Your application should automatically reconnect with exponential backoff.
After reconnecting, you must resubscribe to all channels. Store your subscription list.
Messages may arrive out of order during high load. Use timestamps and sequence numbers.
The server sends a ping every 30 seconds. If the client does not reply with a pong within 10 seconds, the server disconnects. Use a client that responds to ping (or handle it explicitly) and always implement reconnection.
If no application message is received for a long period, consider the connection stale and reconnect. Note: the server will already disconnect if pong is not sent within 10 seconds of a ping.
Enable per-message deflate for bandwidth savings on high-frequency streams.
Use one connection for market data and another for trading to avoid mixing concerns.

Connection States

Monitor connection state to handle different scenarios:
StateDescriptionAction
CONNECTINGInitial connectionWait for open event
OPENConnected and readyCan send/receive messages
CLOSINGConnection closingStop sending messages
CLOSEDConnection closedReconnect if needed

Error Codes

Common WebSocket close codes:
CodeReasonAction
1000Normal closureNo action needed
1001Going awayReconnect
1002Protocol errorCheck message format
1003Unsupported dataCheck message content
1006Abnormal closureReconnect
1008Policy violationCheck rate limits
1011Server errorRetry with backoff

Monitoring Connection Health

Implement health checks to detect stale connections:
class ConnectionMonitor {
  constructor(ws, timeout = 60000) {
    this.ws = ws;
    this.timeout = timeout;
    this.lastMessage = Date.now();
    this.checkInterval = null;
  }
  
  start() {
    // Update timestamp on every message
    this.ws.on('message', () => {
      this.lastMessage = Date.now();
    });
    
    // Check health every 10 seconds
    this.checkInterval = setInterval(() => {
      const timeSinceLastMessage = Date.now() - this.lastMessage;
      
      if (timeSinceLastMessage > this.timeout) {
        console.warn('Connection stale, reconnecting...');
        this.ws.close();
      }
    }, 10000);
  }
  
  stop() {
    if (this.checkInterval) {
      clearInterval(this.checkInterval);
    }
  }
}

// Usage
const monitor = new ConnectionMonitor(ws);
monitor.start();

Multiple Connections

For high-throughput applications, consider using multiple connections:
// Connection 1: Market data
const marketDataWs = new WebSocket('wss://exchange-ws1.bulk.trade');
marketDataWs.on('open', () => {
  marketDataWs.send(JSON.stringify({
    method: 'subscribe',
    subscription: [
      { type: 'ticker', symbol: 'BTC-USD' },
      { type: 'trades', symbol: 'BTC-USD' }
    ]
  }));
});

// Connection 2: Account updates
const accountWs = new WebSocket('wss://exchange-ws1.bulk.trade');
accountWs.on('open', () => {
  accountWs.send(JSON.stringify({
    method: 'subscribe',
    subscription: [
      { type: 'account', user: PUBLIC_KEY }
    ]
  }));
});

// Connection 3: Trading
const tradingWs = new WebSocket('wss://exchange-ws1.bulk.trade');
// Use this for order placement/cancellation

Testing Connection

Test your WebSocket connection with a simple script:
const WebSocket = require('ws');

const ws = new WebSocket('wss://exchange-ws1.bulk.trade');

ws.on('open', () => {
  console.log('✓ Connected successfully');
  
  // Test subscription
  ws.send(JSON.stringify({
    method: 'subscribe',
    subscription: [{ type: 'ticker', symbol: 'BTC-USD' }]
  }));
  
  setTimeout(() => {
    console.log('Test complete');
    ws.close();
  }, 5000);
});

ws.on('message', (data) => {
  const message = JSON.parse(data);
  
  if (message.type === 'subscriptionResponse') {
    console.log('✓ Subscribed to:', message.topics);
  } else {
    console.log('✓ Received update:', message.type);
  }
});

ws.on('error', (error) => {
  console.error('✗ Error:', error);
});

ws.on('close', () => {
  console.log('✓ Connection closed');
});