{"api_version":"2026-10-10","policy":"X-API-Version (CalVer YYYY-MM-DD) is bumped on any breaking or notable response-shape change; each is logged here newest-first.","changelog":[{"version":"2026-10-10","date":"2026-10-10","breaking":true,"changes":["New `GET /api/v1/indicators/adx` (Pro): ADX(14), +DI and -DI per asset on `interval` `1d` (default), `4h` or `1h`. These are the numbers the SIGNUM trend colours are computed from (`1d` = `/indicators/signum-rgg` `adx`, `4h` = `h4_adx` on the UTC 00/04/08/12/16/20 grid, `1h` = `intraday_adx`). Each row has `adx`, `plus_di`, `minus_di`, `direction` (`up` when +DI > -DI), `strength` (`strong` at ADX >= 25, `weak` below 20, `moderate` between), `color`, `bar_open` and `final`. `final` is true for 1d and 4h, which read completed bars, and false for 1h, which includes the bar forming at the hourly sweep. A null `adx` means too few bars or a stale intraday series. Params: `coin` (comma list, alias `symbol`; names the universe lacks are listed in `not_covered`), `source`, `min_adx`, `sort`, `order`, `limit`, `offset`, and `history=N`, which attaches the last N completed bars per row. 4h/1h history needs `coin` with at most 5 symbols.","Breaking: `/indicators/technical` and `/indicators/signum-rgg` now return 400 `unknown_parameter` for a query parameter they do not read. They used to ignore it silently, so `?coin=BTC&interval=4h` returned the whole daily universe as if it were BTC on 4h bars. Only callers sending such parameters are affected. Both routes now take `symbol` (alias `coin`), a comma list of assets, and return `not_covered` with the requested names the universe does not cover. `not_covered` is null when no filter is given. The 400 for `interval` points to `/indicators/adx`.","`/indicators/technical` (list and `/{symbol}`): `ma` gains `sma_20`, `above_20` and `dist_from_20_pct` (signed % of price from the daily SMA-20), next to the 50/100/200 fields. They are null when there are fewer than 20 daily closes.","`/algobrain/search` ranking changed. Results are now ordered by tier, and each result's new `match` field names its tier. A page NAMED by the query comes first (`exact_title`: its title, an acronym in its title such as \"(ADX)\", or an alias). Then come pages with the whole query as a phrase in their title or aliases (`title_phrase`), every term there (`title_terms`), every term anywhere (`all_terms`) and any term (`any_term`). Within a tier, concept and strategy pages rank above the generated per-coin pages, so \"ADX\" returns the indicator before the coin whose ticker it was. `score` (BM25) now orders results only within a tier.","AlgoBrain strategy gates. A strategy page can now map each condition it checks (universe, entry, filter, exit, stop, sizing) to ONE endpoint and ONE response field, or mark it `partial` (a human judgement remains) or not runnable (nothing we serve measures it). `/algobrain/page` gains `gates` and `strategy` (the same frontmatter block search results carry). Each gate has `id`, `role`, `rule`, `runnable` (true, `partial`, false), `endpoint` (with `{coin}`-style placeholders), `field`, and optionally `replay` / `replay_field` (a history endpoint for backtests), `input: position` (a condition on your own position, e.g. a time stop), `input: derived` + `inputs` (a condition built from other gates on the page, e.g. a composite score) and `note`. On search, `strategy` gains `runnable` (`all`, `partial`, `none`) and `gate_counts`. The new `runnable` filter finds pages whose every gate is runnable, and `/algobrain/stats` gains `by_runnable`. All of these are null or {} on pages not yet mapped. The 51 Hyperliquid basket strategy pages are mapped first, with 659 gates: 417 runnable, 207 partial and 35 not runnable. No page is fully runnable yet, so `runnable=all` returns nothing until one is.","Breaking: `/algobrain/search` and `/algobrain/page` return 400 `unknown_parameter` for a query parameter they do not read, instead of ignoring it.","404s on `/api/*` for a guessed indicator path (`/indicators/rsi`, `/indicators/atr`, `/rsi`) carry a `hint` naming the route and field that serve it.","`/hyperliquid/candles` and `/hyperliquid/candles/batch`: no shape change. The bar-close pre-warm now also fills 1h bars, plus every coin requested on 1h or 4h in the last few bars at the window size it was requested with. A repeat `closed_only` pull of the same book is then a cache hit instead of a cold fetch held up to `wait` seconds.","Breaking fix: `/backtesting/klines` now honours `interval` (`1m` default, `5m`, `15m`, `30m`, `1h`, `4h`, `1d`). It was never read, so every call, including `interval=4h`, returned 1-minute bars. Coarser bars are aggregated from the stored 1m bars on the UTC grid: `t` is the bar open, `start` is floored to the interval, and each row gains `n`, the number of 1m bars in it (fewer than a full bar means an archive gap or a bar still forming; `n` is null on 1m rows). `grain.cadence` is the interval. One aggregated page scans at most ~278 days of 1m rows, so a long `1d` request pages early through `next_cursor`. Other unread parameters are now a 400.","Breaking fix: `/quant/gex` and `/quant/positioning` matched `symbol` by upper-casing it, so Hyperliquid's k-coins (`kPEPE`, `kBONK`) were never found; names now match case-insensitively. Both take `coin` as an alias of `symbol`, and an unrecognised parameter is a 400 (`?coin=` used to be ignored and returned every coin). `/quant/gex/history` and `/quant/positioning/ladder` also 400 on unread parameters (`days` on history points to `from` / `to`).","Fix: `/quant/gex` `meta.maintenance_margin_tiers` is now Hyperliquid's live margin table for each coin in the response (from HL `meta.marginTables`), with HL's formula: `maintenance_margin_pct` = 50 / `max_leverage` (half the initial margin) and a new `maintenance_deduction_usd` per tier. The previous static ladder was wrong: it used the initial margin as the maintenance rate (2x too high), and its tiers did not match HL's, e.g. BTC stepped down from 40x at $1M where HL steps at $150M. `_default_alt` is removed; every listed perp now has its own ladder.","`/supply/unlocks`: `limit` (1-500) now caps the rows (it was ignored), `symbol` accepts a Hyperliquid k-name (`kBONK` finds BONK), and an unrecognised parameter is a 400."]},{"version":"2026-10-09","date":"2026-10-09","breaking":false,"changes":["OpenAPI spec (`/api/openapi.json`): every required path and query parameter now carries an `example` that answers 200 for a Pro Plus key, so a working call can be built for each operation from the spec alone. A few optional parameters carry one too, where the bare call would 400 or is far heavier than needed: `coins` on `/hyperliquid/candles/batch`, `data_type=daily` on `/backtesting/archives` and `/archives/download`, and the daily-bundle fields on `/archives/purchase`. Date examples are relative (two days ago). New operation extensions: `x-timeout-seconds` (the longest the operation can take; operations without it answer within 10 s once warm), `x-streaming` on `/stream` (the response never completes) and `x-account-scoped` on the invoice and payment-signature routes (the resource belongs to one caller).","The four `/on-chain/whales*` routes are no longer listed in the spec while whale tracking is disabled. They still answer 503 `coming_soon`, and return to the spec when tracking ships.","`/algobrain/search` with no `q` and no filter now returns 200 with the newest pages (`score` null) instead of 400 `empty_search`, so an agent can discover valid page paths from a bare call. A `q` with no letters or digits does the same.","`/algobrain/page` resolves short paths when the file name is unique: `atr-trailing-stop`, `strategies/atr-trailing-stop`, or the path without `.md`. The returned `path` is always the stored one. The 404 `page_not_found` detail adds `suggestions` (up to 5 real paths matching the words sent, else the newest pages) and `search_url`.","`/derivatives/funding-rates`: `hyperliquid.history_count` and `history_status` now come from the last good Hyperliquid funding history (at most an hour old) while a refresh runs, instead of the call waiting up to 8 s and returning `warming`. `warming` now appears only when no history has been fetched in the last hour.","`/backtesting/archives` and `/archives/download` read the archive listing from a cache, so new uploads appear within 15 minutes under steady use and within an hour at most. Response shapes and defaults are unchanged. The unfiltered default `/archives` is still a ~9 MB listing of every klines file; pass `data_type=daily`, `symbol` or a date range for a small one.","Partner gateways only: a key we have registered as a reseller gateway, on a request carrying that gateway's marker header, is limited by a gateway profile (600/min, no daily cap) instead of its tier. Its `X-RateLimit-*-Minute` headers report that limit and no day headers are sent; a per-minute 429 says it is the gateway profile, adds `gateway` (the gateway's id) and carries no `upgrade` block. The header on any other key changes nothing, and every other key's limits and 429 bodies are unchanged."]},{"version":"2026-10-08","date":"2026-10-08","breaking":false,"changes":["New `GET /api/v1/backtesting/liquidation-density` (Pro Plus): Hyperliquid liquidation density near the mark as a time series, instead of reading whole `liquidation_map` / `liquidation_levels` snapshots (~1 MB each). Each point is one coin at one 5-minute snapshot: `time` (bucket start), `snapshot_time`, `mark`, and for cumulative bands of ±0.5/1/2/3/5% of mark `long_below` (USD of longs liquidating below the mark) and `short_above` (shorts liquidating above it), plus the same split `by_trader_type` (vault, market_maker, whale, smart_money, high_leverage, other) and `by_leverage` (3/5/10/20/25/40/50/100x). Every key is always present; 0 means nothing in that band. Params: `coin` (one or a comma list up to 25), `start`/`end` (≤ 93 days, `end` exclusive), `interval` `5m` or `1h` (first snapshot of each hour, default), `splits` (`trader_type,leverage` default, or `none`), `limit`, `cursor`, `format=csv` (all-account bands only). History starts 2026-06-26, when the full-universe liquidation feed began; the earlier `liquidation_levels` snapshots are a ~25-whale sample and are not included.","New `GET /api/v1/on-chain/exchange-flows/{symbol}/history`: archived CEX inflow/outflow back to 2026-05-28, sampled every ~10-40 minutes. Each point has `ts`, the trailing 1h and 24h `inflow`/`outflow`/`net` summed across chains, `chains`, and `per_exchange_1h`. Params: `start`/`end` (ISO 8601, unix seconds or ms; `end` exclusive; default the last 7 days, at most 93), `interval` `1h` (last sample per UTC hour, default) or `raw`, `exchange`, `chain`. Amounts are in the token's own units (USD for USDT/USDC/DAI).","Spike alerts are now archived: every row of `/on-chain/exchange-flows/spike-alerts` is written to the new `exchange_flow_spikes` type on `/api/v1/backtesting/snapshots`. Each 5-minute snapshot holds only the rows first seen in that tick, so a range lists each transfer once. History starts 2026-10-08 00:25 UTC; the live endpoint still holds only the last 500 rows (about 10 hours).","`/on-chain/exchange-flows/spike-alerts`: each alert now has `source`, either `transfer` (a decoded Transfer event with a real counterparty) or `balance_delta` (inferred from a CEX wallet's balance change, which can be the exchange moving funds between its own hot and cold wallets). New optional filters: `symbol`, `exchange`, `chain`, `direction`, `source`.","Fix: the `exchange_flow` component of `/on-chain/score` summed every tracked token's 24h flow in its own native units, so PEPE, SHIB and BONK counts showed up as $100B-$1T of \"net flow\" and pinned the component score to 20 or 80. It now scores the USDT/USDC/DAI 24h net only. Treat `exchange_flow` scores and `reason` values from before this change as wrong; `/exchange-flows/USDT/history` lets you recompute the stablecoin net for past days.","Docs correction: components of `/on-chain/score` with no data are excluded (weight 0) and the remaining weights renormalised. They do not contribute a neutral 50, as `llms-full.txt` said. `whale_accumulation` is currently always excluded because whale tracking is disabled.","Fix: `/market-intelligence/liquidations` rows are now sorted by `liquidation_usd_24h`, largest first. They came back in an arbitrary internal order, so `limit` (default 250, of ~440 tracked coins) dropped arbitrary coins, including BTC and ETH from the default response. Fields are unchanged. With `exchange=asterdex`, AsterDEX-native rows still follow the aggregate rows."]},{"version":"2026-10-02","date":"2026-10-02","breaking":true,"changes":["Removed `upgrade.discount` from every tier 403 and per-key 429 body (and from the edge-served 403s on /quant/gex, /quant/positioning and /hyperliquid/*, the key-mint `limits.upgrade` block and the MCP server's tier copy). The API no longer quotes any discount code. The rest of the `upgrade` object is unchanged (`pricing_url`, `why_subscribe`, `pro`, `pro_plus`, `agent_subscribe` when the x402 rail is live). Codes you already hold still redeem at card or crypto checkout on the dashboard, and are still never accepted over x402. Breaking only for a client that read `upgrade.discount`; it is simply absent now, not null.","`POST /api/v1/auth/keys` mints one key per network per day (was 3); a second attempt is a 429 `ip_rate_limit` with `Retry-After: 86400`. It also allows one active key per MAILBOX: `name+tag@` aliases and Gmail dot variants of an address that already holds a key now get a 409 `mailbox_already_registered` instead of a fresh key. Keys never expire — reuse yours, or sign in to rotate it. Breaking for a client that minted a new key per run."]},{"version":"2026-09-28","date":"2026-09-28","breaking":false,"changes":["New `GET /api/v1/stream`: live market events as Server-Sent Events (`text/event-stream`), one JSON event per `data:` line, with an `id:` line and a `: ping` comment every 15s. Event types: `liquidation` (OKX, Bybit, Hyperliquid; $250 and up; `side` is the LIQUIDATED position side, plus `usd`; Hyperliquid events add `px`, `sz`), `whale_transfer` ($1M+ CEX deposits/withdrawals: `exchange`, `chain`, `direction` inflow/outflow, `amount` in token units, `usd`), `whale_position` (Hyperliquid top-trader position changes of $25K+: `action` entry/exit/increase/decrease, `side`, `size`, `usd` at entry price, `address`, `change_pct`), `funding` (every Hyperliquid perp: `rate_1h`, `prev_rate_1h`, `settle` true on the first print of a new hour), `oi_delta` (every Hyperliquid perp: signed `usd`, `pct`, `oi_usd`, and `window_s` ~60 for a minute move of max($25K, 0.03%)+ or ~300 for a 5-minute jump of max($5M, 1%)+) and `price_move` (a 0.10%+ mark move over ~1 minute on perps trading $1M+/day: `pct`, `price`, `prev_price`, `window_s`). Every event has `id`, `ts` (event time, ms), `recv_ts` (when we saw it, ms), `type`, `venue`, `symbol` (base coin). Filters: `types`, `symbols`, `min_usd` (funding and price moves always pass). Resume with `Last-Event-ID` or `?since_id=` from a ~2h buffer; ids only increase, including across deploys. A client that falls behind gets `event: lagged` and is disconnected. Up to 3 streams per key (429 `too_many_streams`); an unknown type is a 400 `unknown_event_type`. Pro and Pro Plus; a Free key gets a 403.","New `GET /api/v1/stream/recent`: the newest events as JSON (`count`, `last_id`, `events`, oldest first), same event shape and filters plus `limit` (1-500, default 100). Any key, Free included.","New `GET /api/v1/stream/delayed`: no key needed. Events from a 3-minute window that ended 30 minutes ago (`window_start`/`window_end`, event time), at most 900, with `server_now`, `delay_ms` and `events_per_min`. Cached 60s. It feeds the homepage tape.","Same-day update to the stream (later on 2026-09-28): added the `whale_position` and `price_move` types, widened `funding` and `oi_delta` from the top 15 perps to every Hyperliquid perp, added minute-scale `oi_delta` moves, lowered the liquidation floor from $10K to $250, and narrowed `/stream/delayed` from a 5-minute window of at most 300 events to a 3-minute window of at most 900. Existing fields are unchanged.","New `GET /api/v1/strategies` (+ `/strategies/groups`, `/strategies/{slug}`): the strategy library, 317 crypto trading strategies from the AlgoBrain wiki in 22 groups. List rows carry `slug`, `name`, `group`, `summary`, `timeframe`, `complexity`, `indicators` (`[{slug, name, group}]`, what the playbook links to or requires; empty when it declares none) and `endpoints` (the CDA paths that feed it). `?group=` filters; an unknown group is a 400 `unknown_group` carrying `valid_groups`. The detail call adds `edge` (empty when the playbook states none), `edge_source`, `markets`, `data_required`, `backtest_status` (the playbook's own validation label, not a result we measured), `tags`, `wiki_path` and `prompts` (build + backtest prompts for an AI agent). 404 `strategy_not_found`. Any key, Free included.","New `GET /api/v1/indicators/catalog` (+ `/indicators/catalog/groups`, `/indicators/catalog/{slug}`): the indicator library, 187 indicators in 12 groups, with `summary`, `aliases` and `endpoints` (paths that serve the indicator or its inputs); the detail call adds `tags`, `difficulty`, `used_by` (up to 12 catalogue strategies), `wiki_path` and a compute `prompts` entry. A definitions catalogue; live values stay on /indicators/technical and the other signal routes. Any key, Free included.","Confirming a Free key's email no longer mints a personal 25%-off discount code. It still lifts that key from 100 to 1,000 requests/day and switches Pro on for 24 hours. The `next_step` / `upgrade_message` strings on `POST /auth/keys`, `/auth/keys/me`, `/auth/resend-verify` and the Free 429 body no longer mention a code. Field names and types are unchanged. Codes issued before today stay valid until they expire."]},{"version":"2026-09-27","date":"2026-09-27","breaking":false,"changes":["`GET /api/v1/hyperliquid/candles/batch`: new `closed_only=true` returns the last `limit` COMPLETED bars per coin, with no forming bar (`forming_bar_timestamp` is null). It is served from any series fetched since the latest bar close, so after the 4h pre-warm the liquid coins answer instantly for the whole 4h bar. New `wait` (seconds, 0-25, default 8): how long to hold for cold series before answering with `pending`. New top-level `complete` (true when every requested coin is in `series`) and `closed_only` (echo). `symbols` is accepted as an alias for `coins`; both given and different is a 400 `conflicting_parameters`. Default behaviour is unchanged.","The 4h candle pre-warm at each 4h close now fetches 500 bars (was 200), so a `limit=210` SMA200 pull is a cache hit. No response change.","`GET /api/v1/indicators/heatmap`: new row field `pct_4h_bar`, the open-to-close % of the last CLOSED UTC 4h bar (00/04/08/12/16/20), with top-level `bar_4h_as_of` (that bar's close time). null = that bar is not complete in the series. New top-level `moves` object describing each move. `pct_1h` / `pct_4h` / `pct_24h` are unchanged: rolling windows over completed hourly bars, not clock-aligned.","`GET /api/v1/indicators/signum-rgg/{symbol}`: new `?h4=true` adds `h4_history`, the per-bar 4h SIGNUM series `[{ts, color, adx, plus_di, minus_di}]` for the last 90 completed 4h bars. `ts` is the bar's open time on the `/hyperliquid/candles?interval=4h` grid, and the last entry equals the `h4_*` fields. `[]` when not requested or when the 4h read is null.","`GET /api/v1/algobrain/search`: new strategy filters `backtest_status`, `horizon`, `strategy_type`, `complexity`, `crowding_risk`, `market` (each an any-of match; `q` is optional when one is given). Each result has a new `strategy` object with the page's frontmatter: those labels plus `edge_source`, `expected_sharpe`, `expected_max_drawdown`, `breakeven_cost_bps` and `expected_sharpe_source: \"author_estimate\"`. These are the wiki authors' estimates, not results we measured, so search never ranks by them. `strategy` is null on non-strategy pages. `horizon` is a holding style (scalp, intraday, swing, position), not a bar interval. `GET /algobrain/stats` gains `by_backtest_status`. A server with an older index answers a strategy filter with 503 `algobrain_index_outdated`."]},{"version":"2026-09-26","date":"2026-09-26","breaking":true,"changes":["`POST /api/v1/auth/keys/rotate` and `DELETE /api/v1/auth/keys` now return `403` `oauth_token_cannot_manage_keys` when called with an OAuth access token (the short-lived token an MCP client gets from browser login). Those tokens are delegated grants, not the account's own key. To end a connection use `POST /oauth/revoke` or Connected apps on the dashboard. Calls made with a regular `cdk_live_` key are unchanged.","OAuth client registration (`POST /oauth/register`) now accepts plain `http://` redirect URIs only for loopback hosts (`localhost`, `127.0.0.1`, `[::1]`); anything else must be `https://`. Redirect URIs with a fragment or embedded credentials are rejected, and a client may register at most 10. Already-registered clients are unaffected.","OAuth access tokens now last 1 hour (`expires_in: 3600`), down from 30 days. MCP clients refresh automatically with the refresh token, and each refresh re-reads your current tier, so a plan change now reaches a connected app within the hour.","x402: production 402 responses no longer list testnet networks (`eip155:84532` Base Sepolia, `solana:devnet`) in `accepts`, and a payment settled on a testnet is refused. Pay on Base, Ethereum or Solana mainnet.","Force-refresh routes (`POST` `/indicators/signum-rgg/refresh`, `/indicators/technical/refresh`, `/volatility/regime/refresh`, `/meme/regime/refresh`, `/event/regime/refresh`, `/security/regime/refresh`, `/policy/regime/refresh`, `/market-health/refresh`, `/quant/refresh`, `/hyperliquid/trader-profiles/refresh`): a call made while a rebuild is already running now waits for that rebuild instead of starting another, and a call within 5 minutes of the last completed rebuild returns the current data with `refreshed: false`. Response shapes are unchanged; `refreshed` can now be `false`.","MCP `query_api` now rejects paths with `.`, `..` or empty segments, percent-encoding or backslashes. Plain `/api/v1/...` paths work as before.","**Breaking — wallet sign-in.** `POST /api/v1/wallet/challenge` now returns a Sign-In with Ethereum (EIP-4361) `message` bound to cryptodataapi.com, a chain id (new optional request field `chain_id`: 1 or 8453), the nonce and a 5-minute expiry (new response field `expires_at`). Sign `message` exactly as returned. `POST /api/v1/wallet/verify` checks the signature against the challenge the server issued; expired, reused or altered messages are rejected. Optional new `message` field on verify must match exactly if sent.","**Breaking — Solana/Tron payments.** `POST /api/v1/wallet/create-solana-tx` and `create-tron-intent` now return `amount` / `amount_raw` (the plan price plus a unique sub-cent tag, e.g. 29.004817 USDC) and `expires_in`. Send exactly that amount: claims now require an exact match, so a rounded or larger amount no longer matches automatically (contact support with the hash — nothing is lost). One open request per sending wallet across accounts (409), at most 3 open per key (429), and the plan and discount code must match the request.","**Breaking — webhooks.** Each API key may own at most 2 webhook endpoints on the free tier and 10 on Pro / Pro Plus; creating one past that returns 403 `webhook_limit_reached` with `limit`, `current` and `tier`. Existing endpoints are unaffected. Fields are now length-limited (label 1-100 chars, secret 1-255, up to 200 addresses of at most 128 chars, up to 10 events of at most 32 chars); over-limit values return 422.","Billing: card subscriptions are granted only once payment has settled (delayed methods activate when they clear). A chargeback or full refund cancels the subscription and ends paid access immediately; partial refunds don't change access. Checkout sessions that use a discount code expire after 60 minutes, and a limited-use code can't be held by more open checkouts than it has uses left.","Free tier: the 24h Pro trial and the WELCOME discount code are granted once per mailbox — plus-addressed and Gmail dot variants of one inbox share them."]},{"version":"2026-09-25","date":"2026-09-25","breaking":true,"changes":["**`GET /market-intelligence/etf/{asset}/flows` now returns the settled net flow for a US trade date. Before this it returned a partial sum on most weekdays.** Farside fills a new row fund by fund, with IBIT and FBTC last. We were reading the newest row before those funds reported and stamping it with the fetch time. For 23 Sep 2026 BTC it served +$32.4M (MSBT alone); the settled Total is +$346.9M. Only Friday figures, read on the Saturday, were correct.","`flows` now holds settled days only, oldest first. Each row has `trade_date` (YYYY-MM-DD, the US trading day), `flow_usd` / `total_value` (the Farside Total in USD), `status: \"final\"` and `source` (`farside`, or `sosovalue` for a day Farside had not settled or could not be fetched). `time` is now 00:00 UTC of `trade_date`, not the fetch time. The default is the latest settled day, so `flows[0]` and `flows[-1]` both still read it. New `?days=N` (1-1000) returns the last N settled days: BTC back to 2024-01-11, ETH to 2024-07-23, SOL to 2025-10-28.","New fields: `latest` (the newest settled day) and `in_progress` (the day still being reported, with `status: \"partial\"`, `funds_reported` and `funds_total`, or `null`). Do not add `in_progress` to totals. Also new: `count`, `units`, `sources` and `source_health` (last success and failures of each feed).","`sol` no longer reports `0.0` for a day with no reports yet. A Farside `-` now means no value, not zero.","`GET /market-intelligence/etf/btc/aum` now sums settled days only (it used to include the day still reporting). Its daily closes are now paged, so it keeps working once the history is longer than 1000 days.","Archive: `coinglass_etf_flows` snapshots (`/backtesting/snapshots`) and daily-snapshot ETF values before 2026-09-25 are point-in-time copies of what we served, so most weekday rows hold the partial figure. They are not being rewritten. For settled history use `/etf/{asset}/flows?days=N`. Snapshots from 2026-09-25 carry `trade_date`, `status` and `source`."]},{"version":"2026-09-23","date":"2026-09-23","breaking":false,"changes":["**`GET /derivatives/funding-rates`: `hyperliquid.history_count` no longer reads 0 on every call, and the block now says why when it is 0.** A slow cold fetch of recent Hyperliquid settlements used to be cancelled at the 8s bound, so its cache never filled and every call was cold again. The fetch now keeps running in the background. New fields: `hyperliquid.history_available` (bool) and `hyperliquid.history_status` — `ok` (history present), `warming` (the fetch is still running; retry in a few seconds, `current_rate` is unaffected) or `unavailable` (Hyperliquid returned nothing, or the coin is not an HL perp). `history_count: 0` with `warming` is not a stuck feed. The route is one coin per call (`?coin=`, default BTC); for every Hyperliquid perp's rate in one call — what the public /funding-rates board shows — use `GET /hyperliquid/open-interest` (`assets[].funding_rate`).","**`GET /market-intelligence/liquidations`: `count` and `source` are now real.** Per row, `count` is the number of liquidation events in the 24h window and `source` the venues they came from, `+`-joined (`\"hyperliquid+okx+bybit\"`). Both were CoinGlass-era leftovers that always read `0` / `null`, so a row with millions liquidated looked empty to anything counting rows. `source` is null only when no event in the window carries a venue tag; AsterDEX-native rows keep `source: \"asterdex\"`. New top-level `grain` (`kind: rolling_window`, `is_cumulative: true`, `sum_ok: false`, same vocabulary as the `/backtesting/*` block) and `row_count_meaning: \"liquidation_events_24h\"`: every `liquidation_usd_*` field is a trailing-window level, so never sum or difference successive polls; for incremental flow use `/backtesting/hl-liquidation-bars` (Pro). Tier gating is unchanged (free = BTC)."]},{"version":"2026-09-21","date":"2026-09-21","breaking":false,"changes":["**New `GET /indicators/heatmap` (Pro / Pro Plus) — a coin universe as ONE scan table.** Each row joins the SIGNUM_RGG daily colour (`signum`, `days_in_color`, `adx`), rolling hourly moves (`pct_1h`, `pct_4h`, `pct_24h`), 30-day mean daily USD notional (`notional_30` — the same field as `/volatility/regime` `vol.notional_30`) and the daily 200-SMA position (`above_sma200`, `dist_from_200_pct`). It is served from data the API already holds, so a full scan is one instant call and spends no exchange rate limit. `source` defaults to `hyperliquid_perp` (`all` adds the Binance-spot fill); filter with `min_notional_30`, `color`, `above_sma200`; sort with `sort` (`sort=pct` orders by the move named by `interval`, `1h` or `4h`, default `4h`). **The moves are rolling windows over COMPLETED hourly bars ending at `price` (the last completed 1h close):** `pct_4h` is NOT the open-to-close of a 4h bar and is not aligned to 00/04/08 UTC; `as_of` is that bar's close time. They refresh hourly (:05 UTC) with SIGNUM_RGG, and read `null` (with `as_of: null`) until its first hourly pass after a deploy. `null` on a row means: a move — the bar that window needs is missing from the series; `notional_30` — liquidity not measurable (the row is dropped when `min_notional_30` is set); `above_sma200` — under 200 daily bars. Pegged / stablecoin rows are excluded. Unknown query parameters answer 400 listing the accepted ones. It lives under `/indicators/` rather than `/hyperliquid/` because it is computed by the indicator engines, not the Hyperliquid collector.","**A 404 on `/api/*` for a path that does not exist now includes `did_you_mean`** — up to three real paths closest to the one you called (`/hyperliquid/funding` → `/hyperliquid/funding-rates`, `/indicators/signum_rgg` → `/indicators/signum-rgg`, `/hyperliquid/heatmap` → `/indicators/heatmap`), matched ignoring `_` vs `-` and a missing or wrong `/api/v1` prefix. The key is absent when nothing is close. A 404 an endpoint raises deliberately (an unknown id, a snapshot not yet built) is unchanged.","**`GET /hyperliquid/candles/batch` now bounds a call at ~8s (was ~25s) and says when to retry.** Series that have not arrived by then are listed in `pending` as before; the new `retry_after_s` (null when nothing is pending) suggests how long to wait before re-requesting just those coins, scaled to how many are still fetching (3-30s). Why: a cold candle costs Hyperliquid rate limit (~20 weight, whatever `limit` is), so a first pull of many coins takes about a minute of the budget, and holding the call that long outran clients with a 30s read timeout. **If you were treating one call as complete, honour `pending`** — a first pull of many cold coins now returns partial sooner. Repeat pulls are cheaper: a shorter `limit` is now served from a longer window already cached (same freshness), on `/hyperliquid/candles` too, and the 4h candles of the ~60 most liquid perps are refilled just after each 4h bar closes (00/04/08/12/16/20 UTC), so a pull in the first five minutes of a bar is a cache hit. For a whole-universe scan that needs no candles, use `/indicators/heatmap`.","**SIGNUM on 4h.** `/indicators/signum-rgg` and `/indicators/signum-rgg/{symbol}` rows gain `h4_color`, `h4_adx`, `h4_plus_di`, `h4_minus_di`, `h4_bars_in_color` and `h4_computed_from`; `/indicators/heatmap` rows gain `signum_4h` and `bars_in_color_4h`, plus a `color_4h` filter. It is the same ADX/DMI cascade as the daily colour, run over COMPLETED 4h bars on the UTC grid (00/04/08/12/16/20 — the grid of `/hyperliquid/candles?interval=4h`), so it shares a clock with a desk trading 4h bars. The forming 4h bar is never used; `h4_computed_from` is the ISO open time of the last completed bar. `null` means it cannot be read honestly: no hourly series, under 30 complete 4h bars, or a hole at the end of the hourly series (a 4h bar missing one of its four hours is skipped, not guessed). The daily `color` remains the regime filter and `flip_alert` is still defined on 1h-vs-daily. Additive; the fields read `null` until SIGNUM_RGG's first pass after a deploy. Same tiers as the routes they sit on (Pro).","**`/backtesting/hl-funding-bars` and `/backtesting/funding?grain=hourly`: missing settled days are now filled.** Settled funding prints were collected once a day for the previous day only, so a missed run left that day with `funding_sum: null` / `n_settlements: 0` for good (2026-09-12, 09-13 and 09-19 were affected). The daily run now back-fills any of the last 10 settled days it finds missing. No shape change."]},{"version":"2026-09-20","date":"2026-09-20","breaking":true,"changes":["**BREAKING (bug fix) — `POST /wallet/upgrade` on Base / Ethereum: a blockchain node we cannot reach is now `503` with `Retry-After: 30`, not `502`.** The verifier used to trust a single free public node; when it died every Ethereum upgrade failed with `502 Failed to contact RPC node` while the payer's USDC was already in the treasury. It now tries several nodes, and a node that answers \"not found\" for a transaction its peers can see no longer produces a `400`. Only when NO node answers do you get the `503`, and its `detail` says the funds are safe and not to send again — retry after the delay. A transaction that genuinely is not found or not yet confirmed is still `400`. If you branched on `502` for this endpoint, branch on `503` (or on any 5xx).","**`POST /wallet/upgrade` — the `403` for a payment sent from a wallet that is not the account's linked wallet now tells the payer what to do.** Same status and body shape (`error: forbidden`, `message`), but `message` now names the wallet that paid and the account's linked wallet (if any), states that the payment arrived on-chain and nothing is lost, and gives the way out: sign in with the paying wallet (Connect Wallet on the dashboard), then submit the same transaction hash under Verify Payment — or contact support with the hash. The dashboard also no longer offers a Base / Ethereum deposit address to an account with no linked wallet, and checks the connected wallet before it sends anything."]},{"version":"2026-09-19","date":"2026-09-19","breaking":true,"changes":["**x402 fix — the 402 challenge now names the URL you actually called.** The `resource.url` in every pay-per-request / per-resource 402 body (and the `PAYMENT-REQUIRED` header) was the proxy-internal `http://cryptodataapi.com/...`; it is now the public `https://` URL, path and query preserved. Clients that bind payment to the exact resource URL refused to sign the old value. No other field changed.","**x402 fix — `GET /quant/whales` now answers a keyless request with the 402 challenge** it is listed with in `/api/v1/pricing` ($0.03). It was returning 401 `api_key_required`: the route moved behind edge authentication on 2026-09-09 and keyless requests never reached the payment gate. Requests WITH an `X-API-Key` are unchanged (Pro and above, served from the 5-minute collector cycle); a pay-per-request read is served by the main API on its own liquidation-map cycle, so `positions_as_of` may be up to ~15 minutes old on that path.","**BREAKING (bug fix) — the Hyperliquid coin routes no longer ignore a parameter they do not read.** `GET /hyperliquid/candles`, `/funding-rates`, `/l2-book` and `/summary` read `coin`; the rest of the API says `symbol`. FastAPI silently drops unknown query parameters, so `?symbol=SOL` answered **200 with the default coin's (BTC) data**. Now: `symbol` is accepted as an alias for `coin` (send one; both and different → 400 `conflicting_parameters`), the name is matched case-insensitively against HL's universe (`kpepe` → `kPEPE`; a `USDT` / `-PERP` suffix is tolerated), and any unrecognised query parameter is a 400 `unknown_parameter` listing the accepted names (keys starting with `_` are ignored, for cache-busters). On `/hyperliquid/candles` a coin HL does not list is now a 400 `unknown_coin` instead of a silent empty series. If you were sending extra parameters to these four routes, remove them. All tiers.","**Additive `atr` on `/hyperliquid/candles`.** `?atr=14` adds a Wilder ATR to every bar as `atr`, computed on the exact series returned (TR = max(h-l, |h-prev_close|, |l-prev_close|); seeded with the mean of the first N TRs, then (prev x (N-1) + TR) / N — the TradingView `ta.atr` form). `atr` is `null` for the first N bars of the window; pull ~10x the period for a converged value. `atr_period` echoes the request (`null` = not asked, and bars then carry no `atr` key).","**Additive `forming_bar_timestamp` on `/hyperliquid/candles`.** The `timestamp` of the last bar when it has not closed yet; `null` when the last bar is closed. Drop that bar (and its `atr`) for completed-bar signals.","**New `GET /hyperliquid/candles/batch?coins=ETH,SOL,BNB`** — the same trailing candles for up to 25 coins in one call: `{ interval, limit, series: {COIN: [bars]}, count, pending, atr_period, forming_bar_timestamp }`. Unknown coin → 400 `unknown_coin` (nothing is silently dropped). On a cold cache a series that has not arrived within ~25s is named in `pending` rather than holding the response — its fetch continues and is cached, so retry those coins. Trailing `limit` only. All tiers.","**Additive `fees` on `GET /hyperliquid/meta`.** Hyperliquid's published fee schedule as fractions of notional: `perps` / `spot` `{taker, maker}` at the base tier (perps taker 0.00045 = 0.045%, maker 0.00015), the `volume_tiers` table, `as_of`, `source`, `note`. Venue-wide and static on our side — it is a reference for backtests, not your wallet's tier. All tiers.","**New `GET /backtesting/hl-funding-bars`** — Hyperliquid funding and open interest on the candle clock (`interval` 1h / 4h / 1d; `time` equals the matching `/hyperliquid/candles` `timestamp`). Per bar: `funding_sum` (carry settled in `(time, time + interval]`; sums across bars), `n_settlements`, `funding_rate_mean`, `oi_open`, `oi_close`, `oi_change_pct`, `mark_close`, `n_snapshots`. `funding_sum` is `null` with `n_settlements: 0` until that day's settled prints are archived (daily, previous UTC day) — `null` means not settled-and-archived yet, not zero carry. Bars start 2026-03-30: Hyperliquid publishes no historical open interest, so OI history is forward-only from our own capture and cannot be backfilled. Coin list up to 25, window ≤ 93 days, cursor-paged, `format=csv`. Pro Plus.","**New `GET /algobrain/search`, `/algobrain/page`, `/algobrain/stats`** — hosted search over the AlgoBrain strategy wiki (~5k pages), for agents that cannot run its local MCP server. BM25-ranked results with `path`, `title`, `type`, `tags`, `snippet`, `score` (`null` when only filters were given) and a `read` URL; `/page?path=` returns the full markdown. 503 `algobrain_index_unavailable` if the index is not loaded. All tiers; content CC BY 4.0."]},{"version":"2026-09-17","date":"2026-09-17","breaking":false,"changes":["**`GET /volatility/index/history` now serves the full documented window (`days` up to 365) quickly.** It used to re-read one full daily snapshot per requested day: `days=90` took ~23s and `days>=150` outran the edge timeout (Cloudflare 520). It now reads a precomputed daily series, so every window answers in well under a second. For a minute or two after a deploy, while that series is indexed, it returns `503 warming_up` with `Retry-After`. Pro Plus.","**`cvi_realized_30` / `cvi_realized_7` history now covers the whole year.** The volume-weighted index was first archived on 2026-07-23, so earlier points had no CVI value (or no point at all). Those days are now reconstructed from daily klines with the live formula. Points are one per UTC day in `[today - days, today]`.","**CORRECTION — history values before 2026-08-24 changed.** Until the 2026-08-24 universe fix, the live index still counted delisted exchange pairs (Binance keeps serving a dead pair's last candles, so e.g. FTM, BNX, ALPACA carried frozen, extreme volatility and a phantom volume weight). The CVI published 2026-07-24 → 08-23 was roughly **2× too high** (121.66 served vs 64.52 correct on 07-24; 130.04 vs 74.41 on 08-23), and `composite_score` was ~5-9 points high before 08-24. The history now serves the reconstructed values for every day before 2026-08-24, so the series no longer halves overnight on 08-24. The reconstruction matches the live index to within ~1% on every day from 08-24. Live `/volatility/index` was not affected after 08-24. The raw archived snapshots (`/backtesting/daily-snapshots`) are unchanged and still show what was published.","**Additive `backfilled` (bool) on each history point** — `true` when any value on that day is not a verified live capture: every point before 2026-08-24, plus any later day whose archived regime block was itself backfilled. Reconstructed days use today's coin universe; `false` means the values are exactly what was published that day."]},{"version":"2026-09-15","date":"2026-09-15","breaking":true,"changes":["**BREAKING — `POST /payments/subscribe` and `POST /payments/agent-subscribe` no longer accept `discount_code`.** A non-empty value now returns 400 before any quote or settlement: every x402 purchase (subscriptions and passes) is list price. Discount codes apply only to card checkout (`POST /payments/stripe/checkout`) and the dashboard's crypto checkout (`/wallet/*`). `GET /pricing` `notes` gains a line saying so.","**Subscription prices cut: Pro $29/mo (annual $228/yr = $19/mo), Pro Plus $99/mo (annual $948/yr = $79/mo)** — from $39 / $129 ($348 / $1,188). Every price surface moves with it: `GET /payments/plans`, `GET /pricing` `subscriptions`, the tier-403 `upgrade` block and the HTTP 402 quote on `agent-subscribe`. Existing card subscribers keep billing at the price they signed up at.","**7-day x402 passes repriced:** `pass_7d` $15 → $10 and `pass_7d_plus` $45 → $30 (`GET /pricing` `passes` and the 402 quote). 1-hour and 1-day passes, per-request and per-resource prices are unchanged.","**Discount codes are per customer, and an annual plan gets the advertised window, not the whole year.** A code with `duration_months` N means N discounted months per customer — every key sharing an email or wallet: a monthly card subscription repeats the discount N cycles, N separate monthly USDC payments are discounted, and an annual plan takes N/12 of the percentage off its first year (a 50%-off, 3-month code on the $228 Pro year → $199.50; previously the full 50% came off the year). Once a customer has used the window, card and crypto checkout return 400 `You've already used this discount code on this account or wallet.` Codes with no duration (forever) keep discounting every payment.","**`POST /payments/validate-code` gains additive `duration_months` and `reason` fields**, and `prices` is now the amount due on the first payment under the rule above. Sending `X-API-Key` makes the answer account-specific: `duration_months` is the months left, and a used-up code returns `valid: false` with `reason: \"already_used\"`. Anonymous failures stay uniform (`reason: null`). The tier-403 `upgrade.discount.description` copy now says where discount codes can be used.","**`GET /quant/market` and `/quant/coins/{symbol}` gain an additive `current` field** — an unambiguous alias for `regime`, the present-state nowcast. `regime` was already horizon-independent (computed once from the model's posterior, not the forecast), but calling with `horizon=4h` vs `horizon=24h` returned it under the same key both times with nothing marking it as identical, so it was easy to mistake for a per-horizon forecast. `current` is a byte-identical copy for the same scope at the same instant; `regime` is unchanged and stays for backward compatibility. `null` on both while the scope is warming up. Pro.","**`GET /quant/regimes` gains an additive `catalog_version` field** (CalVer) on the 6-regime taxonomy. The `id`<->`label` mapping was already stable in practice; this makes that a checkable contract — unchanged `catalog_version` means every `id` still maps to the same `label` shown in this response, so a consumer can key off `regime.id` directly instead of re-resolving `label` on every call. A relabel or reorder of an existing id will bump this (and get its own changelog entry); appending a new id at the end will not. Open to any valid key.","**`GET /event/regime/{symbol}` and `/security/regime/{symbol}` now resolve `hl_symbol` even when there's no pending catalyst/implicating event for that symbol.** Previously `hl_symbol` was only populated as a side effect of the per-symbol overlay entry, so it read `null` for the common case — most symbols, most of the time, have nothing pending — even though the Hyperliquid perp join key is a static ticker mapping independent of whether anything is currently pending. `null` now means only 'no HL perp for this ticker' (e.g. stablecoins), not 'nothing pending right now.' Pro+."]},{"version":"2026-09-14","date":"2026-09-14","breaking":false,"changes":["**`GET /api/v1/exchanges` and `/exchanges/{slug}` gain an additive `stats` object** on each exchange row: `open_interest_usd`, `volume_24h_usd`, `funding_rate_apr_pct`, `market_count`, `note`, `source`. `null` for every venue we don't (yet) have this for — this is NOT the BTC-only cross-exchange OI/funding comparison already served by `/open-interest` and `/funding-rates`; it's each venue's own aggregate across its full perp book, pulled from that venue's own public API.","**Populated today for `lighter` and `asterdex` only.** Lighter: `open_interest_usd` and `volume_24h_usd` summed across every active perp market via its own `orderBookDetails` endpoint, `funding_rate_apr_pct` from its own BTC perpetual funding rate (annualized). AsterDEX: `volume_24h_usd` and `funding_rate_apr_pct` the same way, but `open_interest_usd` is always `null` — AsterDEX exposes no open-interest endpoint. AsterDEX's `note` field carries a standing caveat: DefiLlama flagged its self-reported volume for a wash-trading-like correlation with Binance's perp volume in Oct 2025. Refreshed every 5 minutes; the `/exchanges/lighter` and `/exchanges/asterdex` pages show the same figures."]},{"version":"2026-09-12","date":"2026-09-12","breaking":false,"changes":["**Every `/backtesting/*` range reader now says what a row IS.** `GET /backtesting/klines`, `/funding`, `/liquidations`, `/hl-liquidations`, `/hl-trade-flow` and the new `/hl-liquidation-bars` carry an additive `grain` block: `kind` ∈ `event` (one fill) | `bar` (a closed bucket) | `rate_snapshot` (the live rate sampled on a timer) | `rolling_window` (a trailing total sampled on a timer) | `settled_payment` (one settlement print), plus `cadence`, `is_cumulative`, `sum_ok`, `window`, `settlements_per_day` and a one-sentence `note`. This exists because two series look like candles and are not: the default `/funding` rows are `rate_snapshot` (~12 samples per Hyperliquid settlement — summing them overstates carry ~12x) and `/liquidations` is `rolling_window` (a 24h level — neither sum nor difference it).","**`/backtesting/funding?grain=hourly` — settled Hyperliquid funding payments.** One row per settlement from HL `fundingHistory` (the carry actually paid), with the new `premium` field; `open_interest` / `mark_price` are `null` on these rows because HL has no historical source for either at settlement time. `grain.kind` = `settled_payment`, `sum_ok: true`. Loaded daily for the previous UTC day (so it lags ~1 day; the live tail is `/hyperliquid/funding-rates`) and seeded from the `funding_deep` archive back to 2024. Hyperliquid only — `exchange=binance` returns `400 grain_unavailable`. The default (`grain=snapshot_5m`) is byte-identical to before apart from the additive fields; `premium` is `null` on snapshot rows.","**New: `GET /backtesting/hl-liquidation-bars`** (Pro, like the tape). Incremental liquidation flow per closed 5m / 15m / 1h UTC bucket, summed from the exact Hyperliquid fill tape: `time`, `coin` (`null` on the market-wide series), `long_usd`, `short_usd`, `total_usd`, `n_long`, `n_short`, `max_fill_usd`. Buckets with no fills are not returned; `start` / `end` are floored to the grid so the bucket containing now is never served. Omit `coin` for one market-wide row per bucket (window ≤ 31 days) or pass a coin / comma list for per-coin rows (≤ 93 days); beyond that `400 range_too_large`. Every bar carries an `audit` block naming the `/hl-liquidations` call whose fills sum to it.","**Keyset paging on every range reader.** New `cursor` query parameter and additive `next_cursor` (string, `null` when the window is exhausted) + `has_more` (bool) response fields on `/klines`, `/funding`, `/liquidations`, `/hl-liquidations`, `/hl-trade-flow`, `/hl-liquidation-bars`. Pass `next_cursor` back as `cursor` with the same `start` / `end`. Rows are now ordered by each table's FULL primary key (`(time, tid)` on the HL event tape, `(time, symbol)` / `(time, coin)` on the multi-symbol readers) — rows that share a timestamp may come back in a different relative order than before; the set of rows for a window is unchanged. The old `start = last.time + 1` pattern is documented as unsafe on the event tapes: one liquidation order sweeping N book levels is N rows at ONE millisecond, and that pattern silently dropped the siblings past a page boundary.","**`coverage` on every range reader** (additive): `local_first` / `local_last` (ms) for the requested scope, and `archive_hint` (string, else `null`) when the window starts before local retention — the rows live in the daily Parquet archive via `/backtesting/archives`. An empty `data` is no longer silent. For `/hl-liquidations` specifically: live capture began 2026-07-23; the archive also holds a pilot backfill 2026-04-24 → 2026-05-05; nothing exists for 2026-05-06 → 2026-07-22.","**Comma-list scoping**: `symbol` / `coin` accept up to 25 comma-separated values on `/liquidations`, `/hl-liquidations`, `/hl-liquidation-bars` and `/hl-trade-flow` (`422 too_many_values` above that); each value rides the index. **`format=csv`** on `/funding`, `/liquidations`, `/hl-liquidations`, `/hl-liquidation-bars` and `/hl-trade-flow` (klines already had it); CSV carries the paging state in `X-Has-More` / `X-Next-Cursor` headers.","**`GET /backtesting/status` gains `limits`** — the bulkhead's real operating limits (2 requests/s per key sustained, burst 20 → 429; 2 concurrent heavy reads with a 25 s queue → 503 `backtesting_busy`; 10,000 rows per page; 60 s public edge deadline) plus `bt_funding_hourly_rows` and `bt_hl_liquidation_events_rows`. The same numbers are on the docs page. Pages compress ~10x at the edge when the client sends `Accept-Encoding: gzip`.","**New: `GET /derivatives/hyperliquid/history`** (Pro Plus). Daily Hyperliquid funding rate, open interest, and mark price, derived from our own 5-min `bt_funding` archive: `date`, `funding_rate_pct`, `open_interest` (coin units), `open_interest_usd`, `mark_price`. `symbol` (default `BTC`) and `days` (default 30, max 180). Data starts 2026-03-30 and always will — Hyperliquid's `fundingHistory` API has never carried open interest or mark price, so there is no source anywhere for OI before we began capturing it ourselves. A companion `/hyperliquid-open-interest` page shows the same data (14 days free, full history + any coin on Pro Plus)."]},{"version":"2026-09-09","date":"2026-09-09","breaking":true,"changes":["**New: a Hyperliquid per-coin trade tape.** `GET /api/v1/hyperliquid/trade-flow?coin=&minutes=` (Pro, ≤ 1440) returns 1-minute buckets built from Hyperliquid's public `trades` stream: `buy_notional` / `sell_notional` (USD by AGGRESSOR side — HL `B` = taker bought, `A` = taker sold), `n_trades`, `vwap`, `max_fill_usd`, `large_fill_share` (share of notional from fills ≥ $25k; `null` when none), `partial` (our socket did not watch the whole minute — `null` notional, never back-filled; a watched quiet minute is zeros) and a running `cvd_usd` from `window_start`. `stream.coverage_pct` reports how much of the last hour was watched; `503 trade_flow_warming` until the first bucket closes. `GET /api/v1/hyperliquid/trade-flow/universe?window=15m|1h|4h` (Pro Plus) gives every perp's `taker_buy_ratio` (0..1), notionals and `large_fill_share`. `GET /api/v1/backtesting/hl-trade-flow?coin=&start=&end=` (Pro, like `hl-liquidations`) serves the archived minutes (live row minus `cvd_usd`), and the daily Parquet data type **`hl_trade_flow`** (one file per day, all coins) joins `/backtesting/archives` (Pro Plus; $1.00/day via `/archives/purchase`). `/market-intelligence/taker-buy-sell` gains an `exchange: \"Hyperliquid\"` leg (percent, 4h, notional-weighted) for every coin HL lists — it was Binance/BTC only; an absent leg means the tape is warming, not that HL had no trades. Forward-only from 2026-09-09; the public stream carries no liquidation flag, so `hl_liquidations` stays a separate feed.","**BREAKING — SIGNUM-RGG daily colour is now computed from COMPLETED UTC daily bars.** `GET /api/v1/indicators/signum-rgg` (list + `/{symbol}`) and the `signum_rgg` block of `/daily` previously included the forming (still-open) day bar, so a marginal flip at 00:30 UTC could un-flip by the close and `days_in_color` counted a day that had not happened — contradicting the documented rule and the causal daily archive. `color`, `days_in_color`, `adx`, `plus_di`, `minus_di`, `flipped_at`, `pct_change_since_flip`, `price`, `range` and the detail `history` now use completed bars only. The forming-bar read moves to new `color_live`, `adx_live`, `days_in_color_live` (equal to the completed fields when the series already ends yesterday; `null` only for rows served from a pre-deploy cache in the first hour). Every row carries `is_final: true` and `computed_from` (date of the last completed bar). The feed still recomputes hourly; the coin-trends page copy was corrected. Expect `days_in_color` to read one lower and a handful of marginal colours to differ on deploy day.","**SIGNUM-RGG, additive.** `pegged: true` marks stablecoins / pegged assets (curated list, or $1 ± 5% with ≤ 10% annualised 20-bar vol); those rows stay in the list but are excluded from `pct_*`, `net_breadth_pct`, `mean_trend_score` and `top_green` / `top_red` (`summary.pegged_excluded` counts them). `flip_alert` is unchanged and now defined: the last 4 closed 1h bars all disagree with the daily colour; companions `intraday_disagrees` (same predicate), `recent_flip` (`days_in_color <= 3`) and `flip_alert_reason` (`\"intraday_disagrees\"` | `null`) were added. List endpoint: new `offset` param, `limit` max raised 500 → 1000, response echoes `offset`. Detail endpoint: `intraday_history` is now **opt-in** via `?intraday=true` (cached 30 min per symbol) — it was fetching ~1,500 1h bars per request and queuing sequential callers into the edge timeout. The detail resolver now tries the exact HL name first, so `/indicators/signum-rgg/kPEPE` returns the HL perp row (it was unreachable; `PEPE` still resolves to Binance spot). New `GET /api/v1/backtesting/signum-rgg?date=YYYY-MM-DD` returns the archived full-universe colour map for that day (`summary` + `by_symbol`, since 2026-03-02; same gate as `/backtesting/daily-snapshots/{date}`).","**`/quant/gex` regime rule change (`regime.rules_version` 1 → 2) — same shape, different reads on deploy day.** The skew leg of the amplify/dampen score now scores from the coin's own 30-day `distribution_context.mm_skew_pctile` (≥ 90 or ≤ 10 → one-sided, 25–75 → balanced) instead of the raw sign, which read \"one-sided positioning\" on ~95% of the universe because market makers are structurally short perps everywhere. While a coin's history is warming the v1 raw rule applies; `regime.inputs.skew_source` says which fired (`mm_skew_pctile` | `market_skew_raw` | `none`). Weights and every other term are unchanged; the full rule set is now echoed in `meta.regime_rules`. Also **`distribution_context.funding_rate_pctile` is `null` when `funding_pinned` is true** (funding glued to HL's 1.25e-5 interest baseline ranked at ~50 by construction — a rank on a constant). Applies to live `/quant/gex` and the `gamma_exposure` / `liquidation_map` snapshot types.","**`/quant/gex` additive: coverage, levels, history.** Per coin: `regime.coverage` (`full` = ≥ 20 MM accounts and ≥ 1 density band; `funding_only` = state rests on funding/OI/cascade alone, treat as low-evidence; `none`) and `regime.insufficient_mm_coverage` — `regime.state` is kept in all cases; filter on coverage. `coverage {bands, mm_n_accounts, has_flip, history_days}`; `levels` (≤ 6 nearest liquidation clusters above/below mark from EVERY account class, `{price, dist_pct, total_usd, side, n_accounts}`, unlike the MM-only `gamma_profile`); `regime.since` (when the current state was committed, forward-only); `regime.inputs.funding_pinned` (≥ 18 of the last 24 hourly samples at the baseline; `null` until 24 samples), `mm_skew_pctile`, `skew_source`; `distribution_context.sample_ts`, `mm_skew_z`, `funding_pinned`; `positions_as_of`. Top level: `positions_as_of`, `meta.classifier_version`, `meta.regime_rules`; `symbol` accepts a comma list (`BTC,ETH,SOL`). New **`GET /api/v1/quant/gex/history?symbol=&from=&to=&grain=1h`** (Pro): the ~hourly scalar ring behind `distribution_context` — `near_density`, `abs_dist_to_flip`, `dist_to_flip_pct`, `mm_skew`, `mm_gross`, `mm_n_accounts`, `regime_score`, `funding_rate`, `oi_change_pct` per sample, ≤ 30 days, forward-only from this deploy (bins live in the `gamma_exposure` archive).","**`/quant/positioning` and `/quant/whales`: refresh time, tag split, ladder, archive.** Both carry `positions_as_of` (ms, completion of the account re-poll the book comes from) top-level and per coin — dedupe on it, not on `timestamp`. `/quant/whales` is now served from the same 5-minute batch-RPC book as `/quant/positioning` (it was on a separate 15-minute cycle, which is why `directional_net_usd` repeated in runs of three). `/quant/positioning` coins gain `by_tag {smart_money, high_leverage}` (orthogonal overlays, not a breakdown of `by_type`; zeros when absent) and `symbol` accepts a comma list; `/quant/whales` gains `summary.by_tag_usd` and per-coin `long_pct_pctile_30d` (own trailing 30 days, `null` until 2 days of samples). Both carry `meta.classifier_version` (CalVer of the account-tagging rules; bumps get a changelog line). New **`GET /api/v1/quant/positioning/ladder?symbol=`** (Pro Plus): the full ≥ $100k-universe liquidation density for one coin — `bins` (all classes), `bins_directional` (ex-MM), `bins_by_class {market_maker, whale, other}`, `flip`, `levels`. `bins_by_class` is also added to the `liquidation_map` snapshot type. New snapshot types **`positioning`** and **`whale_activity`** (5-min, forward-only; no per-account history exists to backfill from). `/quant/whales/history?live_only=true` drops the 120 modelled seed points; `meta.first_live_date` added. `realized_liq.coverage` on collector-served `/quant/gex` gains the Hyperliquid venue (`okx+bybit+hyperliquid_aggregate`).","**Timestamps on every Hyperliquid payload.** `/daily/hyperliquid`: `generated_at`, `prices_as_of`, `funding_oi_as_of` (ms), per-row `as_of`, and `coverage {universe_total, delisted_excluded, rows}` (177 rows = 232 perps − 55 delisted). `/hyperliquid/prices`, `/open-interest`, `/summary` gain `as_of` / `generated_at`; `/hyperliquid/l2-book` gains `ts` (HL's own book time). `/hyperliquid/candles` accepts `start` / `end` (ms, s or ISO; explicit range ignores `limit`, cap 15,000 bars → `400 range_too_large`); HL itself retains ~4 days of 1m, so ranged history comes from `/backtesting/klines`. Documented: `premium` is HL's own asset-context field (passthrough); `open_interest_usd = open_interest × mark_price`; the table refreshes every 60s.","**`/volatility/regime` additive.** `vol.multiplier_at_floor` and `vol.multiplier_floor_days` (consecutive days pinned at the 0.25 floor). Documented that `vol_target_multiplier` is ABSOLUTE (`60 / rv_gk_30`, clamped 0.25–3.0) — for a relative shock gate use `rv_z_7` (≥ 2 recommended); `vol_shock` = 7d Garman-Klass at or above the 90th percentile of the coin's own trailing 90 days. A coin that misses one 30-min compute cycle is now retained with `stale_cycles` (0 = fresh; ages out after 8) instead of vanishing into a 404.","**HMM regime dwell on `/quant/market` (and the `regime` block of `/quant/coins`).** `in_regime_since` (ISO), `pending {label, count, needed}` (a challenger label accumulating bars; `null` when none) and `hysteresis {margin 0.10, bars 2, override 0.70, incumbent_floor 0.10, min_dwell_bars 0}`. There is no minimum dwell: a challenger above 0.70, or an incumbent below 0.10, switches immediately. Retroactive note: model 2.0.0 (feature set `fv2`) went live 2026-06-21, replacing 1.0.0; the label set did not change. Model bumps now require a changelog entry.","**Smaller additive fields.** `/indicators/technical` list: `breadth {total, in_squeeze, pct_in_squeeze, pct_in_squeeze_pctile_30d, history_days}` (share of the full universe in a Bollinger squeeze, ranked against the last 30 archived days; `null` under 10 days). `/market-intelligence/liquidations`: `as_of` and per-row `liq_1h_vs_7d_median {long, short, total, hours_7d}` (trailing hour ÷ median hourly notional over 7 days, from the exact HL fill tape; `null` under 24 hours of tape) plus a `baseline` descriptor. `/liquidity/depth` records: `levels_truncated` per band (true when the 20-level book ends inside the band, so that depth is a lower bound); the `/liquidity/depth/{coin}` doc now states the tracked universe is the top 25 by 24h notional."]},{"version":"2026-09-07","date":"2026-09-07","breaking":false,"changes":["**`GET /api/v1/backtesting/hl-liquidations` is now Pro, not Pro Plus.** The per-event Hyperliquid liquidation tape — every exact liquidation fill across the full HL perp universe, ~30-day hot window — was the one dataset on the Pro Plus-only bulkhead that a Pro key could not read, and the one no candle-and-funding feed sells. A Pro or Pro Plus key now gets `200`; a Free key still gets the tier `403` with the `upgrade` object, and the 24h email-verify Pro trial reaches it. Response shape unchanged. Everything else under `/backtesting/*` (the daily Parquet archive, snapshots, klines, funding) stays Pro Plus. **Tier change, additive.**","**Three x402 rails for wallet-holding agents, all additive.** (1) **Pay per request**: six listed endpoints — `GET /api/v1/quant/whales`, `/quant/market`, `/regimes/current`, `/market-intelligence/liquidations`, `/event/calendar`, `/market-intelligence/etf/{asset}/flows` — now answer a keyless request with `402` instead of `401`: the body carries x402 `accepts[]`, a `resource` block, a CDP Bazaar `extensions.bazaar` declaration and our `price_usd`, `route_id`, `grants_tier`, `alternatives`. Retrying with the signed payment in `x-payment` returns the normal `200` body (unchanged shape) with `Cache-Control: private, no-store`. A presented `X-API-Key` behaves exactly as before. (2) **Pay per time**: `POST /api/v1/payments/agent-subscribe` and `/payments/subscribe` accept `plan` = `pass_1h`, `pass_1d`, `pass_7d` (Pro) and `pass_1h_plus`, `pass_1d_plus`, `pass_7d_plus` (Pro Plus); a pass starts immediately and never replaces a running subscription. (3) **Pay per resource**: new `GET /api/v1/backtesting/archives/purchase` sells one archived Parquet/JSON object per payment (`404` before any quote if the object is missing; Pro Plus keys get the URL free). New `GET /api/v1/pricing` (no key) is the price table; the OpenAPI spec marks the six operations with `x-price-usdc` / `x-payment`. Tier `403` / per-key `429` `upgrade` objects gained `passes`, `pay_per_request` and `pricing_api`. `POST /payments/agent-subscribe` gained `seconds_remaining` (exact for passes, where `days_remaining` is 0). Absent on older responses means not yet deployed."]},{"version":"2026-09-04","date":"2026-09-04","breaking":false,"changes":["**Tier `403`s on the edge-authed endpoints now carry the complete `upgrade` object** — `GET /api/v1/quant/gex` and `/quant/positioning` are authed at our edge rather than in the app, and their `403` body was a trimmed copy: it had `pricing_url`, the discount code and the x402 endpoint, but not `why_subscribe` or the live `pro` / `pro_plus` prices and caps that `/quant/market`'s `403` carries. The edge now serves the same body the app builds, so the two are identical — including `verify_email` on an unverified free key, which the edge could never offer before. **Additive**; every field the old body had is unmoved.","**Per-key `429`s on those same edge-authed endpoints** (`/quant/gex`, `/quant/positioning`, `/api/v1/hyperliquid/*`, `/daily/hyperliquid`, `/daily/hl-traders`) **gained the `upgrade` object and a real `Retry-After`** — the 2026-09-03 change that put `upgrade` on per-key `429`s had not reached the edge, and the edge `429` previously sent no `Retry-After` header at all. Same scoping as everywhere else: Free and Pro keys only, `null`-safe if the window can't be determined (then no `retry_after` field rather than a guessed one). **Additive**."]},{"version":"2026-09-03","date":"2026-09-03","breaking":false,"changes":["**The `upgrade` object gained `why_subscribe`** — one sentence of subscribe-vs-pay-per-call economics for agents comparing this API to metered per-call feeds: GEX, whales and regimes are polling datasets, and at typical ~$0.008–$0.01-per-call rates a single 5-minute poll costs more per month than the whole Pro tier flat. Appears everywhere `upgrade` already did: tier `403`s, the key mint and `GET /api/v1/auth/keys/me`. **Additive**; every existing field is unmoved.","**Per-key `429`s now carry the same `upgrade` object as tier `403`s** — live prices and caps for both paid tiers, the public discount code, `why_subscribe`, and `POST /api/v1/payments/agent-subscribe` (x402) when enabled. A rate-limited key is the clearest signal a caller has outgrown its tier, and until now the daily 429 offered only the verify-email step and the minute 429 offered nothing. Scoped to `scope: \"api_key\"` responses on Free and Pro keys: the per-IP 429 stays unchanged, and a Pro Plus key gets no offer because there is nothing above it to sell. **Additive**; `error`, `message`, `limit`, `window`, `tier`, `scope`, `retry_after` and every header are unchanged."]},{"version":"2026-08-28","date":"2026-08-28","breaking":false,"changes":["**`POST /api/v1/auth/keys` now returns `next_calls`** — an ordered, machine-readable onboarding ladder: `GET /api/v1` -> `GET /api/v1/daily` -> `GET /api/v1/quant/market` / `GET /api/v1/quant/gex`, each with its MCP tool name, whether it needs a key, the tier it needs, and why. Until now the mint's only forward pointer was \"confirm your email\", so an agent holding a fresh key had nothing naming a first *data* call and fell back to whatever it already knew — in the observed case, polling candles. **Additive**; every existing field is unmoved.","**`GET /api/v1` (the endpoint index) carries the same `first_calls` block.** It is step 1 of that ladder, so it now answers \"what do I do next\" as well as \"what exists\". **Additive.**","**Tier `403`s now carry the whole upgrade path, not just a pricing URL.** Every `pro_required` / `pro_plus_required` body gained an `upgrade` object — live prices and caps for both paid tiers, the public discount code, and `POST /api/v1/payments/agent-subscribe` (x402) when it is enabled, which is the only upgrade an unattended agent can complete without a browser. **Additive**; `error`, `message`, `required_tier`, `pricing_url` and `hint` are unchanged.","**A `pro` 403 held by an unverified free key also gains `verify_email`.** Confirming that key's address switches Pro on for 24 hours, which clears the gate for free — so the cheapest correct next step is the link already in the user's inbox, not the pricing page. Present ONLY when it would actually help: never on a `pro_plus` gate (the trial grants Pro, not Pro Plus), and never for a key that is already verified or already paid. Branch on the field's presence, not on tier.","**MCP: `list_capabilities` no longer requires an API key.** It returns static metadata and makes no data call, so it is now callable with zero credentials alongside `create_free_api_key`. Every onboarding surface tells a new agent to call it first, and until now that was advice it could not take. Its payload also gained `first_calls`, `rest_api_user_agent` and `tiers.discount`.","**MCP tool errors now include the actionable half of the response body.** A tier 403 was being flattened to its `message` alone, dropping the `verify_email` / `upgrade` / `discount` blocks the API had just attached — so an agent saw \"requires a Pro key\" with no way forward. The error text now carries them, built from the API's own fields rather than a second copy of the copy.","**Documented: Python's built-in `urllib` needs an explicit `User-Agent`.** Its default `Python-urllib/3.x` signature is refused by our CDN's bot protection *before the request reaches the API*, returning an HTML 403 (Cloudflare error 1010) instead of a JSON error — on every path, including `/llms.txt` and `/mcp`. Any other User-Agent works (an empty one included); `requests`, `httpx`, `aiohttp`, `curl` and Node `fetch` are unaffected. No API behaviour changed — this is now stated in `llms.txt`, the MCP instructions, the Agent Skill and the docs instead of only being discoverable by hitting it."]},{"version":"2026-08-27","date":"2026-08-27","breaking":false,"changes":["**`limit` now works on `GET /api/v1/derivatives/binance/funding-rates`.** It was silently ignored for actively-tracked symbols (BTCUSDT, ETHUSDT, …): the background refresh cached a fixed window and the route served it whole, so `limit=5` and `limit=100` both came back with `count: 30`. The response now carries the window you asked for, and `avg_rate` is recomputed over exactly those settlements instead of a different, unstated set. `current_rate` is unchanged (it is the newest settlement either way). **If you were passing `limit` and quietly receiving 30 rows, you will now receive what you requested — check any code that assumed the array length.** Uncached symbols always honoured `limit` and are unaffected.","**No change to `avg_rate` on `/derivatives/binance/summary`, `/derivatives/funding-rates` or `/daily`.** Those call sites passed a `limit` that had never taken effect, so honouring it literally would have silently narrowed their published averages. They are pinned to the 30-settlement window they have always served. Same for the market-health funding sub-score — its window is unchanged, so no published 0-100 score moves.","**`minutes` on `GET /api/v1/liquidity/depth/{coin}` is now a real time window.** It used to slice the last N *samples*, which only equals N minutes at a strict 1-per-minute cadence — collection actually runs on a ~2-minute timer and can skip, so `minutes=3` was observed returning snapshots spanning ~48 minutes. Any rate-of-change computed off that window was wrong by an unknown factor. The window is now measured back from now, so **expect roughly `minutes / 2` records rather than `minutes`** — the rows you get are the rows that genuinely fall inside the interval. Each record's `ts` is authoritative; `count` reflects what landed.","**`404` on that route now means \"coin not tracked\", not \"empty window\".** An in-universe coin whose window contains no samples (a collector stall, or a `minutes` shorter than the sampling gap) returns `200` with `count: 0` and an empty `history`. Previously an empty slice raised `404`, which conflated a transient gap with an unknown symbol. **Additive**, but branch on `count` rather than treating `404` as \"no data right now\"."]},{"version":"2026-08-26","date":"2026-08-26","breaking":true,"changes":["**Every `401` / `403` / `429` on an `/api/*` path is now JSON with the same shape: `{\"detail\": {\"error\": \"<code>\", \"message\": \"<text>\"}}`.** **Breaking for two paths.** (1) `GET /api/v1/quant/gex`, `/api/v1/quant/positioning`, `/api/v1/hyperliquid/*`, `/api/v1/daily/hyperliquid` and `/api/v1/daily/hl-traders` returned an **HTML** error page on a missing/invalid/expired key — those are authed at the proxy layer, which had no JSON handler for `401`. They now return the same JSON envelope as everything else. (2) The Hyperliquid trader routes (`/hyperliquid/wallets/search`, `/hyperliquid/watchlist*`, `/hyperliquid/copy-signals`, `/hyperliquid/traders/*/refresh`) and `POST /api/v1/market-health/refresh` returned `detail` as a plain **string**; it is now the object above. If you read `detail` directly on those routes, read `detail.message` instead.","**NEW fields `required_tier` and `pricing_url`** on every `403` tier refusal, and `hint` where a route offers a free-tier alternative (e.g. BTC-scoped depth). `required_tier` is `\"pro\"` or `\"pro_plus\"` and is the machine-readable form of the message — branch on it rather than parsing prose. **Additive.**","**`GET /api/v1/quant/gex` is Pro, not Pro Plus.** It always was in the app (unchanged since the 2026-07-08 tier revision), but the proxy-layer gate still answered \"Pro Plus tier required or access denied\", contradicting `/quant/market`, which enforces the identical gate. No access change — only the message was wrong. The same correction lands in the docs and `llms.txt`, which listed full-universe gamma/whales/depth under Pro Plus.","**`error: \"btc_only\"` is retired.** The single endpoint that used it (`/api/v1/volatility/regime/{symbol}` for a non-BTC coin on the free tier) now returns `error: \"pro_required\"` with `required_tier: \"pro\"`, matching every other coin-scoping refusal. **Breaking if you matched on that code**; the status stays `403`.","**A `429` now carries `Retry-After` and the full `X-RateLimit-*` header set** (`-limit-day`, `-remaining-day`, `-reset-day`, and the `-minute` trio). They were previously present only on successful responses, so the one reply that should tell you how long to wait was the one that didn't. **Additive.**","**NEW fields `tier` and `scope` on `429` bodies, and the message now names the tier** — e.g. \"Daily rate limit of 50000 requests exceeded on this Pro Plus API key.\" The number was always correct for the caller's tier, but nothing said which tier it belonged to. `scope` is `\"api_key\"`: limits are per key, not per account or organisation. **Additive.**","**`POST /api/v1/auth/keys` now returns this key's actual limits and how to raise them**, so a caller no longer has to discover its cap by being rate-limited. NEW: `daily_limit`, `per_minute_limit` (the EFFECTIVE limits — a fresh key is unverified, so 100/day, not the tier headline of 1,000), `rate_limit_scope`, `email_verified`, `verified_daily_limit`, `next_step` (confirming the address lifts the SAME key to 1,000/day and adds 24 hours of Pro and a discount code — nothing to re-install), and `upgrade` (Pro / Pro Plus monthly and yearly prices with their caps, plus the x402 `agent-subscribe` endpoint when that rail is enabled). `GET /api/v1/auth/keys/me` gains `rate_limit_scope` and `upgrade` to match. **Additive.**","**Every `401` now sends `WWW-Authenticate: ApiKey realm=..., header=\"X-API-Key\"`,** so a client can discover the auth scheme from the response instead of the prose. **Additive.**","**Wallet-authenticated keys (`POST /api/v1/wallet/verify`) move to the full free tier: 1,000/day, up from 100/day.** They were minted unverified, but the address is `{wallet}@wallet` — no inbox, so the email confirmation that lifts the cap could never be completed. A signed wallet is the proof; the website's own wallet sign-in already treated it that way. **No action needed.**"]},{"version":"2026-08-26","date":"2026-08-26","breaking":false,"changes":["**Security fix: an authenticated GET endpoint could occasionally serve a cached response with no API key or an invalid one.** The origin response cache keyed purely on method+path+query with no regard for whether the caller was authenticated, and a cache hit (`X-Cache: HIT`) was served before the API-key check ever ran. Every affected endpoint's docs always required a key — this was never intended behavior — but if an integration was inadvertently relying on it, that path is now closed everywhere.","`/api/v1/*` response caching is now default-deny: only a short, explicit allowlist of genuinely public JSON (currently just the landing-page price ticker) is cacheable without a key. Every authenticated endpoint now runs its API-key check on every request, so `X-RateLimit-Remaining-Day` / `X-RateLimit-Remaining-Minute` are now **always** present on a successful authenticated response (previously silently absent on a cache hit).","**NEW fields `mint_url` and `docs_url`** on every `401` error body (`api_key_required` / `invalid_api_key` / `api_key_expired`) — the REST path to mint a free-tier key (`POST /api/v1/auth/keys`) and the docs URL, alongside the existing `error` code and `message`. **Additive, non-breaking.**"]},{"version":"2026-08-25","date":"2026-08-25","breaking":false,"changes":["**NEW field `ret_90d`** on the meme-metrics block — `GET /api/v1/meme/regime`, `GET /api/v1/meme/regime/{symbol}` and the `meme_regime.by_symbol[*].meme` map embedded in the daily snapshot. 90-day percent price return, alongside the existing `ret_1d` / `ret_7d` / `ret_30d`. **Additive, non-breaking.**","`ret_90d` is `null` when a coin has under 91 days of daily kline history (e.g. a recent listing) — same null convention as the other return windows. It is not part of the regime classifier (which uses only `ret_7d`/`ret_30d`), so this changes no coin's regime label.","**NEW field `sr`** on the technical-structure block — `GET /api/v1/indicators/technical`, `GET /api/v1/indicators/technical/{symbol}` and the `technical_regime.by_symbol[*]` map embedded in the daily snapshot. `sr.support`/`sr.resistance` are up to 3 levels per side (nearest to current price first), each `{price, strength, dist_pct}` — `strength` is how many swing-high/low pivots clustered into that level, `dist_pct` is signed % distance from mark (negative = support, positive = resistance). **Additive, non-breaking.**","Levels come from swing-pivot detection on the same 250-day daily series the rest of the technical block already uses (no new data source), clustered within 1.5% of each other, and dropped if more than 30% from mark. Both lists are `[]` for a coin with too little price oscillation to have a clear swing point — that is a real \"no clean level\" read, not missing data."]},{"version":"2026-08-23","date":"2026-08-23","breaking":false,"changes":["**NEW `GET /api/v1/exchanges`** and **`GET /api/v1/exchanges/{slug}`** — the venue directory behind our exchange pages, now machine-readable and **public (no API key)**. The list returns `exchanges[]`, `all_slugs[]`, `count` and `disclosure`; the detail route returns `{exchange, disclosure}` and **404s** on an unknown slug. Known slugs today: hyperliquid, binance, bybit, okx, asterdex, lighter, robinhood.","Each row carries the venue profile — `slug`, `name`, `kind` (CEX / DEX / Broker), `website`, `tagline`, `about`, `founded`, `based`, `focus[]`, `specs{instruments, coins, max_leverage, kyc, custody, fiat_onramp}` — plus `signup_url`, `signup_incentive`, `referral_code` and `is_referral_link`. The four sign-up fields are **null / false** for venues we hold no partner link with; `?referral_only=true` filters to the ones we do.","**Read `signup_url` as what it is: a CryptoDataAPI referral link.** `signup_incentive` is what the *end user* gets for using it — Hyperliquid pays them a 4% discount on spot and perpetual trading fees, Binance up to 20% off. We may earn a commission; it costs the user nothing extra. Every response carries a `disclosure` string: if you surface the link, surface that with it. It has no bearing on any other output of this API — no score, regime or signal is affected by where you trade.","Same data is now on the agent surfaces: the MCP tool `get_exchange_links` gained a `referral_code` field and its `disclosure` string changed wording (it is prose for humans, not an identifier — do not match on it), `list_capabilities` gained a `where_to_trade` block, and the venue link is documented in llms.txt, llms-full.txt and the Agent Skill. Nothing existing was renamed or removed."]},{"version":"2026-08-22","date":"2026-08-22","breaking":true,"changes":["**Pro Plus is now 120 requests/minute** (was 60). The daily cap is unchanged at 50,000, because the per-minute limit was never the cost control — it is a burst shield, and it was sized wrong. Read against the rate each daily cap already implies (per_day / 1440), Free allowed a 14x burst, Pro 4.3x and Pro Plus only 1.7x: headroom shrank as you paid more, on the tier whose callers fan out hardest. If you fan out across a symbol list, you can now do it roughly twice as wide per minute without backing off. Nothing else changes — you cannot pull more per day than before, and `GET /api/v1/auth/keys/me` reports your effective limits.","FIXED `GET /api/v1/market-intelligence/liquidations/by-exchange`, which had returned **503 on every call since 2026-07-24** — a month of hard downtime that no alarm caught. When the paid CoinGlass API was retired, each of its data types was either re-sourced from free feeds or allowed to go quiet on the grounds that \"every call site tolerates None\". This one did not tolerate it: its handler raises a 503 on empty, so an unsourced slot meant a dead endpoint rather than a thin one. It is now built from our own realized-liquidation feed, which knew each event's venue at ingest and simply was not storing it.","ADDED per-venue fields on that endpoint. Each row is `{exchange, symbol, total, total_usd, long_usd, short_usd}` for BTC over the trailing 4h. `total` keeps the legacy CoinGlass field name and `total_usd` mirrors it under the naming the rest of the liquidation surface uses, so integrations written against either keep working. Rows are sorted largest-first.","FIXED `GET /api/v1/market-intelligence/etf/sol/flows`, dead the same way and for the same month: the free-source cutover wired Farside's BTC and ETH pages and never added the SOL one, which publishes in an identical shape. SOL spot-ETF net flows are live.","FIXED `GET /api/v1/market-intelligence/etf/btc/aum`, also 503 on every call since 2026-07-24 — CoinGlass was the only AUM feed and no free one exists. AUM is derivable though, because it is coins held times price and the daily flow history gives the coins: we now sum `net_flow_usd / btc_close` across all 670-odd days since the January 2024 launch and multiply by spot.","CHANGED the shape of that endpoint, and read the field names carefully. It returns `aum_usd_from_flows` and `btc_held_from_flows` — NOT a bare `aum_usd`. GBTC converted in January 2024 already holding a large stake, and it enters the flow table only as later outflows, so this measures BTC accumulated THROUGH FLOWS since launch and understates the whole complex by roughly that seed. That is not a rounding difference. Also returned: `cumulative_net_flow_usd` and `price_appreciation_usd` (the two are different quantities — the gap is appreciation, negative when the average buyer is underwater), plus `method`, `source`, `excludes` and `caveats`.","BREAKING for one asset: `GET /api/v1/market-intelligence/etf/xrp/flows` now returns **400** naming the supported assets, where it returned 503 before. No free source publishes XRP spot-ETF flows (Farside has BTC, ETH, SOL and HYPE pages and no XRP one). A 503 tells a client to retry, which was never going to succeed; a 400 tells it to stop. No working integration breaks, because no call to it has ever succeeded. Supported assets are now btc, eth, sol.","CAVEAT on that endpoint, the same one `/api/v1/market-intelligence/liquidations` already carries: coverage is the venue subset we stream (OKX + Bybit + Hyperliquid), so totals run UNDER a true all-exchange number. Binance geo-blocks its liquidation stream from our IPs. The Hyperliquid contribution is exact, from the per-fill node stream. Expect a 503 for a few minutes after a deploy while the 4h window refills with venue-tagged events, and partial totals until it is 4h old."]},{"version":"2026-08-21","date":"2026-08-21","breaking":false,"changes":["**The market-moving news tape now recognises US policy catalysts.** On 2026-08-20 a White House crypto event, a presidential push for the CLARITY Act and a doubling of US Treasury debt buybacks moved BTC roughly 10% and liquidated about $3B of shorts — and produced **zero** events on `GET /api/v1/news/market-moving`. Every one of those headlines was ingested and then discarded: the catalyst taxonomy had no rule for legislation, executive action, agency rulemaking or a sovereign buyer, so each scored as \"not a catalyst\" before entity resolution or impact scoring ever ran. Those rules did exist, but only in the Regime #12 policy sidecar, which reads three government document feeds and never sees crypto media. The two taxonomies now share one block, so both surfaces see the same events.","ADDED `category` values on `/api/v1/news/market-moving`, `/api/v1/news/coin/{symbol}` and the archived `/api/v1/backtesting/news-events` tape (additive — existing categories are unchanged): `legislation`, `executive_signal`, `rulemaking`, `restrictive_policy`, `sovereign_bid`, `strategic_reserve`, `pro_crypto_eo`, `macro_liquidity` and `macro_tightening`. All are market-wide, so they resolve to `symbol: \"MARKET\"` when a headline names no coin. If you switch on `category`, add a default branch.","CHANGED classification of sovereign debt operations. `treasury_buy` matched the \"buy\" prefix of \"buybacks\", so \"US Treasury Doubles Debt Buybacks\" was filed as a corporate treasury purchasing crypto. It is now `macro_liquidity`. `treasury_buy` still covers corporate and public-company purchases, and has joined the market-wide set so a bid that names no ticker is no longer dropped.","FIXED a sign error on `GET /api/v1/policy/headlines` and the Regime #12 `policy_headlines` sidecar. The `ban` rule was unbounded on the right, so \"**Bank**ing Regulator Races to Finalize GENIUS Act Stablecoin Rules\" — a constructive story — scored as a maximum-severity regulatory ban at `bias: -1.0`. Any headline containing \"banking\", \"banks\" or \"banner\" was affected, which on the SEC/CFTC/Federal Register feeds is common. `headline_tilt` and `regulatory_pressure` were skewed negative accordingly.","ADDED whitehouse.gov (news, fact sheets and presidential actions) to the policy feed set. The Federal Register query is filtered to the literal term \"digital asset\" and SEC/CFTC press covers enforcement, so none of the existing sources could see a political event. Items from these broad feeds are admitted only when they name digital assets, so non-crypto policy does not enter the sidecar.","TUNED two scoring windows on the news funnel. The corroboration window went from 6h to 24h — outside it an item fell back to a single source no matter how many desks carried it, which permanently pinned slow-syndicating macro stories below the impact threshold. And the 12h novelty damping is now 6h for policy categories: every market-wide story shares the symbol `MARKET`, so one 12h bucket per category collapsed a busy policy day into a single tape entry. Expect a modestly denser tape on policy-heavy days. The impact threshold itself (0.45) is unchanged.","NEW `GET /api/v1/market-intelligence/squeeze-alerts` (Pro; free tier scoped to BTC) — coins whose forced-liquidation flow is one-sided and abnormal right now. Even with the taxonomy fixed, a news-derived signal is 15-45 minutes behind a breaking story. On 2026-08-19 the BTC tape printed $322.8M of short liquidations in one 5-minute bucket at a 334x short/long ratio, 23 minutes before the first related headline qualified. Returns `direction` (`short_squeeze` / `long_flush` / `balanced`, named after the side being LIQUIDATED — a short liquidation is forced buying), `severity` (0..1), `triggered`, `asymmetry`, `side_ratio`, `spike_ratio`, `oi_change_pct` and `oi_state` (`covering` / `fresh_longs` / `capitulating` / `fresh_shorts` / `unclear`, `null` when open interest is not yet measurable). Coverage is the same venue subset as `/market-intelligence/liquidations` — Binance geo-blocks its liquidation stream from our infrastructure — so severity is built from ratios rather than absolute notionals. Check `window_uptime_h`: `spike_ratio` is measured against a trailing-24h baseline and a recently reconnected feed understates it.","No historical backfill is possible for the news changes. The tape starts 2026-08-18 and RSS serves only a recent window, so the 2026-08-20 events described here will not appear retroactively in `/api/v1/backtesting/news-events`.","FIXED (same day) `spike_ratio` being inflated on a recently reconnected liquidation feed, and ADDED two absolute floors to `squeeze-alerts`. The cascade baseline is a mean per window, but it divided the trailing-24h total by a full day's worth of windows regardless of how much of that day had actually been observed — so for the first day after any deploy it understated the mean by (24h / uptime) and overstated `spike_ratio` by the same factor. This affected every consumer of the signal, not just the new endpoint: coin pages, `/quant/positioning` and the liquidation snapshots all read inflated cascade values post-deploy. Separately, because notional carries only 20% of severity weight, a perfectly one-sided window could reach severity 0.80 on any size at all — in production this raised alerts on $760 and $1,094 of liquidations. `triggered` now additionally requires a minimum absolute notional and a minimum baseline age. That age is measured from the SPAN OF RETAINED DATA — new `baseline_span_h` on each row — and not from process uptime: the feed restores up to 24h of events from its snapshot on boot, so seconds after a deploy the process is new while the history is a full day. Gating on uptime would have muted real cascades for an hour after every deploy. `window_uptime_h` is still reported, but `baseline_span_h` is the field to check before trusting a spike.","ADDED `suppressed_by` to each `squeeze-alerts` row (additive): the list of gates that blocked an alert — `no_spike_baseline`, `spike_ratio`, `asymmetry`, `severity`, `min_notional`, `baseline_too_young` — empty when `triggered` is true. The floors gate the alert, not the scoring, so a suppressed row is still returned by `include_quiet=true` with the reason attached rather than leaving you to re-derive it from the numbers."]},{"version":"2026-08-20","date":"2026-08-20","breaking":false,"changes":["**Free-tier rate limits raised, and split on email verification.** The free tier is now **10 requests/minute and 1,000 requests/day** (was 5/min, 50/day). A free key whose email address has not been confirmed runs on a starter allowance of **100 requests/day** at the same 10/minute; confirming the address lifts the SAME key to 1,000/day in place — no re-mint, no new key, nothing to re-install. Existing keys are unaffected: every account that already verified its email (which is every web signup, since verification is required before a key can be created) was migrated onto the full allowance, and even the starter rung is double the old 50/day. Pro (30/min, 10,000/day) and Pro Plus (60/min, 50,000/day) are unchanged. The per-IP aggregate for free keys rose to 20/min · 2,000/day to match.","Confirming a free key's email also grants **24 hours of Pro** (full-universe quant regimes and forward probabilities, dealer gamma, whale activity, liquidation heatmaps and L2 order books — every coin, not just BTC) and emails a single-use discount code. The trial is granted once per address, appears as an ordinary subscription on plan `trial_pro`, and expires on its own; it is not purchasable.","ADDED fields on `GET /api/v1/auth/keys/me` (additive, non-breaking): `per_minute_limit`, `email_verified` (bool), `verified_daily_limit` (the daily cap confirming would unlock, `null` when there is nothing to gain) and `upgrade_message`. `daily_limit` now reports the key's EFFECTIVE cap rather than the tier default, so an unverified free key correctly reads 100.","ADDED fields on the daily-quota `429` body for unverified free keys (additive): `upgrade_available: \"verify_email\"`, `upgrade_message` and `verified_daily_limit`. Absent for every caller whose cap verifying would not change, so there is never a no-op offer to parse.","NEW `POST /api/v1/auth/resend-verify` — re-send the confirmation link for the calling key's own address. Returns `status: \"sent\"`, or `status: \"already_verified\"` when there is nothing to confirm. Throttled to one send per 10 minutes.","NEW MCP tools `check_key_status` and `resend_verification_email`, so an agent that hits the cap can report the exact next step to its user instead of failing. `create_free_api_key` now asks for the user's real email address and returns a `verify_email` note; it no longer suggests minting a key against a different address to work around an existing one.","NEW archived dataset `token_unlocks` on `GET /api/v1/backtesting/snapshots` (Pro Plus). Each snapshot is the whole unlock board exactly as published at that moment — per-coin `float_pct`, `locked_usd`, `dilution_overhang` and `next_unlock` (`date`, `usd`, `tokens`, `pct_of_float`, `categories`) — recorded every 2 hours. This exists because an unlock calendar is the one dataset that CANNOT be reconstructed after the fact: the upstream source revises vesting schedules in place and serves only the current version, so a schedule pulled today says nothing about what the market could see three months ago. Forward-looking queries stay on `/supply/unlocks` and `/supply/float`; this answers 'what did we know, and when'. Coverage starts 2026-08-20 and grows forward — nothing earlier is recoverable."]},{"version":"2026-08-19","date":"2026-08-19","breaking":false,"changes":["NEW `GET /api/v1/supply/float` — circulating float and dilution overhang per coin, ranked by ASCENDING float. `float_pct` is circulating ÷ `supply_basis`, and `supply_basis` states WHICH denominator was used (`max_supply` when the token is genuinely capped, else `total_supply`) — `max_supply` is absent for over half the universe and 'share of max' vs 'share of total' are different claims, so this is published rather than assumed. Also `locked_supply` / `locked_usd`, `dilution_overhang` (locked value as a MULTIPLE of market cap; above 1.0x more value is locked than the market prices), and `next_unlock` with the soonest cliff priced against float. **`unlock_coverage: not_tracked` means no vesting schedule is published for that token in our source — NOT that it has no unlock scheduled.** Do not read a null `next_unlock` as safe. Defaults to a $500M `min_market_cap` floor (below it a 'tight float' is usually a nominal `total_supply`, and those artifacts otherwise lead the ranking); pass 0 for the full set. Free = top 25 rows; Pro / Pro Plus = full board + filters.","NEW `GET /api/v1/supply/unlocks` — the forward token-unlock calendar as a first-class endpoint, each cliff sized in tokens, USD and `pct_of_float`. This is a CLIFF calendar, not an emissions feed: continuous emission (mining inflation, linear vesting, runs of identical daily releases) is deliberately excluded because a drip is not a step. Coverage is bounded by the protocols DefiLlama tracks — absence is not evidence that a token has no unlock. Free = next 7 days; Pro / Pro Plus = full horizon (35d). The same data still drives `/event/calendar?type=unlock`; this is a view, not a fork.","NEW `headlines` on every `GET /api/v1/news/pulse` row — raw coverage: how many stories in the current feed window name that coin at high confidence, whether or not any of them qualified as a catalyst. This is attention, NOT an event: the catalyst funnel discards ~95% of what it reads, so most coins sit at `news_pressure: 0` while `headlines` keeps counting, and a coin high on headlines with no pressure is being talked about without anything happening. Only high-confidence match modes count (`entity_hint` / `cashtag` / `venue_ticker` / `name`) — `ticker_context` is excluded because its 'crypto word within 90 chars' guard barely constrains anything in an all-crypto feed. Rows are now ranked by `news_pressure` first, then `headlines`; a qualified catalyst still outranks any amount of ordinary news flow. `0` until the first pipeline tick after deploy.","NEW `mint` event type on `GET /api/v1/event/calendar` and `/event/regime*` — dated stablecoin mint / burn steps, with new `delta_usd` (signed) and `pct_of_supply` fields. A step must clear BOTH a $50M floor and 0.5% of that issuer's own float, so USDT's ordinary daily noise does not fire while a small issuer's real move still does (measured over 180 days: USDT ~8 events, USDC ~21, DAI ~5). Bias is `long` on a mint (new dry powder) and `short` on a redemption — unlike a macro print, the direction is known. These are OBSERVED, not scheduled, so they carry `days_until <= 0`, matching how depegs already behave. Deliberately NOT part of the Event Risk composite: a quiet week must not re-weight the score.","FIX token-unlock coverage — the calendar was missing essentially every major unlock. The deep-fetch shortlist was ranked by DefiLlama's rolling `emission30d`, which reads $0 for a protocol whose next cliff is outside that window: measured 2026-08-19, `arbitrum-foundation` ranked #370 of 370, `optimism-foundation` #260, `sui-foundation` #271, `aptos` #92, so none were ever fetched — while the top of the ranking was mining inflation (Bitcoin, Dogecoin, Litecoin) that is correctly discarded as continuous. The shortlist is now driven by OUR universe first. IMPACT: `/event/calendar?type=unlock` went from 5 events across 3 protocols to 37 across 27 on the same day, now including SUI, OP, ZRO, W, TON, XPL, ALT, GRASS and EIGEN.","FIX unlock sizes and phantom unlocks. Each schedule is published in two overlapping shapes (`metadata.events` flat, `metadata.unlockEvents` nested) and both were being read, so every unlock was emitted TWICE — and the nested rows carry no category, so they bypassed the continuous-emission filter and arrived with no size. That is what the `(size n/a)` rows were: a pure linear rate change published as a catalyst. Only one shape is read now, and `unlocked_value_usd` / `pct_of_fdv` resolve for the majors (their emissions files carry no `gecko_id`, which the enrichment keys off — it is now backfilled). A run of identical daily releases is also recognised as a schedule rather than 35 separate catalysts.","FIX news entity resolution — headlines were being attributed to coins whose name appeared only as a fragment of an unrelated word. The project-name matcher ran an unanchored substring search, and it strips a trailing ' Protocol', so `NEAR Protocol` matched inside 'nearly', `Mina Protocol` inside 'dominate' and `Ordinals` inside 'according' — each at the 0.90 `name` confidence. Name matching is now anchored on word boundaries. IMPACT: `/news/market-moving`, `/news/pulse` and `/news/coin/{symbol}` will attribute FEWER events, and `news_pressure` / `news_tilt` for affected coins drop toward 0 — those values were wrong, not merely noisy. Response shapes are unchanged. If you snapshot the tape, expect a level shift on this date rather than a gradual drift.","Project names that are ordinary English words or place names (`Optimism`, `Stellar`, `Movement`, `Lighter`, `Plasma`, `Sonic`, `Canton`, `Venice`, `Jupiter`, `Babylon`, `Maple`, `Turbo`, `Seeker`, `Iota`, `Usual`, `Succinct`) now require a crypto context word nearby, the same guard already applied to `Sky`, `Gas` and `Move`. They resolve at `match_mode: ticker_context` (confidence 0.60) instead of `name` (0.90) — so a story that genuinely covers the project still lands, while 'stellar results' and 'optimism in markets' no longer do. Coins whose only name is now guarded appear in `unresolvable` on `/news/sources`.","NEW `GET /api/v1/changelog` — the machine-readable changelog now has a canonical home under `/api/v1`, so it is listed in the OpenAPI spec (`/api/openapi.json`) and in the `/api/v1` endpoint index like every other endpoint, instead of sitting outside the documented surface. Body is unchanged: `api_version`, `policy`, `changelog`. Public, no API key. It always returns JSON regardless of `Accept` — prefer it for new integrations.","`GET /changelog` now serves the human-readable changelog page to browsers and THIS SAME JSON to everything else. If you poll it, nothing changes and no action is needed: no key, identical body, and the JSON is what a caller sending no `Accept` header, `*/*` (the curl / requests / httpx default) or `application/json` receives. Only a caller that ranks `text/html` above JSON gets HTML.","`/changelog` now responds `Vary: Accept` with `Cache-Control: no-store`, so no cache between us can hand you the representation someone else asked for. `/api/v1/changelog` has a single representation and caches normally (`private`)."]},{"version":"2026-08-18","date":"2026-08-18","breaking":false,"changes":["NEW `GET /api/v1/news/pulse` — a per-coin news feature series for the Hyperliquid perp universe: `news_pressure` (0..1, recency-weighted sum of the impact scores of qualified catalysts, saturating) and a signed `news_tilt` (-1 risk-off … +1 risk-on), plus `event_count`, `top_impact` and a cross-sectional `rank`. This is the endpoint built to join against price. **Every active perp gets a row** — a coin with no news is `news_pressure: 0`, NOT a missing row, so a cross-sectional ranking has the zeros it needs and an absent symbol always means a pipeline failure rather than a quiet market. Free returns the top 10 rows by pressure; Pro / Pro Plus return the full universe.","NEW `GET /api/v1/news/market-moving` — the filtered catalyst tape. Each event carries `symbol` (HL perp, or `MARKET` for stories that move everything), `category`, `impact_score` (0..1), signed `bias`, and `corroboration` (how many independent sources carried the same `(symbol, category)` inside the window — the strongest free indicator that a story is real rather than syndicated). Free: BTC / ETH / MARKET, 24h, top 10. Pro: every perp, 7 days. Pro Plus: full retained history plus the score components (`severity`, `confidence`, `match_mode`, `sources`) and `market_response`.","news: this is a FILTERED TAPE, not a news archive. Roughly 300-500 stories a day are ingested and only the ~15-40 that clear the impact threshold are stored or served. Sub-threshold stories are discarded outright — there is **no raw-feed endpoint at any tier**, and tiering varies coin coverage and score detail, never the noise threshold. Do not build an integration that expects to query all news about a coin.","NEW `GET /api/v1/news/coin/{symbol}` — qualified events plus `news_pressure` / `news_tilt` for one perp. Free is limited to BTC and ETH; Pro and above may query any perp; Pro Plus additionally receives `market_response`.","news: Pro Plus responses carry `market_response` — the MEASURED move after the event at `15m` / `1h` / `4h`: `ret_pct`, `oi_change_pct`, `funding_shift`, `volume_multiplier` (vs that coin's own 30-day baseline) and a combined `abnormality` (0..1). Windows fill in as they mature, so a recent event legitimately reports `null` for `4h` — that means 'not yet', not 'no reaction'. A headline that moved nothing scores near-zero abnormality.","news: every event states HOW its coin was identified via `match_mode` (`cashtag` / `name` / `venue_ticker` / `entity_hint` / `venue_native` / `ticker_context`) and `confidence` (0.6-1.0), because the HL universe is full of tickers that are ordinary English words (`TRUMP`, `MOVE`, `NOT`, `S`, `W`, `ME`, `GAS`, `SAND`). Those can only match on a cashtag or their full project name, never a bare ticker. Filter on `match_mode` if your strategy cannot tolerate a false positive.","news: `category` includes `hl_listing` / `hl_delisting`, sourced from our own Hyperliquid universe diff rather than any feed. These are exact and immediate, and are the only coverage a brand-new listing gets before it has a profile or any press.","NEW `GET /api/v1/news/sources` — per-feed health, the hourly funnel counts (`ingested` / `dropped_noise` / `dropped_no_catalyst` / `dropped_no_entity` / `dropped_below_threshold` / `qualified`, counts only — no titles or URLs), and `unresolvable`: active perps whose ticker is an ordinary word and which have no safe project name, so they can only ever match on a cashtag. The coverage gap is published as a field rather than left as a silent blind spot.","news: sourcing is free and keyless throughout — publisher RSS (The Block, CoinDesk, Cointelegraph, Decrypt, The Defiant, CryptoSlate, Bitcoin Magazine), OKX announcements, and ~25 project blogs / Discourse governance forums / GitHub release feeds for the larger perps. **Latency is ~30s-5min behind the source**: this is a context, risk-off and event-study feed, not a listing-sniping race winner. There is no X/Twitter coverage (no free read tier exists), so anything breaking there reaches us via the outlets minutes later.","NEW `GET /api/v1/backtesting/news-events` (Pro Plus) — the archived news tape with MEASURED market-response labels (`ret_15m` / `ret_1h` / `ret_4h` / `vol_mult` / `oi_change_pct` / `funding_shift` / `abnormality`), so `impact_score` can be validated against realised moves rather than taken on trust. Archived daily to Parquet as the `news_events` type. Only qualified events are archived — this is not an 'all news' history.","news: history starts at deploy day and CANNOT be backfilled — RSS serves a recent window only, so no earlier tape will ever exist. Same constraint as `/regimes/history`."]},{"version":"2026-08-17","date":"2026-08-17","breaking":false,"changes":["NEW `GET /api/v1/volume/scanner` — the Crypto Volume Scanner: every Hyperliquid perp with its live rolling-24h notional (`volume_24h`), its 30-day baseline (`avg_volume_30d` / `median_volume_30d`) and the `multiplier` between them (3.0 = trading at three times its own normal daily volume). Relative volume, so a mid-cap punching above its weight outranks a major drifting sideways. Each row also carries `multiplier_median` (same ratio vs the 30d median — far less distorted by a single prior blow-off day; when the two disagree sharply that coin's history is lumpy), an activity `band` (dormant / quiet / normal / elevated / surging / extreme / unknown), `change_24h`, `open_interest_usd`, `funding_rate` and `days_of_history`. Filter with `band` / `min_multiplier`, sort by `multiplier` (default) / `volume_24h` / `symbol` / `change_24h`. Tiers: Free returns the top 20 perps by 24h volume; Pro / Pro Plus return the full universe (~177 active markets).","volume/scanner: the baseline is 30 **settled** UTC days — today's partial day is deliberately excluded, since counting a half-finished day would drag every average down and inflate every multiplier. Perps listed less than 7 days ago have no honest baseline and report `multiplier: null` + `band: \"unknown\"` rather than a spurious figure; they sort last. `market_multiplier` (Σ24h ÷ Σ30d-average) is computed over only the coins that have a baseline, so numerator and denominator cover the same set.","NEW `GET /api/v1/volume/scanner/{symbol}` — one perp's multiplier plus `history`, the 30-day daily notional series the baseline is built from (one point per settled UTC day). HL thousand-unit tickers (`kBONK`, `kPEPE`, `kSHIB`) are accepted as aliases. Pro / Pro Plus.","volume/scanner: each item also carries `volume_change_24h` — the live rolling-24h notional against the **most recent settled day** (a like-for-like ~24h window, where `multiplier` compares against the 30-day norm) — and `spark_7d`, the last 7 settled days of daily notional for sparkline rendering. Both are null when the coin has no baseline. Note `change_24h` is and remains the **price** change; the volume equivalent is the new field.","The relative-volume block is now computed by one shared helper (`build_volume_metrics`) behind /volume/scanner, /hyperliquid/summary and the per-coin pages, so the three surfaces can never disagree about a coin's multiplier. No response-shape change from the refactor itself.","hyperliquid/summary: gains three **additive** fields — `avg_volume_30d`, `volume_multiplier` and `volume_band` — so the relative-volume read rides along wherever a single coin is already being fetched. Null until the 30-day baseline has been built, or when the coin is too newly listed to have one. No existing field changed; the ranked full-universe scan stays at /volume/scanner."]},{"version":"2026-07-10","date":"2026-07-10","breaking":false,"changes":["quant/gex: each coin gains a `distribution_context` block — trailing-30-day percentile ranks of the near-mark cluster density, |distance to flip|, normalized MM skew (`mm_net_delta/mm_gross`), regime score and funding rate, plus `history_days`/`sample_count`/`status` honesty fields. Every `*_pctile` is null while `status` is `warming` (< 2 days of hourly samples — the history is forward-only). `meta.distribution_legend` documents each field. The block is also archived in the `gamma_exposure` snapshot type from 2026-07-10.","quant/gex + quant/positioning: Pro-tier keys are now accepted — an edge-gate lag from the 2026-07-08 tier revision was still requiring Pro Plus at the proxy layer and 403'd Pro keys despite both endpoints being Pro endpoints. No change for Pro Plus. (Historical `gamma_exposure` snapshots via /backtesting/snapshots remain Pro Plus.)"]},{"version":"2026-07-06","date":"2026-07-06","breaking":false,"changes":["quant/gex: `regime.flip_price`/`gamma_flip` is now capped to within ±30% of mark and returns `null` when there is no in-band liquidation crossover — previously it could report a level hundreds/thousands of % from mark (stale far-out clusters). Consumers trading 'price vs flip' should treat `null` as 'no meaningful flip', not zero.","quant/gex: each `regime` gains a `confidence` field (0–1) scaling with the MM account count and gross book behind the read, so thin-sample regimes can be down-weighted.","quant/gex: `regime.inputs.realized_liq` now populates for HL 1000x-multiplier meme perps (kPEPE/kBONK/…) — a symbol-key fix maps them to the OKX/Bybit `1000<BASE>` / bare-ticker feeds.","quant/gex: delisted HL markets (zero OI/funding/volume, e.g. TON) no longer surface as empty shells; the universe is the live perp set.","backtesting snapshots: NEW `gamma_exposure` snapshot type — archives the /quant/gex MM-lens per-coin map (mm_net_delta, gamma_profile, gamma_flip, funding/OI + composite regime) at 5-min cadence, byte-parity with the live endpoint. Pull via `/backtesting/snapshots?data_type=gamma_exposure` (Pro Plus).","backtesting snapshots: the existing `liquidation_map` type already carries the full-universe composite `regime` per coin (5-min, since 2026-06-26) — if you consume `liquidation_levels` you can read the regime layer from its sibling `liquidation_map` today.","backtesting funding: HL funding/OI is now archived for the FULL live universe (bulk snapshot, no per-coin cost), so mid-cap coins no longer silently lose their funding series when 24h volume rank drops them out of the top-N kline set."]},{"version":"2026-06-27","date":"2026-06-27","breaking":true,"changes":["quant/gex: `?symbol=X` now returns the bulk envelope `{scope, note, timestamp, meta, coins:{X:{...}}}` — a single-symbol query is the SAME shape as the full response, just narrowed to one coin. Previously it returned a flat per-symbol object. Read `response['coins'][SYMBOL]`. An unknown symbol yields `coins: {}` with HTTP 200 (not 404)."]}]}