Skip to main content
You hold one WebSocket open for everything that happens to your account or your maker: RFQs, quotes, acceptances, on-chain operations, positions and balances. It starts with a snapshot of your state, then streams every change in order. Each change carries a cursor, so a dropped connection resumes where it stopped. Your bot connects with its key, takes a snapshot, applies events in order, and resumes from its saved place after a drop Your bot connects with its key, takes a snapshot, applies events in order, and resumes from its saved place after a drop

Pick a socket

A maker that also trades as a taker opens two connections, one per key. Server integrations price from their own Hyperliquid feed and read the market catalog over List markets. The market data socket is for apps.

How a connection runs

1

Connect

Open the socket with the totalis.realtime.v2 subprotocol and your key as Authorization: Bearer <key> on the upgrade. A browser cannot set that header, so it sends a second subprotocol, auth.<base64url of the key>, instead.
2

Subscribe

Send one subscribe frame. Include your saved cursor if you have one. The server answers with subscriptionResponse, whose resume says what follows.
3

Load the snapshot

On SNAPSHOT or RESET, a snapshot frame carries your open RFQs, quotes, acceptances and operations, and the cursor they match. Replace your local state with it. Balances, positions and maker capital are not in the snapshot: read them over HTTP now.
4

Apply events

Each event frame is one committed change. Apply it, then save its cursor. Delivery is at least once, so skip any event whose cursor.sequence is not above the one you saved.
5

Resume

After a drop, reconnect and subscribe with your saved cursor. On REPLAY you get only the events you missed. Resuming needs the replay:read scope, which the taker scopes do not include, so add it to the key.
The server sends a WebSocket ping every heartbeat_interval_ms. WebSocket libraries answer with a pong on their own. There is no unsubscribe: to change the subscription, open a new connection.

One trade on the socket

The same trade, seen from each side: An RFQ that reaches expires_at, and a quote that reaches expiry, send no event. Track both timestamps yourself. Position, capital and wallet balance events are pointers. They name what changed and the block it changed in, not the new state. Read Get position, Get maker capital or Get balances when one arrives. Every event type and payload: Account channel and Maker channel.

When the server closes

Every close carries an error code as its reason. Its retry says what to do. The replay window and the heartbeat interval are on Limits. A cursor older than the window resumes as RESET. The stream is the only replay: there is no HTTP event log.

Example

A Node client that resumes, dedupes and reconnects. It uses the ws package, which can set the Authorization header. The TypeScript SDK wraps all of this in TotalisStream.
Save the cursor where it survives a restart, in the same transaction as the state you applied, so the two never disagree.

Socket or reads

Connection

Every frame, the subscribe response and the resume rules.

Account channel

Every taker event and snapshot class.

Maker channel

Every maker event, snapshot class and the scopes each needs.

Market data

Public reference prices for browsers.