perpPrices
Stream the accepted oracle and mark prices of perps, one message per block.
Every live data message includes both routing identifiers with the same value:
{"type": "perpPrices", "channel": "perpPrices"}Other payload fields are omitted above. Existing clients may continue routing on either field.
The streaming counterpart of perpPriceHistoryByTime: the prices the exchange actually uses for margining and liquidation, on every dex including HIP-3. A row here is identical to the row the REST endpoint returns for the same block.
Subscribe
{
"method": "subscribe",
"subscription": {
"type": "perpPrices",
"coins": ["BTC", "xyz:GOLD"]
}
}Parameters:
coins
string[]
Yes
Coins to subscribe to, 1 to 200 per request. Use the dex-prefixed symbol for HIP-3 markets (e.g. xyz:GOLD).
Unsubscribe
Name coins to remove; the rest keep streaming without a gap. Omit coins to drop the whole subscription.
Update data format
A message is sent for every block (~70 ms), including blocks that carry no price round for your coins β prices is then []. That lets you track the chain height and time continuously, and seq is gap-free per connection so a missed message is detectable. Price rounds arrive roughly every 3 seconds per dex, so expect about one message in forty to carry rows.
A block carries at most one row per coin: when a dex publishes more than one round in a block, the last one is the accepted price, exactly as the REST endpoint reports it.
Field definitions
height
int
Block height
timestamp
int
Block timestamp (ms since epoch)
prices
object[]
The block's accepted rounds for your coins; empty when there were none
prices[].time
int
Block timestamp of the oracle round (ms) β equals timestamp
prices[].blockNumber
int
Height of the block that committed the round β equals height
prices[].dex
string
DEX identifier ("hyperliquid" for the native dex)
prices[].coin
string
Trading pair
prices[].oraclePx
string?
Accepted oracle price. Omitted when the round carried no oracle slot for the asset (the natively-priced HYPE/PURR miss the slot in ~2 rounds a day)
prices[].markPx
string
Accepted mark price β the value used for margining, liquidations and funding. Always present
prices[].extPerpPx
string?
External perp reference price; omitted for assets without one
prices[].updateClass
string?
HIP-3 only: how the network sourced the round as reported by the node ("Deployer", "Fallback", "Normal"). Omitted for the native dex
Reconnect replay
Subscribe with the cursor of the last message you received (block:timestamp) and the server first sends a replay message covering up to 30 seconds of missed data, then the subscription ack, then live messages. Replay carries one item per block that had a round for your coins β the empty tick messages are not replayed; take the chain height from your first live message. hasGap: true on the replay means the cursor is older than the cache: fill the hole with perpPriceHistoryByTime, whose rows are identical to these. The replay message format is described in Session management and reconnection.
Limits
perpPrices is included in every tier. Your tier's coin cap bounds the coins per API key (10/100/200 for starter/growth/scale; higher bespoke limits are available β contact us), and each subscribed coin counts as one subscription toward your tier's total. A single request may name at most 200 coins.
Examples
Common errors
The subscription feedback and warning shapes are documented in the WebSocket overview. A well-formed but unknown or inactive coin succeeds with an inactive_coins warning and simply never produces rows.
coins required for perpPrices subscription- Send at least one coin; there is no all-markets form yetToo many coins- More than 200 coins in one request, or the merge would push the set past your tier's coin cap; the existing subscription is left unchangedRate limit exceeded- Reduce subscription frequencyAuthentication failed- Check API key
Last updated