d.prod.pro docs
API

Conventions

Shared conventions for all Public Data API endpoints — response envelope, symbol formats, pagination, timestamps, rate limits, credits, data freshness, recipes, and errors.

These conventions apply to every endpoint in the Public Data API (HyperLiquid endpoints, Markets, Stocks, News, and Account).

Response envelope

Every response is wrapped in the same envelope. code is "0" on success; data holds the payload.

{ "code": "0", "data": { /* endpoint payload */ }, "msg": "success" }

All numeric values that can be large or fractional (balances, prices, PnL) are returned as strings to preserve precision — parse them with a big-decimal library, not floats.

Symbol & coin formats

HyperLiquid coin identifiers

The coin parameter format on HyperLiquid endpoints depends on the market:

MarketFormatExample
Spottoken symbolPURR
PerpsymbolBTC
HIP-3 (stock perps)dex:SYMBOLxyz:TSLA

To discover every currently-listed coin, call /hl/prices/mids — its response keys are the valid coin values (spot, perp, and dex:-prefixed HIP-3 symbols).

Market Data symbols

Every Market Data endpoint is symbol-first — you never pass an internal id. Use the asset's symbol:

Asset classFormatExample
CryptotickerBTC
Stockticker, bare or market-qualifiedNVDA / NVDA-US
HIP-3 (tokenized stock perp)dex:SYMBOLxyz:TSLA

Every symbol-keyed endpoint accepts both the bare ticker and the market-qualified form interchangeably — NVDA and NVDA-US resolve the same on /assets/{symbol}, klines, tickers, and stock detail endpoints (fundamentals, company, news, SEC filings). A bare crypto ticker like BTC resolves to the canonical asset (Bitcoin itself, not a same-symbol ETF). Symbol matching is case-insensitive (btc = BTC).

If you are unsure which symbol an asset uses, look it up with /search first. A symbol that matches no asset in either form returns 404.

Asset types & locales

  • typeCRYPTO · STOCK · ETF · FOREX · COMMODITY (some list endpoints also accept ALL).
  • News localeen · zh-CN · zh-TW · fr · ru · es · ar · ko. When a locale's AI analysis isn't ready yet, the item falls back to its base (English) content.

Pagination

List endpoints accept page (1-based) and limit, and return a flat pagination envelope alongside the items:

{ "items": [ /* … */ ], "page": 1, "limit": 100, "total": 17161, "hasMore": true }

Defaults and caps vary by endpoint (see each below). total — the total number of matching rows when the source provides a count; null when it does not (always null for news and news search). hasMore is always present. Page until hasMore is false.

Cursor pagination

Three endpoints support an optional opaque cursor token for unbounded deep paging: GET /hl/spot/holders, GET /hl/perp/holders, and GET /hl/leaderboard. All other list endpoints use offset paging only.

When cursor paging is available, responses include "nextCursor": "<token>" | null alongside the standard pagination fields. To page forward, pass the previous response's nextCursor as ?cursor=<token>; stop when nextCursor is null (equivalently hasMore: false).

  • cursor and page are mutually exclusive — when cursor is present, page is ignored. Keep limit constant across a cursor sequence.
  • The token is opaque (base64url), bound to the exact sort (coin/sortBy/sort + order). Do not hand-craft it or reuse it across different sort configurations — a mismatched cursor returns 400 VALIDATION_ERROR.
  • A cursor minted just before a snapshot refresh resumes at the same rank in the new snapshot; rows may shift slightly across a refresh (the data is a periodic snapshot).

Offset cap: offset paging is capped at the first 10,000 rows — requests where (page − 1) × limit ≥ 10,000 return 400 VALIDATION_ERROR. To read rows beyond 10,000, use cursor paging (cursor is not capped).

Timestamps & freshness

HyperLiquid endpoints

Times such as updatedAt and snapshot_time are ISO-8601 UTC strings (e.g. "2026-06-25T13:06:22.103Z"). Data is served from periodic snapshots (typically refreshed every few seconds to a minute), so values are near-real-time, not tick-by-tick. snapshot_height is a numeric block height, not a time.

BREAKING CHANGE — the following response fields changed type from number (epoch ms) to string (ISO-8601 UTC):

  • updatedAt — in /hl/spot/holders, /hl/perp/holders, /hl/leaderboard, /hl/hip3/smart-trader, /hl/hip4/smart-trader
  • snapshot_time — in /hl/trending

snapshot_height remains a number (block height). Update code that treated these as numbers — new Date(updatedAt) and Date.parse(updatedAt) accept ISO strings directly.

Market Data endpoints

Timestamps in Market Data responses are ISO 8601 strings (e.g. 2026-06-26T08:30:00.000Z) — not the epoch-millisecond integers used by the HyperLiquid endpoints. Parse them as dates.

Rate limits & credits

Each key is rate-limited by requests per minute. On paid tiers, each successful call (HTTP 2xx) also draws from a monthly credit quota.

  • Rate limit — a fixed per-minute window per key. The counter resets at the top of each clock minute. Every response carries X-RateLimit-Limit (your per-minute cap), X-RateLimit-Remaining (calls left in the current minute window), and X-RateLimit-Reset (Unix epoch seconds when the window resets at the top of the next clock minute). Pace your requests against X-RateLimit-Remaining. Exceeding your cap returns 429 Too Many Requests with a Retry-After header (seconds until the window resets) in addition to the X-RateLimit-* headers.
  • Credits (paid tiers) — each endpoint has a per-call credit cost (see Credit costs). A call is billed only on success (HTTP 2xx) — failed requests (4xx/5xx) cost nothing, and each successful request is billed exactly once. The Pro and Max quotas are deliberately sized well above what the rate limit can spend in a month, so you'll rarely reach them; if a monthly quota is exhausted, calls return 403 with code INSUFFICIENT_CREDITS until it resets at the start of the next period.
  • Free tier — rate-limited only, with no credit cost.

The tiers table (Free / Pro / Max rate limits and monthly credit quotas) is on the Authentication page. Check your remaining balance on the Usage tab of the developer portal, or per-endpoint via GET /usage.

Credit costs

CreditsEndpoints
0/usage
1/hl/spot/holders, /hl/perp/holders, /hl/prices/mids, /hl/trending, /hl/leaderboard
1/news (feed), /news/{id}, /assets, /assets/{symbol}, /global-stats, /trending-gainers, /assets/{symbol}/tickers, /stock/overview, /stock/{symbol}/fundamentals, /stock/{symbol}/company, /stock/{symbol}/news, /rwa
2/hl/perp/liquidation-map, /hl/hip3/smart-trader, /hl/hip4/smart-trader
2/news/search, /search, /assets/{symbol}/klines, /stock/{symbol}/sec-filings

Costs may change over time; treat these as current defaults, not a contract.

Data freshness & degraded responses

Each Market Data endpoint is served through a short-lived cache — typically 30–60 seconds, and up to about an hour for equity fundamentals (/stock-info) and SEC filings (/sec-filings) — so values are near-real-time. During a brief upstream outage the API keeps serving the most recently cached value (up to roughly an hour old) instead of failing; responses do not carry a separate "stale" flag, so treat every value as point-in-time. If no cached value is available while the upstream is unreachable, the call returns 502 (MARKET_UPSTREAM_ERROR); if the market-data service is disabled or not configured, it returns 503 (MARKET_DATA_DISABLED). Responses expose only public, cleaned fields — internal upstream metadata is stripped and never returned.

HyperLiquid endpoints are served from periodic snapshots (typically every few seconds to a minute).

Recipes

Paginate through every page

Request page=1, accumulate data.items from the response, then check data.hasMore. If data.hasMore is true, request the next page (page=2, 3, and so on), continuing until data.hasMore is false. Keep limit at the endpoint's maximum to minimize the number of requests.

Paginate past 10,000 rows with cursor

Offset paging is capped at 10,000 rows. For the three large-list endpoints (/hl/spot/holders, /hl/perp/holders, /hl/leaderboard), use cursor paging to read the full dataset:

  1. Fetch page 1 normally: GET /hl/spot/holders?coin=PURR&limit=100.
  2. If the response includes a non-null nextCursor, pass it as ?cursor=<token> on the next request (omit page): GET /hl/spot/holders?coin=PURR&limit=100&cursor=<token>.
  3. Repeat, always forwarding the latest nextCursor, until it is null or hasMore is false.

Keep limit and sort parameters (coin/sortBy/sort/order) constant across the sequence — changing them invalidates the cursor and returns 400 VALIDATION_ERROR.

Retry with backoff

Honor the Retry-After header on a 429 response — wait that many seconds, then retry. For 502, 503, and other 5xx responses, retry with exponential backoff (for example, wait about 0.5s, then 1s, then 2s between attempts, capping the total number of attempts). Failed attempts (429/5xx) are not billed — only a successful 2xx response is — so retrying is safe and never double-charges for a request that did not succeed.

Errors

Errors use the same envelope, with code non-"0", data: null, and an HTTP status that reflects the problem:

{ "code": "INVALID_API_KEY", "msg": "Invalid or revoked API key", "data": null }
StatuscodeMeaningRetry?
400VALIDATION_ERRORInvalid parameter — unknown coin, bad enum, out-of-range limit. msg names the offending field.No — fix the request
401INVALID_API_KEYMissing or invalid API keyNo — check the x-api-key header
403INSUFFICIENT_CREDITSMonthly credit quota exhausted (paid tiers)No — wait for reset or upgrade
403FORBIDDENAccess not permitted for this account/tierNo
404NOT_FOUNDUnknown route or resourceNo — check the path
404MARKET_ASSET_NOT_FOUNDUnknown symbol (Market Data endpoints)No — verify via /search
429RATE_LIMITEDRate limit exceededYes — wait for Retry-After, then retry
502MARKET_UPSTREAM_ERRORUpstream data provider error (Market Data)Yes — retry with backoff
503MARKET_DATA_DISABLEDMarket data temporarily unavailable / not configuredYes — retry with backoff
500INTERNAL_ERRORUnexpected server errorYes — retry with backoff; contact support if it persists

Handling rule of thumb: retry 429/5xx with backoff (see Retry with backoff); fix and don't blindly retry 400/401/403/404.

On this page