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

# Reconnection & Error Handling

> Handle disconnections, rate limits, and errors in production

WebSocket connections drop. Networks fail. Rate limits hit. This guide covers how to build a resilient client.

## Reconnection

hypernode WebSocket connections can drop due to server restarts, network issues, or idle timeouts. Your client must handle reconnection.

### Basic pattern

```typescript theme={null}
import WebSocket from "ws";

const API_KEY = "pn_live_YOUR_KEY";
const URL = `wss://hyper.polynode.dev/ws?key=${API_KEY}`;

let ws: WebSocket;
let reconnectDelay = 1000; // Start at 1 second
const MAX_DELAY = 30000;   // Cap at 30 seconds

function connect() {
  ws = new WebSocket(URL);

  ws.on("open", () => {
    console.log("Connected");
    reconnectDelay = 1000; // Reset on successful connect

    // Re-subscribe after every reconnect
    ws.send(JSON.stringify({
      action: "subscribe",
      filters: { action_types: ["order", "fill"] }
    }));
  });

  ws.on("message", (raw) => {
    const event = JSON.parse(raw.toString());
    handleEvent(event);
  });

  ws.on("close", (code, reason) => {
    console.log(`Disconnected: ${code} ${reason}`);
    scheduleReconnect();
  });

  ws.on("error", (err) => {
    console.error("WebSocket error:", err.message);
    // on("close") will fire after this
  });
}

function scheduleReconnect() {
  const jitter = Math.random() * 1000;
  const delay = Math.min(reconnectDelay + jitter, MAX_DELAY);
  console.log(`Reconnecting in ${Math.round(delay)}ms`);

  setTimeout(() => {
    reconnectDelay = Math.min(reconnectDelay * 2, MAX_DELAY);
    connect();
  }, delay);
}

connect();
```

### Python

```python theme={null}
import json
import time
import websocket

API_KEY = "pn_live_YOUR_KEY"
URL = f"wss://hyper.polynode.dev/ws?key={API_KEY}"

def on_open(ws):
    print("Connected")
    ws.send(json.dumps({
        "action": "subscribe",
        "filters": {"action_types": ["order", "fill"]}
    }))

def on_message(ws, message):
    event = json.loads(message)
    handle_event(event)

def on_close(ws, close_status_code, close_msg):
    print(f"Disconnected: {close_status_code}")

def on_error(ws, error):
    print(f"Error: {error}")

# websocket-client handles reconnection with run_forever
while True:
    try:
        ws = websocket.WebSocketApp(
            URL,
            on_open=on_open,
            on_message=on_message,
            on_close=on_close,
            on_error=on_error,
        )
        ws.run_forever()
    except Exception as e:
        print(f"Connection failed: {e}")
    time.sleep(5)
```

## Key rules

### Re-subscribe on every reconnect

Subscriptions are not persisted across connections. After reconnecting, you must send your subscribe messages again. Keep your subscription configs in a variable so you can replay them.

```typescript theme={null}
const MY_SUBSCRIPTIONS = [
  { action: "subscribe", filters: { assets: ["BTC"], action_types: ["order", "fill"] } },
  { action: "subscribe", filters: { addresses: ["0xabc..."] } },
];

ws.on("open", () => {
  for (const sub of MY_SUBSCRIPTIONS) {
    ws.send(JSON.stringify(sub));
  }
});
```

### No replay or gap-fill

hypernode does not replay missed events during a disconnection. If your connection drops for 5 seconds, events during that window are lost. Design your application to tolerate gaps.

If you need guaranteed delivery, combine the stream with periodic REST API polling:

```typescript theme={null}
// Stream for real-time updates
ws.on("message", handleEvent);

// Poll every 30 seconds as a safety net
setInterval(async () => {
  const response = await fetch("https://hyper.polynode.dev/v2/user/open-orders?address=0x...", {
    headers: { "x-api-key": API_KEY }
  });
  const orders = await response.json();
  reconcileState(orders);
}, 30000);
```

### Exponential backoff with jitter

Don't reconnect immediately in a tight loop. Use exponential backoff (1s, 2s, 4s, 8s, ...) with random jitter to avoid thundering herd problems.

## Error responses

### HTTP errors during handshake

| Status | Meaning                    | Action                                  |
| ------ | -------------------------- | --------------------------------------- |
| 401    | Invalid or missing API key | Check your key                          |
| 403    | Key is disabled            | Contact support                         |
| 429    | Connection limit reached   | Close other connections or upgrade tier |

### WebSocket errors after connection

Subscription errors are delivered as JSON messages:

```json theme={null}
{"error": "subscription limit reached for free tier (max 1)"}
```

Handle these in your message handler:

```typescript theme={null}
ws.on("message", (raw) => {
  const msg = JSON.parse(raw.toString());
  if (msg.error) {
    console.error("Server error:", msg.error);
    return;
  }
  handleEvent(msg);
});
```

## Rate limits

* **Connections per key:** determined by your tier (1 for free, 5 for starter, up to 500 for enterprise)
* **Subscriptions per connection:** determined by your tier
* **Messages:** no rate limit on inbound subscribe/unsubscribe messages
* **Events delivered:** no throttling, you get everything matching your filters

## Health checks

If you need to verify the service is up before connecting:

```bash theme={null}
curl https://hyper.polynode.dev/health
```

Returns `{"status":"ok"}` when the stream service is running.

## Browser clients

If running in a browser, handle tab visibility to avoid wasting resources:

```typescript theme={null}
document.addEventListener("visibilitychange", () => {
  if (document.hidden) {
    ws.close();
  } else {
    connect(); // Reconnect when tab becomes visible
  }
});
```
