Skip to main content
Coinversa streams every fill on Hyperliquid — native perps, the builder dexes, spot — over one websocket at block cadence, with the wallet attached to each fill and closedPnl, dir and startPosition already on it. Frame shapes mirror Hyperliquid’s own websocket API, so existing Hyperliquid client code is familiar. Twenty-one feeds are live today, five of them multi-wallet lists that follow up to 1,000 addresses on a single subscription. Live OHLC candles for any coin on any of 14 intervals are on every plan. Pro accounts can also stream the full L4 order book of any coin — every resting order with its wallet, as one snapshot followed by per-block diffs — through the l4Book feed, plus every liquidation as it happens, every accepted leverage and isolated-margin change, and the node’s own TWAP state machine, which opens with a snapshot of the TWAPs running right now. It is the third way into the same data, beside the REST API and the MCP server, and it uses the same API key. It is also a different protocol with its own rules: auth travels on the upgrade rather than in an X-API-Key header, refusals are HTTP statuses or close codes rather than problem details, and limits are capacity — connections and subscriptions — never per message. This section states those rules in full; the REST pages point here rather than repeating them. Endpoint: wss://ws.coinversa.ai Reference in the portal: developers.coinversa.ai/websockets

Connecting

Open the socket with your key on the HTTP upgrade, then send one subscribe frame per feed you want. The server acknowledges each and pushes a data frame whenever matching fills land in a block. Nothing is polled, and by default nothing is replayed on connect — you receive fills from the moment you subscribe onward. Pass a sequence to resume where a dropped connection left off instead. Every connection needs a key, including Free. There is no anonymous tier and no auth frame to send once the socket is open. The request path is ignored — /, /ws and /stream all upgrade the same.

Authentication

The key goes out with the upgrade request, so the socket is already authenticated by the time it opens. There are two places it can travel, and which one you use is decided by your runtime, not by preference.

Servers: Authorization header

Node, Python, Go, and anything else that can set request headers sends Authorization: Bearer cvsa_... on the upgrade. The Bearer scheme token is case-sensitive.

Browsers: bearer subprotocol

A browser WebSocket cannot set headers, so it offers the key as the second subprotocol after the literal bearer: new WebSocket(url, ['bearer', key]). The server selects and echoes bearer; the key is never echoed.
Whichever input is present decides on its own. A bad Authorization header is refused even if a valid subprotocol rides beside it; the subprotocol is read only when the header is absent. The key is never accepted in the URL — ?token=, ?apiKey= and ?key= are refused exactly like no key at all, and a key in a URL is a key in every access log between you and us.
A key shipped to other people’s browsers is a key you have published. The subprotocol path is for your own dashboards and tools. For anything served to other users, hold the socket on a server you control and relay what the page needs over your own transport.

The first frame

The first frame on every live connection is connected, and its limits are what this account may do: how many connections it may hold in total, how many concurrent subscriptions per connection, and which feeds its tier includes. They are the account’s resolved numbers — enterprise overrides already applied — so read them back rather than hard-coding your plan. The tier is fixed for the life of the socket; moving to another key or plan means reconnecting.

When the handshake is refused

A refused key is fatal to the connection: the upgrade never completes, no socket exists, and nothing you write in a message handler will run. This is a different code path from the error channel, and it is the one most clients get wrong. A browser cannot read the status of a refused upgrade, so for a subprotocol client the handshake completes and the socket is closed at once with the verdict as a close code — the HTTP status plus 4000 — and the same reason text. These close codes are a public wire contract; match on them. A 1006 with no reason is the browser saying the handshake itself failed or the link dropped, and it will not say which. No Retry-After comes with a 429, and a connection that died silently can hold its slot until keepalive notices — roughly 40 seconds. Client libraries that hide the response surface a refused upgrade as close code 1002, reason Expected 101 status code, with zero frames received; pick an API that hands you the status.

Where to go next

Quickstart

A complete client in Browser, Node, Python or Go — reconnect, resubscribe, sequence ordering, heartbeat.

Feeds and the fill object

The firehose, one coin, one wallet, a list of wallets, liquidations, leverage changes, candles, the order book, the three TWAP views — and every field on a fill.

Wire protocol

Every frame, the exact error strings, and how sequence numbers work.

Plan limits

Connections per account, subscriptions per connection, fair-use bandwidth.