Contents
Liquidation heatmap over time
Liquidation heatmap over time from the tracked Hyperliquid positions (venue=lighter | lighter_rh: tracked Lighter / Lighter RH accounts with the liquidation price Lighter reports; venue=dydx | gmx: every open dYdX v4 / GMX v2 position read from the chain, with the liquidation price we compute from the chain state; venue=all: every enabled real layer added per column): one column per interval tf (times, UTC aligned), one row per price bucket (prices, ascending); cells[t][p] is the USD that would be liquidated in that bucket at that time (longCells / shortCells split by side), plus the mark per column.
/api/v1/heatmap/{coin}Every keyDescription
A column shows the levels at its start: the first stored snapshot inside it (snapshotAt), never a sum of several; the running column shows the newest snapshot (live). 1m columns (and 5m for the top 20 coins) come from a fine layer (0.1 % buckets within ±10 %, a snapshot every minute), the rest from 0.5 % buckets within ±50 % (layer, bucketPct, rangePct). freshUsdShare / ageP50Sec per column say how much of the mapped USD was re-read within the cadence and how old it is. tf=1d (Hyperliquid): every day the daily history covers shows its snapshot of every open Hyperliquid position (shortly after 00:00 UTC, the same 0.5 % buckets), the tracked wallets only the days it does not cover yet (normally today); columnSource says which per column, the price rows span the lowest to the highest column mark ± 30 %, and the window reaches as far back as the plan history. Intraday tf (Hyperliquid): every value is measured, nothing is computed between snapshots (since 1.22.0). Live columns start on 2026-11-03 14:15 UTC; before that a column shows the newest daily snapshot of all positions before its end when it is at most 24 hours older (columnSource daily_fill, never across a missing day); after it a column without a live snapshot shows the same daily snapshot until the next live column. Columns reconstructed from fills (columnSource reconstructed, dataSource reconstructed_fills, the recon* fields) are no longer returned; the values stay in the schema. data.dataSource marks every column live or daily_snapshot; columns=live keeps only the live columns. Intervals per plan: free key and keyless 15m, 1h; Pro / API Dev add 5m, 4h, 12h, 1d; Desk / API Pro add 1m. Every coin on every plan; heatmap history (also for its layers): free key and keyless the last 24 hours, Pro / API Dev 1 year, Desk / API Pro 3 years.
Who can call it
Available with every key and keyless (X-Blockmetric-Access: public). Keys of Free accounts get free-tier data (real-time feeds, 30 days of history, heatmap over time for every coin over the last 24 hours, liquidation levels now for every coin, the same as the free web plan); Pro and Desk keys the data of their plan; API Dev / API Pro raise the limits.
- No key
- 24 h
- Free
- 30 days
- Pro
- 365 days
- Desk
- 1095 days
- API Dev
- 365 days
- API Pro
- 1095 days
History window per plan. Limits: Authentication & limits.
Parameters
| Parameter | Description |
|---|---|
coinstring · pathrequired | Coin name, e.g. BTC (case-insensitive). |
tfstring | Column interval: 1m, 5m, 15m, 1h, 4h, 12h or 1d (UTC aligned: 4h at 00/04/…, 12h at 00/12, 1d at 00:00). An interval outside the plan answers 403 plan_required. 1m only for the top 20 coins by open interest (kept 30 days); a window without 1m data answers 400. Recommended: always pass tf (tf=15m gives the same columns as the default). Without tf the API behaves as before 1.12.0 (15-minute columns, coarsened automatically for long windows) and end is not available.one of 1m, 5m, 15m, 1h, 4h, 12h, 1d |
hoursinteger | Window in hours (with tf). Default = the interval's default range (1m 6, 5m 24, 15m 24, 1h 336, 4h 720, 12h 4320, 1d 8760), clamped to the plan's heatmap history (free key and keyless the last 24 hours, Pro / API Dev 1 year, Desk / API Pro 3 years). More than the interval's column cap (1m 12 h, 5m 48 h, 15m 168 h, 1h 720 h, 4h 2160 h, 12h 8760 h, 1d 26280 h) or the heatmap history answers 400 invalid_param, never coarsened silently.1–26280 |
endstring | End of the window (ISO 8601, default now; needs tf, without tf 400): page back in time, e.g. 1m columns three days ago. Clamped to now and to the plan's heatmap history (meta.warnings says so).ISO 8601, e.g. 2026-10-23T12:00:00Z |
columnsstring | all (default) | live: only the columns of the live collection (data.dataSource live); columns filled from the daily snapshot become gaps (zero cells, hasSnapshot false, mark null), like a window without a snapshot. Times and price rows stay the same.one of all, live · default all |
clustersinteger | Add data.clusters: the N (1–20) price buckets with the most USD above and below the mark of the newest column with a snapshot (price, usd, longUsd, shortUsd, signed distance): the alert-bot view without reading cells.1–20 |
venuestring | hyperliquid (default: the tracked Hyperliquid positions, the answer as before) | lighter | lighter_rh (tracked Lighter / Lighter RH accounts with the liquidation price Lighter reports) | dydx | gmx (every open dYdX v4 / GMX v2 position read from the chain, with the liquidation price we compute from the chain state) | all (every enabled DEX venue: the real per-venue layers added per column; data.venueColumns says which venue contributed a snapshot to which column, a venue without one adds nothing). Never an estimate for positions that are not public. A coin without a market on the venue answers 404; a switched-off venue 400.one of hyperliquid, lighter, lighter_rh, dydx, gmx, all · default hyperliquid |
stepMinutesintegerdeprecated | Deprecated (API 1.12.0), use tf. Without tf: column width in minutes, default 15, clamped to 5–1440, coarsened automatically for long windows (≥ 60 beyond 14 days, at most 720 columns). Below 15 needs Pro, Desk or an API plan (403 plan_required, since 1.23.0). Together with tf: 400.5–1440 |
layerstring | real (default, the only layer: tracked positions of the venue). est / both were removed on 2026-10-23 (API 1.9.0) together with the estimated CEX layer (real data only); they now answer 400 invalid_param.one of real · default real |
Response fields
Every success is { data, meta }; these are the fields of data. meta is the same on every endpoint.
| Field | Description |
|---|---|
coinstring | Coin name as on Hyperliquid (BTC, kPEPE, HIP-3 names like xyz:TSLA). |
bucketSizenumber | Price step between rows (0 when there is no data). |
times[]array of string (ISO 8601) | Column times, oldest → newest. |
prices[]array of number | Lower edge of each price row, ascending. |
cells[]array of array | cells[timeIndex][priceIndex]: USD that would be liquidated in that bucket at that time (long + short). |
longCells[]array of array | Same shape, long positions (below the mark). |
shortCells[]array of array | Same shape, short positions (above the mark). |
mark[]array of number | Mark price per column (null when no snapshot exists for that slot). |
hasSnapshot[]array of boolean | Per column: true when a stored snapshot fills the slot. false = gap (collector down): its cells are 0 only for the shape and are not data. |
maxUsdnumber | Largest cell value (USD). |
updatedAtstring (ISO 8601) · nullable | Newest snapshot. |
tfstring (enum) | Column interval (absent with the deprecated stepMinutes). |
stepMinutesinteger | Column width in minutes. |
layer"fine" | "wide" | fine = 0.1 % buckets within ±10 % (every minute, top 20 coins); wide = 0.5 % buckets within ±50 %. One layer per response. |
bucketPctnumber | Bucket width of the layer as a fraction of the mark (0.001 / 0.005). Deprecated: use bucketFraction (same value). |
rangePctnumber | Range of the layer around the mark as a fraction (0.1 / 0.5). Deprecated: use rangeFraction (same value). |
bucketFractionnumber | Bucket width of the layer as a fraction of the mark (0.001 = 0.1 %). |
rangeFractionnumber | Range of the layer around the mark as a fraction (0.5 = ±50 %). |
clustersobject · nullable | With ?clusters=N: the largest buckets above and below the mark of the newest column with a snapshot. |
clusters.timestring (ISO 8601) | Column the clusters are read from (the newest one with a snapshot). |
clusters.marknumber · nullable | Mark of that column. |
clusters.above[]array of objects | Largest buckets at or above the mark, largest first (short liquidations). |
clusters.above[].pricenumber | Lower edge of the price bucket. |
clusters.above[].usdnumber | Long + short that would be liquidated in the bucket (USD). |
clusters.above[].longUsdnumber | Long positions (USD). |
clusters.above[].shortUsdnumber | Short positions (USD). |
clusters.above[].distancenumber · nullable | Signed distance from the mark, fraction (−0.03 = 3 % below). |
clusters.below[]array of objects | Largest buckets below the mark, largest first (long liquidations). |
clusters.below[].pricenumber | Lower edge of the price bucket. |
clusters.below[].usdnumber | Long + short that would be liquidated in the bucket (USD). |
clusters.below[].longUsdnumber | Long positions (USD). |
clusters.below[].shortUsdnumber | Short positions (USD). |
clusters.below[].distancenumber · nullable | Signed distance from the mark, fraction (−0.03 = 3 % below). |
snapshotAt[]array of string (ISO 8601) | Per column: exact time of the snapshot drawn (the first one inside the column; the newest one in a live column); null = gap. |
liveboolean | The last column is still running and shows the newest snapshot. |
freshUsdShare[]array of number | Per column: share (0–1) of the mapped USD whose wallet was re-read within the cadence before the snapshot (fine 1 min, wide 5 min); null = unknown. |
ageP50Sec[]array of integer | Per column: USD-weighted median age of the mapped positions in seconds; null = unknown. |
venue"hyperliquid" | "lighter" | "lighter_rh" | "dydx" | "gmx" | "all" | Venue layer of the answer (present with ?venue=lighter | lighter_rh | dydx | gmx | all; absent = Hyperliquid). |
venueColumns[]array of objects | venue=all: per venue, the columns it contributed a snapshot to (a venue without one adds nothing to that column). |
venueColumns[].venue"hyperliquid" | "lighter" | "lighter_rh" | "dydx" | "gmx" | Trading venue the row comes from. |
venueColumns[].present[]array of boolean | Per column: the venue contributed a snapshot. |
columnSource[]array of "all_positions" | "daily_fill" | "reconstructed" | "tracked" | With tf on Hyperliquid: per column, where its snapshot comes from. tracked = the live layer of the tracked wallets. tf=1d: all_positions = the daily history of every open Hyperliquid position (one snapshot shortly after 00:00 UTC), used for every day it covers; tracked only for days it does not cover yet (normally today). Intraday tf (1m to 12h): reconstructed (not returned since 1.22.0) = no live snapshot was stored in the column, so it shows the levels of all positions reconstructed from fills between two daily snapshots (snapshotAt = the 15-min reconstruction step at or before the column start; see reconBaseAt, reconSinceBaseSec, reconAssumedShare, reconQuality); daily_fill = no live snapshot and no reconstruction, so it shows the newest daily snapshot of all positions before the column end, at most 24 hours older than its start (snapshotAt = that snapshot). Neither has freshness figures; clusters use the newest tracked column. The sources differ in scale (all positions vs the tracked part); null = gap. |
dataSource[]array of "live" | "reconstructed_fills" | "daily_snapshot" | Per column, where its data comes from: live = a snapshot of the live collection (tracked wallets; also every column of venue=lighter / all and of the deprecated stepMinutes path); reconstructed_fills (not returned since 1.22.0) = reconstructed from fills between two daily snapshots of all positions; daily_snapshot = the daily snapshot of all positions (tf=1d history or an intraday daily fill); null = gap. ?columns=live keeps only the live columns. |
reconBaseAt[]array of string (ISO 8601) | Not returned since 1.22.0 (measured values only). Reconstructed columns only (null elsewhere; absent when the window has none): time of the real daily snapshot of all positions the reconstruction was replayed from (the nearer one). |
reconSinceBaseSec[]array of integer | Not returned since 1.22.0 (measured values only). Reconstructed columns only: seconds from reconBaseAt to the reconstruction step; negative = before that snapshot (replayed back in time from the next day's snapshot). |value| is at most about 12 hours. |
reconAssumedShare[]array of number | Not returned since 1.22.0 (measured values only). Reconstructed columns only: share (0–1) of the mapped USD whose margin type or entry price had to be assumed (a position the fills opened without a known setting). |
reconQuality[]array of "pass" | "warn" | "fail" | "unvalidated" | Not returned since 1.22.0 (measured values only). Reconstructed columns only: how the reconstruction of this coin and day scored when replayed all the way to the other daily snapshot (worst case, 24 hours): pass = long and short USD within 10 %, bucket error at most 0.25, correlation at least 0.9 and closer than carrying the snapshot forward; warn = within 30 % and correlation at least 0.8; fail = worse (deposits, withdrawals and transfers between accounts are not in the fills); unvalidated = not checked yet. |
Errors
- 400
invalid_paramInvalid parameter - 401
unauthorizedMissing or invalid API key - 403
plan_requiredPlan does not include this - 403
scope_requiredKey not scoped for this API - 403
ip_not_allowedAddress not on the IP allowlist of the key - 403
origin_not_allowedKey used from a browser origin it does not allow - 403
source_restrictedExchange data not redistributed - 403
https_requiredhttps required - 404
not_foundNot found - 429
rate_limitedPer-minute limit reachedretry - 429
daily_limit_exceededDaily limit reachedretry - 429
stream_limitToo many open streamsretry - 500
internal_errorInternal errorretry
Methodology
- Liquidation heatmap (real positions) (how it is computed and its limits)
Code snippets in these docs are licensed under MIT-0: copy them into your own code without attribution. The SDKs, CLI and MCP server will be, once published, Apache-2.0; the name Blockmetric is not licensed (forks must be renamed). Data from the API is governed by the API terms; third-party exchange data by the exchange’s terms.