GET /v1/hl/levels
Hyperliquid take-profit, stop-loss and liquidation level runs of one coin over a time range.
Returns the measured take-profit, stop-loss and liquidation levels of one Hyperliquid coin as runs: how much USD notional sat in each price bucket, from when to when. Flowscope draws these as the Hyperliquid heatmaps.
Request
GET /v1/hl/levels/<COIN>?from=<ms>&to=<ms>
HEAD /v1/hl/levels/<COIN>?from=<ms>&to=<ms>| Parameter | In | Type | Description |
|---|---|---|---|
COIN | Path | string | A coin from /v1/hl/coins, case-sensitive (BTC, kPEPE) |
from | Query | integer, ms | Start of the range, inclusive |
to | Query | integer, ms | End of the range, exclusive |
Rules for the range:
tomust be greater thanfrom.- The range may span at most 60 days.
- A missing or unparsable
todefaults to a far-future sentinel, and a missingfromto one day beforeto. Always pass both:fromalone makes the range longer than 60 days and is rejected.
Response
| Header | Value |
|---|---|
Content-Type | application/octet-stream |
Cache-Control | no-cache |
The body is one levels payload in the positions codec: a header with the coin, the bucket size, the newest sample time and the range answered, then the runs, then a checksum.
What the payload contains:
- Every run that overlaps the range:
start < toandstart + duration > from. - Runs sorted by their end time.
- Runs still open are included, ending at the newest sample (
latest). - The payload’s
fromis the requestedfrom, or later when nothing is recorded before that point. Use it to tell “no levels” from “not recorded yet”.
Each run has a kind:
| Kind | Value | Meaning |
|---|---|---|
| Long TP | 0 | Take-profit of a long: a sell above the price |
| Short TP | 1 | Take-profit of a short: a buy below the price |
| Long SL | 2 | Stop of a long: a sell below the price |
| Short SL | 3 | Stop of a short: a buy above the price |
| Long liquidation | 4 | Liquidation of a long, below the price |
| Short liquidation | 5 | Liquidation of a short, above the price |
A run’s bucket covers prices [bucket × bucket_px, (bucket + 1) × bucket_px), and its notional is the total USD of the orders or positions in it.
Status codes
| Status | Body | When |
|---|---|---|
200 | Levels payload | The coin has samples |
400 | bad range | to <= from, or the range is longer than 60 days |
404 | coin not recorded | The coin is not in --hl-coins |
404 | hyperliquid positions not recorded | Hyperliquid recording is off |
503 | no sample yet | The coin is recorded but has not been sampled yet |
Example
The last hour of BTC levels:
TO=$(( $(date +%s) * 1000 ))
FROM=$(( TO - 3600000 ))
curl -fsS -o btc.levels "http://127.0.0.1:8787/v1/hl/levels/BTC?from=$FROM&to=$TO"
ls -l btc.levelsAn hour is typically about 12 kB.
The start of the file, annotated:
46 53 50 4c "FSPL"
01 00 version 1
00 00 reserved
00 00 00 00 00 00 34 40 bucket_px = 20.0
... latest, from, to (i64 each)
03 42 54 43 coin: length 3, "BTC"
... run count (u32), runs (21 bytes each), checksumDecoding in Rust
The flowscope-engine crate has the decoder:
use flowscope_engine::positions::codec::decode_levels;
let bytes = std::fs::read("btc.levels")?;
let p = decode_levels(&bytes)?;
println!("{} bucket {} from {} to {} latest {}", p.coin, p.bucket_px, p.from, p.to, p.latest);
for r in &p.runs {
let lo = f64::from(r.bucket) * p.bucket_px;
println!("{:?} {lo}..{} ${:.0} from {} for {} ms", r.kind, lo + p.bucket_px, r.notional, r.start, r.dur);
}In any other language, follow the byte layout in Positions codec.
How answers are built
Runs that ended before what memory holds completely come from the coin’s .levels day files; the rest come from memory. Payloads in the day files with a different bucket size are skipped, so every answer uses one grid.
Levels are sampled every 10 seconds. A Flowscope client loads the last 3 hours, then asks for the tail every 10 seconds and for older history six hours at a time.
Related
/v1/hl/footprintreturns the positioning footprints of the same coin.- Recorder: Hyperliquid describes how levels are sampled.