Skip to main content

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.

When a WebSocket connection drops, you typically lose every event that happened while you were disconnected. hypernode solves this with session replay: pass a session_id and since_seq cursor on reconnect, and the server replays every missed event that matches your subscription filter before resuming the live stream.

When to use this

  • Production trading systems where gaps in the event stream can cause desync between your local state and the real chain state
  • Fill monitors that track PnL and positions by watching fill events
  • Builder analytics tracking every fill attributed to your builder address
  • Compliance and audit pipelines that need to prove they received every event in a window
  • Any client that cares about reliability — brief network blips, client crashes, container restarts, load balancer reconnects
If you don’t care about gaps (sampling use cases, dashboards that refresh periodically), you don’t need session replay. It’s optional. Clients that don’t pass session_id work exactly as before.

How it works for the client

Every event on the WebSocket stream now carries a seq field. It’s a monotonically increasing integer — each event gets a unique seq one higher than the last. Track the highest seq you’ve processed, and on reconnect you’ll get exactly what you missed. Example event with seq:
{
  "seq": 4404497,
  "type": "fill",
  "timestamp": 1776229763378,
  "consensus": "confirmed",
  "asset": "BTC",
  "user": "0x4b53ec7251039454d057757b6fde7e39e48b49a1",
  "data": {
    "coin": "BTC",
    "side": "B",
    "px": "74207.0",
    "sz": "0.02644",
    "dir": "Close Short",
    "closedPnl": "1.70538",
    "fee": "0.784813",
    "oid": 382009563898,
    "tid": 1094600066756112
  },
  "network": "mainnet"
}

Reconnect flow

Pass session_id and since_seq as query params when reconnecting:
wss://hyper.polynode.dev/ws?key=YOUR_KEY&session_id=<uuid>&since_seq=<last_seq>
After you send your subscribe message, the server responds with:
  1. A normal subscribed ack
  2. A reconnected notification listing how many events will be replayed
  3. The replayed events themselves (ordered by seq, already filtered)
  4. A replay_complete message
  5. Live events resume

Subscribed ack

{
  "action": "subscribed",
  "id": "default",
  "sub_id": 5,
  "total_subs": 2,
  "newest_seq": 4423387,
  "session_id": "84391b47-cc77-4b01-97df-ff0944d94d9f"
}

Reconnected notification

{
  "action": "reconnected",
  "id": "default",
  "from_seq": 4412795,
  "to_seq": 4423117,
  "total_events": 218,
  "total_chunks": 1
}

Replay complete

{
  "action": "replay_complete",
  "id": "default",
  "replayed": 218,
  "resume_seq": 4423117
}

Retention window

hypernode keeps up to 30 seconds of recent events available for replay. If you reconnect within that window, every missed event that matches your subscription filter is delivered before live events resume. If your cursor is older than the window, you’ll receive a gap signal instead.

Gap signal

{
  "action": "reconnected",
  "id": "default",
  "gap": true,
  "oldest_available_seq": 4423117,
  "newest_available_seq": 4612394,
  "message": "cursor too old, gap detected — reload snapshot state"
}
When you see gap: true, your cursor was older than the oldest replayable event. Reload any snapshot state you were maintaining (positions, order book, etc.) from REST and start fresh. In practice this rarely happens — most clients reconnect within a few seconds.

Full Python client

A reconnect-safe client that tracks the last seq and resumes automatically:
import asyncio, json, uuid, websockets

API_KEY = "pn_live_..."
SESSION = str(uuid.uuid4())

async def stream(last_seq=0):
    url = f"wss://hyper.polynode.dev/ws?key={API_KEY}&session_id={SESSION}"
    if last_seq > 0:
        url += f"&since_seq={last_seq}"

    async with websockets.connect(url) as ws:
        await ws.send(json.dumps({
            "action": "subscribe",
            "filters": {"action_types": ["fill"]}
        }))

        async for raw in ws:
            event = json.loads(raw)
            action = event.get("action")

            if action == "subscribed":
                print(f"Subscribed, newest_seq={event.get('newest_seq')}")
                continue
            if action == "reconnected":
                if event.get("gap"):
                    print("Gap detected — reload snapshot state")
                    # Reload positions, orderbook, etc. from REST here
                else:
                    print(f"Replaying {event['total_events']} missed events")
                continue
            if action == "replay_complete":
                print(f"Replay done, live at seq {event['resume_seq']}")
                continue

            # Normal event
            if event.get("seq"):
                last_seq = event["seq"]
            if event.get("type") == "fill":
                d = event["data"]
                print(f"{d['coin']} {d['sz']}@{d['px']} seq={event['seq']}")

    return last_seq

async def main():
    last = 0
    while True:
        try:
            last = await stream(last)
        except Exception as e:
            print(f"Disconnected ({e}), reconnecting with cursor {last}")
            await asyncio.sleep(1)

asyncio.run(main())
This client tracks the last seq it processed, and whenever the connection drops — for any reason — it reconnects with the cursor and receives exactly the events it missed.

Limits

LimitValue
Retention window30 seconds
Max events per replay10,000
Max events per chunk500
If you need longer retention or higher replay caps, reach out and we can tune these.