/assets/hourly
Hourly close plus open interest, funding rate and premium, for many assets at once — the join a research walk actually consumes.
GET https://data.vike.io/v1/hyperliquid/assets/hourlyHourly close plus open interest, funding rate and premium, for many assets at once. This is the join a research walk actually consumes; it exists so consumers stop stitching the price series and the market-state series together by hand.
Truncated: rows holds 96 rows (2 assets x 48 hours); the first 4 are shown. They are the
positional arrays the trap below describes, ordered by columns.
# the default shape: two assets over two days, one row per asset-hour
curl -s "https://data.vike.io/v1/hyperliquid/assets/hourly?symbols=BTC,ETH&days=2" \
-H "X-API-KEY: $VIKE_API_KEY"
# pin the window for a reproducible request: days counts back from the server's clock at call time,
# start and end do not (end is exclusive)
curl -s "https://data.vike.io/v1/hyperliquid/assets/hourly?symbols=BTC&start=2026-08-01T00:00:00Z&end=2026-08-15T00:00:00Z" \
-H "X-API-KEY: $VIKE_API_KEY"
# a research basket over the longest window days accepts (1-400): rows = symbols x hours
curl -s "https://data.vike.io/v1/hyperliquid/assets/hourly?symbols=BTC,ETH,SOL&days=400" \
-H "X-API-KEY: $VIKE_API_KEY"
# a basket too wide for the 256,000-row ceiling is refused, not paged: split it across calls
# over the same pinned window (first half)
curl -s "https://data.vike.io/v1/hyperliquid/assets/hourly?symbols=BTC,ETH&start=2026-01-01T00:00:00Z&end=2026-06-01T00:00:00Z" \
-H "X-API-KEY: $VIKE_API_KEY"
# the other half of the basket, same window
curl -s "https://data.vike.io/v1/hyperliquid/assets/hourly?symbols=SOL,XRP&start=2026-01-01T00:00:00Z&end=2026-06-01T00:00:00Z" \
-H "X-API-KEY: $VIKE_API_KEY"
# rows are positional arrays in columns order: zip each one back into an object client-side
curl -s "https://data.vike.io/v1/hyperliquid/assets/hourly?symbols=BTC&days=1" \
-H "X-API-KEY: $VIKE_API_KEY" \
| jq '. as $d | $d.rows[] | [$d.columns, .] | transpose | map({(.[0]): .[1]}) | add'symbolsqueryrequiredBTC,ETH,SOL. Max 64daysqueryoptionalend. Default 120, range 1–400startqueryoptionaldaysendqueryoptional{
"assets": [
"BTC",
"ETH"
],
"tz": "UTC",
"source": "1h fills rollup + hourly asset summary",
"start": 1788250014,
"end": 1788422814,
"columns": [
"ts",
"asset",
"close",
"open_interest",
"funding_rate",
"premium"
],
"rows": [
[
1788253200,
"BTC",
77899.0,
38732.56691000003,
0.000012500000000000004,
-0.00005489032500000001
],
[
1788256800,
"BTC",
78024.0,
38985.63194500007,
0.000012500000000000004,
-0.000063838675
],
[
1788260400,
"BTC",
78094.0,
39051.50362166676,
0.000012500000000000004,
-0.00007857194999999999
],
[
1788264000,
"BTC",
78000.0,
39224.664336666705,
0.000012500000000000004,
-0.00006519585833333333
]
]
}Rows are arrays, not objects
Each entry of rows is a positional array, ordered by the response's columns field:
ts, asset, close, open_interest, funding_rate, premium. A 120-day 10-asset window is
28,800 rows, and repeating six key names on each would triple the payload for nothing. This is a
bulk read, not a browsing endpoint.
Nulls stay null
The join is LEFT from the price side, so a missing market-state row arrives as null, never as
0.0: an open interest of zero is a real reading, and substituting it for "we did not sample this
hour" turns a gap into a data point. Carry it through as a NaN.
No cursor: an oversized request is refused, not paged
The ceiling is 256,000 rows (64 assets × 4,000 hours). Half a training window silently returned is worse than a refusal.
close comes from the fills rollup, not from the hourly summary's own OHLC grid. That grid is
built from a mid price sampled every 300s — 12 samples an hour, a midpoint nobody transacted at
— and a backtest priced off it books fills at prices that never existed. Only
open_interest, funding_rate and premium, which have no fills-side equivalent, come from the
summary.