Skip to main content
The PerpsWsClient provides streaming data over WebSocket. Connections are made directly to the DEX for lowest latency — the SDK discovers the WebSocket URL from GET /providers automatically. All incoming data is normalized to the same types used by REST responses, so your application logic works identically whether data comes from a REST call or a WebSocket event.
WebSocket support is provider-specific. Not all channels may be supported by every provider — check the provider’s documentation for available channels.

Setup

The PerpsWsClient lazily initializes connections — no WebSocket is opened until you call subscribe().

Subscribe

subscribe() is async and returns a Promise<() => void> — awaiting it yields the unsubscribe function. The descriptor’s dex field selects the venue (use the key from getProviders(), e.g., 'hyperliquid' or 'lighter').
subscribe() accepts an optional third argument, onStatus, a listener for the underlying connection’s health. It fires 'reconnecting' on a transient drop and the terminal 'disconnected' once auto-reconnect is abandoned, so consumers can surface a reconnecting/disconnected state instead of silently showing stale data.

getQuote

One-shot fill quote for size USD notional of symbol on a single venue: VWAP expected fill, price impact (bps), base-tier taker fee, and funding (perps only). This is a plain request/response service (not a WS subscription) — the streaming counterpart is subscribeQuote, and both share the GetQuoteParams shape. Cross-venue comparison is a consumer-side loop over client.providers.
GetQuoteParams: Throws a PerpsError when the provider plugin is not registered, no market matches the symbol and type, or on network / parsing errors.

subscribeQuote

Stream live fill quotes for a market. The provider’s WS plugin layers the quote on its orderbook channel, so a concurrent orderbook subscription on the same market shares a single wire subscription. Async; returns a Promise<() => void> unsubscribe function.
Throws a PerpsError when no WS provider factory is registered for params.provider, or no market matches the symbol and type.

Available Channels

marketsContext

Full context for every market on the DEX, keyed by marketId. Use this when a UI needs mark price, oracle price, 24h volume, open interest, or funding for the whole market list.

marketContext

Full context for a specific market. The event data is a MarketContext snapshot, so consumers should replace their cached context for that market when a new event arrives.

orderbook

L2 orderbook for a specific market. Fires on every book update. Pass an optional depth to limit price levels (provider default if omitted), and an optional priceStep to request a price granularity — the desired bucket width in quote currency (e.g. 10 buckets a BTC book into $10-wide levels). Providers that aggregate the book server-side honour priceStep best-effort; providers that stream the full book ignore it, and undefined requests full precision.

candle

OHLCV candle updates for a specific market and interval.

trades

Public trade prints for a specific market. Fires as trades execute on the venue.

orderUpdates

Live order status changes for a user’s orders. Requires the user’s wallet address.

fills

Live fill notifications for a user’s trades.

positions

Live position updates for a user.

spotBalances

Live spot balance updates for a user (e.g., USDC collateral held on the DEX).

accountSummary

Live account roll-up for a user — portfolio value, available margin, margin used, and unrealized PnL. Requires the user’s wallet address. Field coverage matches the venue’s own stream (e.g. Hyperliquid’s portfolioValue covers perps equity only; spot balances have their own spotBalances channel).

Multiple Subscriptions

You can subscribe to multiple channels simultaneously. Each returns an independent unsubscribe function.

Connection Lifecycle

  • Lazy connection — The WebSocket connects on the first subscribe() call for each DEX
  • Automatic reconnection — Jittered exponential backoff on disconnect. The first-retry delay is drawn once per socket from [500, 1500)ms (to de-synchronize reconnect storms) and grows exponentially, capped at 10s. After 10 attempts the socket is declared disconnected.
  • Resubscription — All active subscriptions are automatically re-sent after reconnection
  • Keepalive — 30-second ping/pong heartbeat to detect stale connections
  • Refcounted — Duplicate subscriptions to the same channel are deduplicated; the upstream subscription is removed only when the last listener unsubscribes
  • Manual reconnectws.reconnect(provider) forces an already-created provider whose socket reached terminal disconnected to reconnect with a fresh retry budget. It is a safe no-op when the provider is unknown or not terminal.

Cleanup

Always close the client when you’re done to release WebSocket connections:
Individual subscriptions can be cleaned up by calling the returned unsubscribe function. The underlying WebSocket connection stays open as long as at least one subscription is active on that DEX.

Type Safety

The subscribe method is fully typed — the callback receives the correct event type based on the subscription channel: