Skip to main content
Lighter has six WebSocket channels. Four accept live subscriptions, and all six support historical replay. Live messages use the same subscribe command and data envelope as live Hyperliquid channels, with the payload shapes documented on this page.

Endpoint and access

Connect to wss://api.0xarchive.io/ws with the same server-side authentication as any other channel. See WebSocket connection. The stream.0xarchive.io endpoint does not carry Lighter channels: a Lighter subscribe there returns an error that names wss://api.0xarchive.io/ws. Every plan, including Free, can subscribe to the live Lighter channels. Each delivered message is metered like other WebSocket data; see Credits. For lighter_orderbook, each book sent is one metered message, so the interval you choose caps the order-book message rate. Your plan’s subscription and connection limits apply, and each connection accepts at most 10 subscribe operations per second. See WebSocket tier limits.

Subscribe

Use the Lighter symbols returned by GET /v1/lighter/instruments. Symbols are case-insensitive on subscribe, and the acknowledgment returns the symbol as that route lists it, which is uppercase for markets such as BTC and ETH. Match data messages to subscriptions without regard to case. Keep Lighter subscriptions in the Lighter family even when the same symbol text is also a Hyperliquid market.
The server confirms each subscription before you should treat it as active:
To stop a stream, send unsubscribe with the same channel and symbol:
Sending subscribe again for a channel and symbol you already hold replaces that subscription. Use this to change interval_ms without unsubscribing first. The server sends a new subscribed acknowledgment. The subscription limit is checked before the replacement, so if the connection is already at its plan’s subscription limit, unsubscribe first, then subscribe with the new interval_ms. Live Lighter data arrives in the envelope type, channel, coin, symbol, and data, with type set to data. The data payload depends on the channel, as described below. Notices arrive as error messages; see Errors and lag notices.

lighter_orderbook

Each message is a full top-20 L2 book, not a diff. Replace your local book with every message instead of applying it as a change. This example is a real message shortened to three levels per side; live messages carry up to 20 levels per side.

Order-book rate and interval_ms

The server sends the newest book at most once per interval. The default interval is one book per second. To choose another rate, pass interval_ms on subscribe with a value from 100 to 5000, inclusive. When the book changes several times within one interval, only the newest book is sent, and a newer book is never replaced by an older one. The interval is a maximum rate, not a heartbeat. A book is sent only when Lighter publishes an update, so quiet markets send fewer books than the interval allows. On subscribe, the current book is sent immediately when one is available. An illiquid market can go minutes without a change, so the first book can stay current for a long time. interval_ms is accepted only on lighter_orderbook. See Errors and lag notices for the messages returned by an invalid value.

lighter_trades

Each message carries an array of fills. Every trade appears as two legs, one per side, with the same tid. This example is a real message with both legs of one trade:
Count trades by distinct tid, not by array length. Compute volume from sz on one leg per tid; adding both legs doubles it. Keep a record of tid values you have processed so a trade is counted once across messages.

Preliminary and finalized trades

Live trades are delivered as they happen and are preliminary. The finalized record, which adds fields the live stream does not carry, such as fees, is served by GET /v1/lighter/trades/{symbol}. That route returns only reconciled trades; meta.finalized_through reports how far reconciliation has reached. GET /v1/lighter/trades/{symbol}/recent serves the preliminary tier over REST. See Trades for both tiers.

lighter_open_interest and lighter_funding

Both channels carry the same market-context message. Subscribe to the channel name that fits your code. If you subscribe to both for one symbol, each update arrives on both channels and each message is metered. This example is a real message:
Numeric values are decimal strings. Updates arrive as Lighter publishes them, about once per second per market. On subscribe, the latest values are sent immediately when they are available. The message has no timestamp field, so record the receive time if your application needs one.

Errors and lag notices

Rejected commands and stream notices arrive as error messages: {"type":"error","message":"..."}. Log the full message text. lighter_orderbook does not send drop notices for books skipped by its interval. Each book is a full state, so skipping an older book loses nothing, and a newer book is never replaced by an older one. Move message handling off the socket callback when processing is slow, and watch for these notices in the same place you watch reconnects and gaps. See WebSocket limits for consumer sizing.

Live and replay message shapes

Replay is available on all six Lighter channels, and op: "replay" returns historical_data rows in the stored Lighter shape, not the live shape above. Use separate parsers for live data messages and replay historical_data messages. A bounded Lighter replay looks like this:
A replay reads stored history and completes with replay_completed; it does not continue into the live stream. See WebSocket replay for replay controls, gap events, and run manifests.

Server-side example

Keep the API key on the server. This Node.js example subscribes to BTC books at two per second and to BTC trades, and counts each trade once:
Use WebSocket channels for the full channel matrix, Real-time streams for live subscription design, Lighter REST for current and historical Lighter routes, and WebSocket message schema for the command and event contract.
Last modified on September 25, 2026