REST conventions
The rules that hold across every /v1 namespace on data.vike.io — one credential, one hourly counter, one error envelope, one request helper — and where the per-endpoint reference lives.
The REST base URL is:
https://data.vike.ioThat is what the live OpenAPI document's own servers array declares, so a generated client picks
it up without configuration.
This page is not the endpoint list. The full endpoint reference is
API endpoints — the 71 operations of the spec plus the archive's three paths, the schema's three and the WebSocket, one page per endpoint,
grouped by namespace. ⚠ One of them is not on the base URL above: the WebSocket
wss://vike.io/v1/stream answers on vike.io rather than data.vike.io, so following the base
URL cannot reach it. It still validates the same credential and spends the same hourly counter.
What stays here is the set of conventions that hold across the /v1 namespaces, so the
namespace pages do not restate them: how you authenticate, how identity and time windows are
spelled, what a failure looks like, how a call is labelled, and why a payload is byte-stable.
One request path for all four namespaces
onchain, hyperliquid, hl-cohorts and polymarket share one request path: the same auth, the
same rate limiting, the same metering, the same 400 and 503 mapping and the same snapping of
aggregated floats apply to every route in them.
/v1/polymarket/* keeps two differences of its own. Its 400 and 503 bodies are
{"error": "<message>"}, a plain string, where the other namespaces answer {"detail": ...}; a
401 or 429 there is the same as everywhere else. And its query parameters are read as text and
checked by the route: an unknown parameter is a 400 with that body, never the framework's 422.
A value the route checks (an interval, a slug_prefix, a trader-route window) is a 400 too, but a
malformed number or boolean on the book, tape and settlement routes is ignored and the default
applies. Its window parameters are spelled per route too — see Time.
Authenticate
Two credentials are accepted, and either one alone is enough:
curl -H "X-API-Key: $VIKE_API_KEY" \
"https://data.vike.io/v1/onchain/tokens/search?q=USDC&limit=3"curl -H "Authorization: Bearer $VIKE_OAUTH_TOKEN" \
"https://data.vike.io/v1/onchain/tokens/search?q=USDC&limit=3"- API key —
X-API-Key: vk_.... Create and revoke keys at vike.io/account. HTTP header names are case-insensitive, so theX-API-KEYspelling in the OpenAPI spec is the same header. - OAuth 2.1 bearer —
Authorization: Bearer vk_oat_..., discovery athttps://vike.io/.well-known/oauth-authorization-server.
The bearer is tried first, and a request falls through to the API-key header when the bearer is invalid — including when that rejection came from cache — so a request may carry either or both, and both reach the same account. The spec models this as a list of single-key requirements, which in OpenAPI means OR, not AND.
Every call is counted against one hourly budget shared by MCP and REST; archive downloads are
authenticated with the same credential but do not spend it — see Rate limits. Nothing under /v1/ is reachable without a credential, the spec
and the interactive docs included: there is no exemption for a schema surface. The spec has a
keyless copy off this host: The OpenAPI spec.
⚠ That gate is header-only. Exactly two headers can carry a credential — Authorization and
X-API-KEY — and nothing else is consulted. No cookie, no session and no ?token= query parameter
authenticates a request to this host, so being signed in to vike.io in the same browser does
nothing here.
Key lifecycle, the OAuth discovery document and the revocation window: Authentication.
Conventions
Identity
⚠ There is no API-wide identifier. Each namespace addresses its subject in its own vocabulary,
and the spelling is not interchangeable: hyperliquid and hl-cohorts take a symbol, onchain
takes an address, and polymarket takes a token_id (one outcome) on its book and tape routes,
a condition_id (the market) on /markets/{condition_id} and /settlements/{condition_id}, and a
wallet address on /wallets/{address}. The Hyperliquid wallet routes take an address too.
A response echoes the identity it resolved, so a client can pin what it was given rather than re-deriving it.
Time
⚠ There is no API-wide window rule. The parameter list on each endpoint page states its own, and it is the authority. By namespace:
hyperliquidandhl-cohortstakestartandendas ISO-8601 UTC where a route takes an explicit range, and most of those keep a row stamped exactly atend./ohlcv,/ohlcv/exportand/symbols/hourlyare half-open instead —endexclusive, so consecutive windows tile without double-counting a boundary bar — and also accept epoch time. Other routes take a namedwindow(the trader, leaderboard and builder routes) or a number ofhours(several cohort series) instead of a range.onchaintakes no timestamps: a named window (window,period) or a number ofdaysback from now.polymarketspells it per route:/ohlcvtakesfrom/toin epoch milliseconds with an inclusiveto;/eventsand/settlements/bulktakefrom_ms/to_ms, half-open; the archive index takesfrom/toas dates, both inclusive.
Every timestamp in a response is UTC, but its spelling is per route too: ISO-8601 strings on most
hyperliquid, hl-cohorts and onchain routes; epoch milliseconds on Polymarket's book, tape and
settlement routes and on the Hyperliquid candles; Polymarket's trader routes return
YYYY-MM-DD HH:MM:SS UTC strings and its archive routes dates. Read it off the endpoint page's
example.
Errors
Branch on the status code, not the body. The mapping is deliberate: 400 for input the query
engine rejected, 401 for a bad or missing credential, 429 for the hourly limit (with
Retry-After), 503 for an upstream failure. A store outage is never reported as a 200 with an
empty body — a silent empty list is indistinguishable from "no data", which is how bad backfills
get built on top of an outage.
Full envelope shapes, and the 422 that comes from parameter validation before the query runs at all: Errors.
Metering
Every call is recorded under a per-surface label, so archive downloads, REST calls and MCP calls meter separately instead of all landing under one name. Two things produce those labels, and they do not share one vocabulary:
- One is derived from the request path.
/v1/<ns>/<resource>becomesapi_<ns>_<resource>,/archive/...becomesarchive_download, and/mcpbecomesmcp. Only the first two path segments after/v1/are read, and an over-long segment is rejected rather than labelled — so a wallet address or a token id can never reach a usage row. - Some routes report a shorter name of their own —
api_onchain_wallets,api_hl_positions./v1/hyperliquid/positionsisapi_hl_positionsunder that vocabulary andapi_hyperliquid_positionsunder the path-derived one, so do not assume one name per endpoint when reading usage rows back.
A rate-limited request is logged too, with zero credits — a client hammering past its ceiling would otherwise appear, in usage analytics, to have simply stopped calling. What the budget is, and which surface stopped spending it on 2026-09-02, are on Rate limits.
Determinism and caching
Aggregated floats in a successful payload are snapped to a fixed precision on the way out — once,
after the query has run, rather than inside each individual query. The query engine reduces a
sum() in an unpinned parallel order, so two executions of one byte-identical query return values
differing in their last digits; snapping is what lets a client cache, diff or content-address a
response at all.
Every gated response carries Cache-Control: private, no-store. The CDN in front of
data.vike.io must not cache an authenticated response — that would bypass both the gate and the
metering.
The OpenAPI spec
| Surface | URL |
|---|---|
| Spec, keyless copy | https://vike.io/data/openapi.json |
| Spec | https://data.vike.io/v1/openapi.json |
| Swagger UI | https://data.vike.io/v1/docs |
| ReDoc | https://data.vike.io/v1/redoc |
The keyless copy needs no credential and is not metered: it is the same document, exported with
the code, plus a top-level x-vike-limits block. Every /v1 response names it in a Link header
with rel="service-desc", so a client holding only a response can find the spec.
curl -s https://vike.io/data/openapi.json -o openapi.jsonThe other three sit under /v1/ and behind the same key gate as the operations they describe, so a
browser cannot open Swagger UI or ReDoc without injecting a header.
The spec will not carry a code generator
It is generated from the /v1 namespaces it describes; the WebSocket appears in no spec at all.
What it does describe, it describes thinly — it
declares no response schemas at all: components.schemas holds two entries and both are error
types, so every 200 in the document is an undeclared {}.
Schema and spec has the full accounting.
Related surfaces
- Hyperliquid OHLCV — price candles now have one home,
Hyperliquid's own, read from HL's fill stream and served on the base URL above. Polymarket's
/v1/polymarket/ohlcvis a different thing under the same word: one outcome token, priced in probability rather than currency. - Polymarket — the reference for that namespace: 13
/v1/polymarket/*routes, all under the base URL above. - Bulk history is not a REST concern. Research-scale Polymarket data is the Parquet archive, and Hyperliquid's is daily Parquet files by temporary link: downloads do not spend the hourly budget, and you should fetch whole files.
- One MCP server, two hosts.
https://vike.io/mcpis the canonical URL — see Data tools.https://data.vike.io/mcpreaches the same server with the same tools and the same API key; OAuth clients such as Claude Desktop should usevike.io/mcp.
Limits and errors
The hourly request limit, the per-minute cap on /v1, what each call costs in credits, every status code and error body, and what to do about each.
Page through large reads
The four ways the /v1 routes page, a cursor, a cursor in an envelope, an offset and a moving time window, each walked in one script with its stop condition, and the on-chain routes that do not page at all.