# GET /v1/hl/footprint

> Hyperliquid open-interest, net-long and net-short footprint minutes of one coin over a time range.

Returns per minute and price row what Hyperliquid trades did to positions: the change in open interest, longs opened minus closed, and shorts opened minus closed. Flowscope draws these as the [Hyperliquid positioning footprints](/docs/app/liquidations#hyperliquid-positioning-footprints).

## Request

```text
GET /v1/hl/footprint/<COIN>?from=<ms>&to=<ms>
HEAD /v1/hl/footprint/<COIN>?from=<ms>&to=<ms>
```

| Parameter | In | Type | Description |
|---|---|---|---|
| `COIN` | Path | string | A coin from [`/v1/hl/coins`](/docs/api/hl-coins), case-sensitive |
| `from` | Query | integer, ms | Start of the range; rounded down to the minute |
| `to` | Query | integer, ms | End of the range; rounded up to the minute, exclusive |

The range rules are the same as for [`/v1/hl/levels`](/docs/api/hl-levels#request): `to` greater than `from`, at most 60 days, and always pass both.

## Response

| Header | Value |
|---|---|
| `Content-Type` | `application/octet-stream` |
| `Cache-Control` | `no-cache` |

The body is one **footprint payload** in the [positions codec](/docs/api/positions-codec#footprint-payload): the coin, the row size, the newest sample time and the start of the range answered, then the minutes, then a checksum.

The payload holds every recorded minute that **starts** in the range, in time order. Each minute has:

| Field | Unit | Meaning |
|---|---|---|
| `minute` | minutes since the epoch | `ms / 60 000` |
| `oi` | coins | Hyperliquid's open interest at the end of the minute |
| `mark` | price | Mark price at the end of the minute |
| `tracked_long`, `tracked_short` | coins | Long and short size of the tracked accounts at the end of the minute |
| `volume` | coins | Traded volume in the minute |
| `taker_known` | coins | Volume whose aggressor was tracked: covered by net long and net short |
| `both_known` | coins | Volume with both sides tracked: covered by Δ OI |

and a list of price cells:

| Field | Unit | Meaning |
|---|---|---|
| `row` | — | Prices `[row × row_px, (row + 1) × row_px)` |
| `d_oi` | coins | Δ open interest: buyer's plus seller's change of long size |
| `nl` | coins | Δ net long: longs opened minus closed by the aggressor |
| `ns` | coins | Δ net short: shorts opened minus closed by the aggressor |
| `volume` | coins | Traded volume in the row, all trades |

`NaN` means unknown: `oi`, `mark`, `tracked_long` and `tracked_short` are `NaN` in a minute where they were not seen.

Coverage per minute is `taker_known / volume` for net long and net short, and `both_known / volume` for Δ OI. Show it next to the figures; a chart without coverage overstates what it knows.

The payload's `from` is the start of the requested range, or later when nothing is recorded before that point.

## Status codes

| Status | Body | When |
|---|---|---|
| `200` | Footprint 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 collected |
| `404` | `hyperliquid positions not recorded` | Hyperliquid recording is off |
| `404` | `not found` | Anything other than `levels` or `footprint` after `/v1/hl/` |
| `503` | `no sample yet` | The coin is recorded but has not been sampled yet |

## Example

The last hour of ETH footprints:

```bash
TO=$(( $(date +%s) * 1000 ))
FROM=$(( TO - 3600000 ))
curl -fsS -H "$AUTH" -o eth.fp "$BASE/v1/hl/footprint/ETH?from=$FROM&to=$TO"
```

An hour is typically 2–4 kB per coin.

### Decoding in Rust

```rust
use flowscope_engine::positions::codec::decode_footprint;

let bytes = std::fs::read("eth.fp")?;
let p = decode_footprint(&bytes)?;
for m in &p.minutes {
    let taker_cov = if m.volume > 0.0 { m.taker_known / m.volume } else { 0.0 };
    let nl: f32 = m.cells.iter().map(|c| c.nl).sum();
    let ns: f32 = m.cells.iter().map(|c| c.ns).sum();
    println!("{} oi {} net long {nl:+.2} net short {ns:+.2} coverage {:.0} %", m.minute * 60_000, m.oi, taker_cov * 100.0);
}
```

## How answers are built

Minutes that memory no longer holds completely come from the coin's `.fp` day files; the rest come from memory. A minute is appended to its day file once it has closed. Payloads written with a different row size are skipped.

Footprints need the complete trade stream, which Capture follows; Flowscope clients get them from the data hub.

## Related

- [`/v1/hl/levels`](/docs/api/hl-levels) returns the TP, SL and liquidation levels of the same coin.
- [Data: Hyperliquid](/docs/data/hyperliquid#footprints) describes how footprints are computed.
