REST API
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 — 68 endpoints across 6 namespaces (61 spec operations plus the archive's three, the schema's three and the WebSocket), one page per endpoint,
grouped by namespace. ⚠ One of those 68 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.
Polymarket is the one /v1 namespace these conventions do not cover
onchain and hyperliquid share one request path, so no route in either can quietly diverge from
what follows — the same auth, the same rate limiting, the same error mapping and the same usage
metering apply to every one of them.
/v1/polymarket/* is the exception. It answers on the same host, behind the same credential and
the same hourly budget, and it carries the same edge behaviour — the same key gate, the same
Cache-Control header — but none of the conventions below are guaranteed to hold there, and its
parameter spellings differ (from/to rather than start/end, for one). Read
Polymarket for its own.
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.
⚠ 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 takes a coin, onchain takes an
address, and polymarket takes a token_id — the outcome token, never the condition_id.
A response echoes the identity it resolved, so a client can pin what it was given rather than re-deriving it.
Time
Windows are half-open — start inclusive, end exclusive — across onchain and
hyperliquid alike: the filter is ts >= start and ts < end, so consecutive windows tile
without double-counting a boundary row. Both bounds accept ISO-8601 UTC or epoch milliseconds.
Timestamps in responses are epoch milliseconds, UTC.
⚠ Polymarket does not follow this. Its window parameters are from/to and its upper bound is
inclusive — see Polymarket.
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 | https://data.vike.io/v1/openapi.json |
| Swagger UI | https://data.vike.io/v1/docs |
| ReDoc | https://data.vike.io/v1/redoc |
All 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. Fetch the spec with a client
that can set one, and read it locally:
curl -H "X-API-Key: $VIKE_API_KEY" https://data.vike.io/v1/openapi.json -o openapi.jsonThe 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: 11
/v1/polymarket/*routes, all under the base URL above. - Bulk history is not a REST concern. Research-scale Polymarket data is the Parquet archive: 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.