Automatic Reconnection
WebSocket connections will eventually disconnect -- due to network issues, server deployments, or the 1-hour maximum connection lifetime. Your client must implement automatic reconnection.
Reconnection Strategy
Use exponential backoff with jitter to avoid all your clients reconnecting at the same instant:
class InsightXWebSocket {
constructor(apiKey) {
this.url = `wss://api.insightx.network/ws/?api-key=${apiKey}`;
this.subscriptions = []; // track subscriptions for re-subscribe
this.backoff = 500; // initial backoff in ms
this.maxBackoff = 30000; // max backoff: 30s
this.connect();
}
connect() {
this.ws = new WebSocket(this.url);
this.ws.onopen = () => {
this.backoff = 500; // reset backoff on successful connect
};
this.ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
if (msg.op === "ready") {
// Re-subscribe to previously active topics
this.resubscribe();
}
if (msg.op === "event") {
this.handleEvent(msg);
}
};
this.ws.onclose = (event) => {
if (event.code === 4401) {
console.error("Authentication failed. Not reconnecting.");
return;
}
// Reconnect with exponential backoff + jitter
const jitter = Math.random() * this.backoff * 0.5;
const delay = this.backoff + jitter;
console.log(`Reconnecting in ${Math.round(delay)}ms...`);
setTimeout(() => this.connect(), delay);
this.backoff = Math.min(this.backoff * 2, this.maxBackoff);
};
}
subscribe(network, address, stream) {
this.subscriptions.push({ network, address, stream });
this.ws.send(JSON.stringify({
op: "subscribe",
network, address, stream
}));
}
resubscribe() {
if (this.subscriptions.length === 0) return;
this.ws.send(JSON.stringify({
op: "subscribe",
mode: "replace",
subs: this.subscriptions
}));
}
handleEvent(msg) {
// Your event handling logic here
console.log(`[${msg.stream}] ${msg.network}:${msg.address}`, msg.data);
}
}
const client = new InsightXWebSocket("your-api-key-here");
client.subscribe("sol", "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", "clusters");Python Example
import asyncio
import json
import websockets
API_KEY = "your-api-key-here"
URL = f"wss://api.insightx.network/ws/?api-key={API_KEY}"
SUBSCRIPTIONS = [
{"network": "sol", "address": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", "stream": "clusters"},
]
async def connect():
backoff = 0.5
max_backoff = 30.0
while True:
try:
async with websockets.connect(URL) as ws:
backoff = 0.5 # reset on successful connect
# Wait for ready
ready = json.loads(await ws.recv())
assert ready["op"] == "ready"
# Re-subscribe
await ws.send(json.dumps({
"op": "subscribe",
"mode": "replace",
"subs": SUBSCRIPTIONS,
}))
# Process events
async for raw in ws:
msg = json.loads(raw)
if msg["op"] == "event":
print(f"[{msg['stream']}] {msg['network']}:{msg['address']}")
except websockets.ConnectionClosedError as e:
if e.code == 4401:
print("Authentication failed. Check your API key.")
return
print(f"Disconnected ({e.code}). Reconnecting in {backoff:.1f}s...")
except Exception as e:
print(f"Error: {e}. Reconnecting in {backoff:.1f}s...")
await asyncio.sleep(backoff)
backoff = min(backoff * 2, max_backoff)
asyncio.run(connect())Keep-Alive Pings
Send a ping message every 30 seconds to keep the connection alive and detect stale connections early.
setInterval(() => {
if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({ op: "ping", id: Date.now().toString() }));
}
}, 30000);Use Bulk Subscriptions
When subscribing to multiple addresses, use the subs array instead of sending individual messages. This reduces round-trips and is processed more efficiently.
{
"op": "subscribe",
"subs": [
{ "network": "sol", "address": "addr1...", "stream": "clusters" },
{ "network": "sol", "address": "addr2...", "stream": "clusters" },
{ "network": "sol", "address": "addr3...", "stream": "clusters" }
]
}Track Subscriptions Client-Side
Maintain a local list of your active subscriptions so you can re-subscribe after reconnecting. Use mode: "replace" on reconnect to ensure a clean state without duplicates.
Handle the Connection Lifetime
Connections have a maximum lifetime of 1 hour. After this, the server closes the connection. Your reconnection logic handles this automatically, but be aware that:
- You will receive a close frame when the lifetime is reached.
- The reconnect should be immediate (no backoff needed).
- All data delivered before the close is reliable.
- Events that occur during the brief reconnect window (typically under 1 second) may be missed.
Use Full-State Replacement
Each cluster event contains the complete, current cluster state for a token -- not a diff or delta. You can safely replace your local state with the contents of each new event without needing to merge or reconcile.
Don't Rely on Cross-Token Ordering
Events for different tokens may arrive in any order. Use the timestamp field in each event if you need to sequence updates.
Monitor Your Topic Count
The topics_now field in subscribe/unsubscribe responses tells you how many topics are active. Monitor this to stay within your plan limits and avoid TOPIC_LIMIT errors.