Skip to main content

Connection

Connect to the WebSocket server:
wss://ws.notional.xyz

Protocol

The Notional WebSocket API uses JSON messages for bidirectional communication:
  • Client → Server: Subscribe, unsubscribe, ping
  • Server → Client: Data updates, confirmations, errors

Quick Start

const ws = new WebSocket('wss://ws.notional.xyz');

ws.onopen = () => {
  // Subscribe to user fills
  ws.send(JSON.stringify({
    method: 'subscribe',
    subscription: {
      type: 'userFills',
      user: '0x742d35cc6634c0532925a3b844bc9e7595f0beb'
    }
  }));
};

ws.onmessage = (event) => {
  const message = JSON.parse(event.data);

  if (message.channel === 'userFills') {
    console.log('New fills:', message.data.fills);
  }
};

Subscription Types

User-Specific Subscriptions

Require a user parameter (your wallet address in lowercase):

Global Subscriptions

No user parameter needed - available to all clients:

Message Format

Subscribe

{
  "method": "subscribe",
  "subscription": {
    "type": "userFills",
    "user": "0x742d35..."
  }
}

Unsubscribe

{
  "method": "unsubscribe",
  "subscription": {
    "type": "userFills",
    "user": "0x742d35..."
  }
}

Ping

{
  "method": "ping"
}

Server Messages

Data Update:
{
  "channel": "userFills",
  "data": { ... }
}
Subscription Confirmation:
{
  "channel": "subscriptionResponse",
  "data": {
    "method": "subscribe",
    "subscription": { ... }
  }
}
Error:
{
  "channel": "error",
  "data": {
    "message": "Error description"
  }
}

Multiple Subscriptions

Subscribe to multiple channels over a single connection:
ws.onopen = () => {
  const user = '0x742d35...';

  // Subscribe to multiple channels
  ws.send(JSON.stringify({
    method: 'subscribe',
    subscription: { type: 'userFills', user }
  }));

  ws.send(JSON.stringify({
    method: 'subscribe',
    subscription: { type: 'accountSummary', user }
  }));

  ws.send(JSON.stringify({
    method: 'subscribe',
    subscription: { type: 'newBlocks' }
  }));
};

ws.onmessage = (event) => {
  const message = JSON.parse(event.data);

  switch (message.channel) {
    case 'userFills':
      handleFills(message.data);
      break;
    case 'accountSummary':
      handleAccount(message.data);
      break;
    case 'newBlocks':
      handleBlock(message.data);
      break;
  }
};

Connection Management

Heartbeat

Send pings every 30 seconds to keep the connection alive:
setInterval(() => {
  if (ws.readyState === WebSocket.OPEN) {
    ws.send(JSON.stringify({ method: 'ping' }));
  }
}, 30000);

Idle Timeout

  • Connections idle for 60 seconds are automatically closed
  • Send pings every 30 seconds to prevent timeout

Reconnection

If the connection drops:
  1. Exponential Backoff: Wait before reconnecting (1s, 2s, 4s, etc.)
  2. Resubscribe: Re-establish all subscriptions after reconnect
  3. Sync State: Fetch latest data via REST API if needed

Compression

Per-message deflate compression is enabled by default for bandwidth efficiency.

Error Handling

Common error messages:
{
  "channel": "error",
  "data": {
    "message": "Binary messages not supported"
  }
}
{
  "channel": "error",
  "data": {
    "message": "Unknown method: foo"
  }
}
{
  "channel": "error",
  "data": {
    "message": "Invalid message format"
  }
}

Best Practices

Address Format

  • Always use lowercase addresses: 0x742d... not 0x742D...

Message Handling

  • Parse messages in try-catch blocks
  • Check channel field in every message
  • Handle errors gracefully

Connection Management

  • Subscribe after onopen event fires
  • Resubscribe on reconnection
  • Send pings every 30 seconds
  • Unsubscribe before closing connection

Performance

  • Share WebSocket connections across subscriptions
  • Filter client-side for high-volume streams
  • Batch UI updates to avoid render overload
  • Consider rate limiting for global subscriptions

Connection Limits

  • Max Payload: 16 KB per message
  • Idle Timeout: 60 seconds
  • Max Connections: Unlimited (subject to fair use)

Status Monitoring

Check WebSocket server health:
curl https://api.notional.xyz/health
Look for fillsPoller status to verify data availability.