vike
API EndpointsHyperliquid

/symbols/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/symbols/hourly

Hourly 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.

cURL
# the default shape: two assets over two days, one row per asset-hour
curl -s "https://data.vike.io/v1/hyperliquid/symbols/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/symbols/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/symbols/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/symbols/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/symbols/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/symbols/hourly?symbols=BTC&days=1" \
  -H "X-API-KEY: $VIKE_API_KEY" \
  | jq '. as $d | $d.rows[] | [$d.columns, .] | transpose | map({(.[0]): .[1]}) | add'
symbolsqueryrequired
Comma-separated perp tickers, e.g. BTC,ETH,SOL. Max 64
daysqueryoptional
Window length back from end. Default 120, range 1–400
startqueryoptional
ISO-8601 UTC or epoch s/ms, inclusive. Overrides days
endqueryoptional
ISO-8601 UTC or epoch s/ms, exclusive. Defaults to now
Example response
{
  "symbols": [
    "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.

Example response
{  "symbols": [    "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    ]  ]}