Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Error Handling & Reconnection

HTTP errors (during handshake)

If authentication or rate limiting fails, the server rejects the WebSocket upgrade with an HTTP error:

HTTP CodeReasonAction
400Malformed requestFix your WebSocket client
401Missing API keyAdd X-API-Key header
403Invalid, revoked, or expired keyContact administrator for a new key
429Rate limit or connection limit exceededWait and retry with backoff

WebSocket close codes

Once connected, the server may close your connection with a close frame. The reason field tells you why:

CodeReasonMeaningShould reconnect?
1000key_expiredYour API key’s expiration date has passedNo – get a new key
1000key_revokedAdministrator revoked your keyNo – get a new key
1008too_slowYou fell 10+ messages behind (lagging consumer)Yes
1008rate_limit_exceededYou sent too many messages to the server (>3/min)Yes – stop sending messages
1009frame_too_largeYou sent a frame larger than 1 KBYes – stop sending large frames

Exponential backoff

Attempt 1: wait 1 second
Attempt 2: wait 2 seconds
Attempt 3: wait 4 seconds
Attempt 4: wait 8 seconds
...
Cap: 300 seconds (5 minutes)

Decision logic

On disconnect:
  |
  |-- Close reason = "key_expired" or "key_revoked"?
  |     → Stop. Your key is no longer valid.
  |
  |-- Close reason = "rate_limit_exceeded"?
  |     → Wait 60s, then reconnect. Stop sending messages.
  |
  |-- Close reason = "too_slow"?
  |     → Reconnect immediately. Process messages faster.
  |
  |-- HTTP 429?
  |     → Back off. You're connecting too frequently.
  |
  |-- Unexpected disconnect / network error?
        → Reconnect with exponential backoff.

Heartbeat-based health check

Monitor heartbeats to detect silent disconnections:

import asyncio

HEARTBEAT_TIMEOUT = 35  # seconds

async def monitor_connection(ws):
    while True:
        try:
            msg = await asyncio.wait_for(ws.recv(), timeout=HEARTBEAT_TIMEOUT)
            data = json.loads(msg)
            if data["type"] == "announcement":
                handle_announcement(data)
        except asyncio.TimeoutError:
            print("No heartbeat received -- reconnecting")
            break  # Exit loop and reconnect

The server sends heartbeats every 30 seconds. If you receive nothing for 35 seconds, assume the connection is dead and reconnect.