> ## Documentation Index
> Fetch the complete documentation index at: https://docs.novig.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Heartbeat & Connection Management

> Keep WebSocket connections alive and handle reconnection

## Heartbeat Mechanism

The NBX WebSocket API uses a client-initiated ping/pong heartbeat to keep connections alive. The server does not send pings on its own — it only responds to pings sent by the client.

The connection is dropped by the upstream load balancer after **300 seconds of no traffic in either direction**. Any message — a market update from the server, a subscribe from you, a ping, or a pong — resets that idle timer. In practice this means you only need to send a ping during *quiet* periods; while the server is actively pushing data to you, the connection stays alive on its own.

### Recommended pattern

Track the timestamp of the last message you received. If that timestamp is older than some threshold (e.g. 30 seconds), send a ping. This avoids unnecessary pings during active periods and guarantees the connection stays warm during quiet ones.

### Client Ping

Send a ping message after a period of no inbound traffic:

```json theme={null}
{
  "event": "ping"
}
```

### Server Response

The server replies with a pong message:

```json theme={null}
{
  "event": "pong",
  "data": {
    "message": "pong"
  }
}
```

<Warning>
  The connection is dropped after **300 seconds** with no messages flowing in either direction. Any inbound or outbound message resets the idle timer.
</Warning>

### Example Implementation

<CodeGroup>
  ```javascript JavaScript/TypeScript theme={null}
  import WebSocket from 'ws';

  const IDLE_PING_THRESHOLD_MS = 30_000;
  const CHECK_INTERVAL_MS = 10_000;

  const ws = new WebSocket('wss://api.novig.us/tape', {
    headers: {
      'Authorization': `Bearer ${ACCESS_TOKEN}`
    }
  });

  let lastReceivedAt = Date.now();
  let idleCheck;

  ws.on('open', () => {
    lastReceivedAt = Date.now();
    // Send a ping only if we haven't heard from the server in IDLE_PING_THRESHOLD_MS
    idleCheck = setInterval(() => {
      if (Date.now() - lastReceivedAt >= IDLE_PING_THRESHOLD_MS) {
        ws.send(JSON.stringify({ event: 'ping' }));
      }
    }, CHECK_INTERVAL_MS);
  });

  ws.on('message', (data) => {
    lastReceivedAt = Date.now();
    const message = JSON.parse(data.toString());

    // Server responds to each client ping with a pong
    if (message.event === 'pong') {
      return;
    }

    // Handle other messages...
  });

  ws.on('close', () => {
    clearInterval(idleCheck);
  });
  ```
</CodeGroup>
