l4BookUpdates
Stream real-time L4 orderbook updates per block.
Every live data message includes both routing identifiers with the same value:
{"type": "l4BookUpdates", "channel": "l4BookUpdates"}Other payload fields are omitted above. Existing clients may continue routing on either field.
Subscribe
{
"method": "subscribe",
"subscription": {
"type": "l4BookUpdates",
"coins": ["ETH", "BTC"]
}
}Parameters:
coins
string[]
No
Coins to subscribe to. Omit for all markets (needs l4BookUpdatesAll add-on).
marketTypes
string[]
No
All-markets only. Filters delivery by market type β see All-markets filter. Rejected if combined with coins.
merge
bool
No
Default false. When true, union these coins into the connection's existing coin-list l4BookUpdates slots instead of opening a new stream. Subscribe-only β rejected on an unsubscribe, on the all-markets firehose, and on other subscription types.
Planned change: merge will become the default. Today a coin-list subscribe defaults to merge: false, so a second subscribe on the same connection opens a second stream. In a future release that default flips to merge: true: repeated coin-list subscribes will union into one stream delivering a single frame per block, and unsubscribe with coins will subtract from it.
What to do now β set merge explicitly on every coin-list subscribe. "merge": true opts into the future behaviour today. "merge": false pins the current behaviour and keeps working after the flip. Only clients that send neither will change behaviour.
The flag is not being removed; it stays accepted after the default changes.
You are unaffected either way if you already batch every coin into a single subscribe, or open one connection per coin list. The change only alters what happens when one connection subscribes to coin lists more than once.
All-markets filter
When coins is omitted, the optional marketTypes field restricts the firehose to specific market types. Each entry is "perp", "spot", "outcome", or the wildcard "*" (alone) for "every type the server currently tracks":
Omitting marketTypes defaults to ["perp"] β outcome and spot markets do not appear unless you opt in. The default never grows; new market types must be added to your marketTypes array explicitly. Pass ["*"] to auto-opt-in to future types.
A second subscribe with a different marketTypes value replaces the previous filter rather than coexisting with it.
Unsubscribe
Name coins to remove. With one coin-list slot those coins are subtracted (remaining coins keep streaming, gap-free). With several coin-list slots the request must match one slot's coin set exactly:
Omit coins to drop the all-markets firehose only. That does not remove a coin-list subscription. To clear the coin-list, unsubscribe the coins you hold (the last coin drops the subscription).
The same boundary rule applies in reverse: a block whose fan-out began before the unsubscribe committed still carries the coin set it started with, and that frame is delivered before the unsubscribe ack. No frame after the ack contains the dropped coins.
Update data format
Updates are streamed per-block with order changes for subscribed markets:
Diff types
new
New order placed
coin, oid, user, side, px, sz, insertBefore (optional)
update
Order size changed (partial fill)
coin, oid, sz
remove
Order removed (filled/cancelled)
coin, oid
Field definitions
height
int
Block height
timestamp
int
Block timestamp (ms since epoch)
coin
string
Market symbol (e.g., "ETH", "BTC")
oid
int
Unique order ID
user
string
User address (0x...)
side
string
"B" = bid, "A" = ask
px
string
Price as decimal string
sz
string
Size as decimal string
insertBefore
int (oid), optional
On new diffs only: insert this order into its price level's queue immediately before the resting order with this oid (Hyperliquid ALO priority placement). Absent = append to the tail of the queue. If the target oid is no longer at the level, append to the tail.
Examples
Common errors
The subscription feedback and warning shapes are documented in the WebSocket overview. Invalid names are rejected together, for example invalid coin name(s): ["BTC-USD","ETH USD"]. More than 200 names in one request returns too_many_coins with requested and maximum details. A well-formed inactive name succeeds with an inactive_coins warning instead of silently producing no data.
Too many coins- Either one request named more than 200 coins, or amerge: truesubscribe would push the stored set past your tier's coin cap. Both leave the existing subscription unchanged.Subscribing to all markets requires permission- Needs add-on for all booksRate limit exceeded- Reduce subscription frequencyAuthentication failed- Check API key
Last updated