API reference

Two market-analysis endpoints, one header, no SDK. Start with a free key by email; that is the only key, and no paid plan is offered on the site.

New to the terms on this page? Learn explains day types, volume-price analysis and how to read a probability from zero, and Guides walks through max pain and gamma exposure by hand. This page assumes you just want to call things.

Base URL and authentication

Every endpoint lives under https://api.quantdata.uk, and every one of them takes a single header:

Machine-readable contract: https://api.quantdata.uk/openapi.json. Import that OpenAPI document when an agent platform supports Actions or tool schemas; this page remains the human-readable reference.

X-API-Key: qd_9f3c...

Keys always begin with qd_. There is no OAuth flow, no bearer token exchange and no session cookie — the header is the entire authentication story, which is deliberate: it means any AI agent that can make an HTTP request can use the API without an integration project. Requests are HTTPS only. Treat a key like a password; if one leaks, email us and we will rotate it.

Start with one anonymous look, then get a free key

Omit the header and one source can receive 1 complete first-look response per UTC day. That first response has the same analytical fields and no watermark. Its free_tier.next_action points to the free-key request.

To go further, request a free qd_ key with an email address. No login, password, card or GUI is required:

curl -s -X POST https://api.quantdata.uk/v1/access/free-key \
  -H "Content-Type: application/json" \
  -d '{"email":"YOUR_OWN_EMAIL_ADDRESS","marketing_opt_in":false,"source":"docs"}'

The endpoint returns 202 and delivery status, never the raw key:

HTTP/1.1 202 Accepted
Content-Type: application/json
Cache-Control: no-store

{
  "status": "verification_required",
  "delivery": "email",
  "message": "Confirm your inbox using the verification email before receiving a usable key.",
  "free_key": {
    "email_verification_required": true,
    "price_usd": 0,
    "card_required": false,
    "account_required": false,
    "calls_per_utc_day": 10,
    "shared_across": ["brooks", "weis"],
    "auth": { "header": "X-API-Key", "prefix": "qd_" }
  },
  "marketing_updates": "not_subscribed"
}

First confirm the one-hour, single-use verification link sent to your inbox. The key is activated and displayed only after you press the confirmation button; a copy is emailed to you. Signup and MCP responses never contain a key. marketing_opt_in is optional and defaults to false; set it to true only when the email owner explicitly asks for product updates. A true value records a pending request only. The recipient must reply SUBSCRIBE to the delivery email before any marketing updates begin, so another person cannot subscribe an inbox they do not control. The delivery email is a service message, not marketing consent.

A free key provides 10 successful responses per UTC day, shared across Brooks, Weis, Max Pain and Gamma. Both the anonymous and free-key counters reset at 00:00 UTC. Their responses carry free_tier.access_kind, calls remaining, reset time and the correct next action. When either allowance runs out, the API returns 429 FREE_QUOTA_EXHAUSTED with Retry-After and quota_resets_at. The anonymous error offers request_free_api_key; the free-key error carries the reset time and a sentence to relay to the person. A source address is hashed rather than stored for the anonymous counter, as the privacy page spells out. The same two endpoints are also a remote MCP server — per-client connect instructions on the MCP page.

Or connect over MCP

The same two endpoints are also a remote MCP server, for Claude Desktop, ChatGPT, Cursor, VS Code, Zed and anything else that speaks the Model Context Protocol. There is nothing to install — point the client at:

https://api.quantdata.uk/mcp

Transport is streamable HTTP. The MCP tools are the same two APIs behind another transport. No authentication follows the same no-key rules as the HTTP API: one anonymous first-look response per source, or a shared 100-per-platform daily pool when the call arrives through a recognised AI-platform fetcher. The MCP access tool quantdata_request_free_api_key requests a verification email and never returns a key. The owner must open the email and press the confirmation button before free API access is granted. Configure the key received after verification in the client using X-API-Key for the shared 10-response UTC-day allowance. The tool descriptions carry the same coverage limits and caveats as this page.

Free access

Verify your email to receive a free key. Brooks and Weis share 10 successful calls per UTC day. Research notes, studies and methodology are free to read without a key.

Max Pain and Gamma data services were discontinued on 14 October 2026. Their old API URLs return 410 Gone without charging or consuming free calls.

Quickstart

# Brooks price-action events in the freshest window
curl -H "X-API-Key: qd_9f3c..." \
     https://api.quantdata.uk/v1/brooks/ES=F

# Weis Wave state, US day session
curl -H "X-API-Key: qd_9f3c..." \
     https://api.quantdata.uk/v1/weis/ES=F

# Same instrument, Asian session (24h instruments only: =F futures, =X spot FX)
curl -H "X-API-Key: qd_9f3c..." \
     "https://api.quantdata.uk/v1/weis/ES=F?session=asia"

# Full bar stream, including pre/post-market; matches the published Weis reference-rate window
# Requires a source with real extended-hours volume; otherwise returns 422 NO_EXTENDED_VOLUME
curl -H "X-API-Key: qd_9f3c..." \
     "https://api.quantdata.uk/v1/weis/ES=F?session=full"

Endpoints

GET /v1/brooks/{symbol}

Rolling Brooks price-action events in the current window, each with the pre-registered outcome rate measured for that window; range breakouts additionally carry a calibrated per-event failure estimate. Use a key for ongoing calls. Definitions and validation are described on the Brooks Events page. The same response carries two further blocks: day_type, the five-class day-type distribution from the model trained for the current window — the day session (mode=day_session, published accuracy 66%/80% against a 37% baseline) and the Asian window (mode=asia_session, trained natively on Asian windows, pre-registered and confirmed zero-shot on NQ, with its own accuracy figures on its own label taxonomy — the two sets are not comparable; the gap window has no validated model and returns available: false) — and shape, a plain arithmetic description of the bars already printed in the window.

Parameter In Type Default Notes
symbol path string required Ticker in any supported format. Normalised server-side — see symbol formats below.
window query rth | gap | asia freshest bars rth is the market's own day session — 09:30–16:00 New York for US symbols, local hours for Hong Kong and A-shares. gap covers 16:00–21:30 Beijing — the stretch between the Asian close and the US open. asia covers 09:00–16:00 Beijing. gap and asia are limited to 24-hour instruments — futures (=F), spot FX (=X) and crypto; anything else returns 400. Omit the parameter to follow whichever window has the freshest bars.
{
  "symbol": "ES=F",
  "window": "gap",
  "window_is_live": true,
  "session": "2026-08-06",
  "bars": 41,
  "quote": {
    "open": 6362.25, "high": 6371.5, "low": 6355.0,
    "last": 6368.75, "prev_close": 6360.5, "change_pct": 0.13
  },
  "source": "leandata",
  "volatility_base": {
    "avg_daily_range_5d": 62.75,
    "definition": "mean high-low range of the last 5 completed US regular sessions"
  },
  "events": [
    {
      "type": "range_breakout",
      "direction": "up",
      "at": "2026-08-06T18:05:00+08:00",
      "bars_since": 6,
      "breakout_level": 6366.0,
      "range": { "low": 6358.25, "high": 6366.0 },
      "outcome_so_far": { "failed_within_10_bars": null, "resolved": false, "bars_observed": 6 },
      "conditional_estimate": {
        "p_fail_within_10_bars": 0.58,
        "meaning": "Of past breakouts whose own rolling context looked like this one, this share closed back through the breakout level within 10 bars."
      },
      "reference": {
        "failure_rate_pct": {
          "within_5_bars": { "rate_pct": 50.4, "n": 3918 },
          "within_10_bars": { "rate_pct": 62.8, "n": 3916 },
          "within_20_bars": { "rate_pct": 72.4, "n": 3882 }
        }
      }
    }
  ],
  "day_type": {
    "available": false,
    "why": "No day-type model has been trained or validated for the gap window. The day session has its own model (mode=day_session) and the Asian window has its own (mode=asia_session); the gap window is covered by the shape block and the event reads instead.",
    "window_note": "Two validated day-type reads exist, each from a model trained on its own window ..."
  },
  "shape": {
    "bars_measured": 41,
    "net_move_pct_of_range": 61.2,
    "close_position_in_range": 0.83,
    "one_way_share": 0.58,
    "deepest_pullback_pct_of_range": 24.5,
    "high_made_at": 0.92,
    "low_made_at": 0.04,
    "direction_changes": 3,
    "range_tightness": 0.74,
    "how_to_read": "Arithmetic over the last 41 five-minute bars — what this stretch did, not what comes next ...",
    "not_a_forecast": "Measured description of bars that have already printed. It carries no accuracy figure because nothing is being predicted, and it is not the day-type classification ..."
  },
  "reference_note": "Measured on ES futures 5-minute bars, 2010-2026, pre-registered.",
  "disclaimer": "Educational analytics, not investment advice."
}
FieldTypeMeaning
symbolstringThe normalised symbol the read was computed on. May differ from what you sent.
windowstringThe window the events were built on: rth, gap or asia. An unrecognised value such as eth returns 400 rather than quietly falling back.
window_is_livebooleanCheck this before saying "now". true means the window is open and this read covers the window in progress. false means you are reading the last completed one; a window_note spells it out.
sessionstringThe window-local trading date the read covers, YYYY-MM-DD.
barsinteger5-minute bars inside the window so far. A complete US day session is 78; the gap window holds 66.
quoteobjectopen, high, low, last, prev_close, change_pct for the window — so you can verify the read describes the stretch you think it does.
sourcestringWhich market-data vendor served the bars.
volatility_baseobjectThe 5-day average daily range every event definition is scaled by, with its definition spelled out.
eventsarrayEach classical event detected in this window today (first occurrence per type), with direction, timing, live outcome status, the pre-registered reference rates for this window, and — on range_breakout — the calibrated conditional_estimate. An empty array means nothing has fired yet; that is a finding, not an error.
day_typeobjectThe calibrated five-class day-type distribution for the session in progress. available is true only in the rth window; in gap and asia it is false with a why, because the model reads a session from its open and those windows have none. When available: bars_read, probs (five classes, descending, summing to 1), meaning, accuracy, and below 18 bars a provisional_note. This block is a prediction about an unfinished day and carries its measured accuracy with it.
shapeobjectArithmetic over the bars already printed in this window: bars_measured, net_move_pct_of_range, close_position_in_range, one_way_share, deepest_pullback_pct_of_range, high_made_at, low_made_at, direction_changes, range_tightness, plus how_to_read and not_a_forecast. Present in every window including gap, and absent only when fewer than 10 bars have printed. It measures a stretch that already happened, so it has no accuracy figure and is not the day-type read.
window_notestring (optional)Present whenever window_is_live is false. Names the completed window being returned so it is not reported as live action. Relay it.
window_progress_notestring (optional)Present when the window just opened: the first range needs 10 completed bars plus a breakout bar, so no range event can exist earlier.
coverage_notestring (optional)Present on symbols other than ES, NQ and QQQ: the reference rates and the conditional model were measured and validated there, so elsewhere they are context, not claims. Relay it.
symbol_notestring (optional)Present when the ticker resolves but is a known confusion trap — for example GOLD, which is a US-listed equity (Gold.com, Inc.) and not the metal — for the metal use GC=F or GLD.
disclaimerstringAlways present. Surface it; do not strip it.

The example above is a gap read, so its day_type block is unavailable by construction. In the rth window the same block looks like this:

{
  "day_type": {
    "available": true,
    "mode": "day_session",
    "bars_read": 54,
    "probs": {
      "TrendUp": 0.34,
      "Reversal": 0.22,
      "TrendingRange": 0.21,
      "Range": 0.2,
      "TrendDown": 0.03
    },
    "meaning": "Calibrated probability distribution over the five day types this session
                could resolve into. Report the distribution, not just the top class — the
                second script is often very much alive.",
    "accuracy": "Measured on held-out S&P 500 E-mini years the model never trained on: at
                 18 bars (90 minutes in) top-1 53.5% and top-2 68.6%; over a complete
                 session 66% and 80%. The majority class covers 37% of days — quote that
                 baseline alongside any accuracy figure. Calibration error is under 10% ..."
  }
}

Two different kinds of statement live in one response, and they should not be merged. day_type estimates how an unfinished day resolves, so it is scored: 66% top-1 and 80% top-2 over a complete session, 53.5% and 68.6% at the 18-bar mark, against a 37% majority-class baseline, on held-out ES years. shape measures bars that have already printed, so there is nothing to score and no accuracy figure to borrow — it is not a day type, and it says nothing about what comes next.

Only our own labels and probabilities are returned. No course material, slide titles or third-party text is ever exposed through this endpoint.

GET /v1/weis/{symbol}

Weis Wave structure and volume-price events for the instrument. Use a key for ongoing calls.

ParameterInTypeDefaultNotes
symbol path string required Must be an instrument whose data source carries volume. Same formats as /v1/brooks.
session query rth | asia | full rth rth is the US day session. asia is accepted only for 24-hour instruments (=F, =X), otherwise 400; in practice Weis needs exchange volume, so spot FX still returns its no-volume result. full uses the whole bar stream, including pre- and post-market, and is the window on which the published Weis reference win rates were measured. It is valid only when the active source publishes real extended-hours volume. Otherwise the endpoint returns 422 NO_EXTENDED_VOLUME; obey its retryable field, or use rth.
{
  "symbol": "ES=F",
  "asof": "2026-07-24",
  "session_window": "rth",
  "session_is_live": true,
  "source": "leandata",
  "waves_total": 63,
  "current_wave": {
    "dir": "up", "bars": 7, "move": 12.25,
    "volume": 41822.0, "vol_vs_median20": 0.63
  },
  "recent_waves": [
    { "dir": "down", "move": -21.75, "bars": 14,
      "volume": 128640.0, "end_time": "2026-07-24 10:35:00-04:00" },
    { "dir": "up", "move": 18.5, "bars": 9,
      "volume": 74210.0, "end_time": "2026-07-24 11:20:00-04:00" }
  ],
  "events": [
    { "type": "no_supply_long", "dir": "long",
      "time": "2026-07-24 12:15:00-04:00", "price": 6404.75,
      "reference": {
        "es_win": 0.514, "es_n": 23077, "es_p": "3e-5",
        "verdict": "CONFIRMED(ES)",
        "warning": "reverses on BTC (48.3%) — do not port across assets"
      } }
  ],
  "disclaimer": "Wave/event analytics derived from Weis Wave methodology. Reference stats are ES-futures-specific (2010-2026, pre-registered tests); edges do NOT transfer across assets (verified: BTC null/reversed). Educational analytics, not investment advice."
}
FieldTypeMeaning
session_windowstringThe window used: rth, asia, or full.
note_windowstring (optional)On rth and full, states whether the returned wave construction matches the full-stream window used for the published reference win rates.
note_sessionstring (optional)On asia, states that the window is experimental and the reference win rates do not transfer directly.
waves_totalintegerCompleted waves detected in the fetched window. Event detection needs at least 46.
current_wave.dirstringup, down, or forming when no wave has been established yet.
current_wave.barsintegerBars elapsed since the last confirmed pivot.
current_wave.movefloatPrice change since that pivot, in the instrument’s own units.
current_wave.volumefloatVolume accumulated in the wave so far.
current_wave.vol_vs_median20float | nullThat volume divided by the median volume of the last twenty completed waves. Below 1.0 means this move is being made on less than normal effort. null when there is not enough history.
recent_waves[]arrayThe eight most recent completed waves, oldest first. Each has dir, move, bars, volume and end_time.
events[].typestringOne of cib_long, cib_short, no_supply_long, no_demand_short, sot_short.
events[].dirstringlong or short — the direction the tradition assigns to the event, not a recommendation.
events[].timestringTimestamp of the bar at which the event became knowable. Never earlier than the data that confirms it.
events[].pricefloatClose at that bar.
events[].referenceobjectPre-registered statistics for that event type on ES 5-minute data, 2010–2026: es_win (win rate as a fraction), es_n (sample size), es_p (p-value where significant), verdict, and note or warning where the result reverses or fails to transfer.
notestring (optional)Present when there is wave structure but fewer than 46 waves, so no event can fire yet.
errorstring (optional)Present instead of an analysis when the source has no volume or fewer than 120 bars.
disclaimerstringAlways present, and it carries the cross-asset warning. Surface it.

A maximum of ten events are returned, most recent last. Reference win rates hover between 45% and 52% and two of them run the opposite way from the tradition — that is the measured truth, not a bug. See the Weis Wave page for the full table and the pre-registration.

Symbol formats

Symbols are normalised server-side, so several common spellings resolve to the same instrument. The rules:

MarketFormatExamples
US stocks and ETFsPlain tickerNVDA, TSLA, SPY, QQQ
US futuresRoot plus =FES=F, NQ=F, GC=F, CL=F
Hong KongDigits plus .HK; leading zeros and an HK: prefix are both accepted, and codes shorter than four digits are padded to four3690.HK, 03690.HK, HK:3690 all resolve to 3690.HK; 700.HK and HK:700 resolve to 0700.HK
China A-shares6-digit code; the exchange suffix is inferred (6/9/5 leading digit → Shanghai, 0/1/2/3 → Shenzhen)600519, 600519.SH, SH600519, 600519.XSHG all resolve to 600519.SS
Crypto spotBase and quote joined by a hyphen; USD, USDT, USDC, EUR and GBP quotes are acceptedBTC-USD, ETH-USD, SOL-USDT
Crypto perpetualsBase plus -PERP; Kraken’s own PF_ code also worksBTC-PERP, SOL-PERP, PF_XBTUSD
Spot FXCurrency pair without a slash; 15 major currenciesEURUSD, eurusd, EUR-USD, EURUSD=X all resolve to EURUSD=X. EUR/USD does not work — the slash is a path separator, so it 404s before the symbol is parsed.

Crypto bars come from Kraken, which publishes real per-bar volume; spot reads cover the last 60 hours and perpetuals about seven days. On thinly traded pairs many five-minute bars record no trades at all, and every Weis condition is a volume ratio, so /v1/weis returns 422 TOO_ILLIQUID rather than waves built on gaps in trading. The response reports the share of bars that traded. /v1/brooks is unaffected — its event definitions use price only. Liquid pairs such as BTC-USD, ETH-USD and SOL-USD traded in every bar when we checked. Note that no win rate has been measured on any crypto pair: the pre-registered transfer tests on BTC confirmed none of eight signals, and one reversed. Treat crypto reads as descriptive.

Spot FX works on /v1/brooks only, with a standing caveat in the response: the reference rates and the conditional model were measured on ES, NQ and QQQ, so FX reads carry a coverage_note and are descriptive. /v1/weis cannot run on spot FX at all (no consolidated volume exists over the counter) — use the CME currency futures instead: 6E=F, 6B=F, 6J=F, 6A=F. The options endpoints do not cover FX and there is no substitute to offer (see the prompt below for why currency ETFs do not work either).

Never pass a plain English instrument name as a ticker. GOLD resolves — to a US-listed equity, Gold.com, Inc., not the metal. The API flags that specific trap with a symbol_note, but the general rule matters more: map the name to an instrument first. Gold is GC=F for futures, GLD for the ETF or GC=F for futures; the S&P 500 is SPY or ES=F; the Nasdaq is QQQ or NQ=F; crude is CL=F or USO. A wrong ticker returns a 404 that suggests the right one, and a 404 is never charged — so guessing is safe.

Errors

Every error is RFC 9457 application/problem+json — the standard shape, with type, title, status and detail. Every body additionally guarantees error_code and retryable. Access and billing errors add richer handoff fields such as available_actions and message_for_your_human; ordinary request and validation errors do not promise them.

The retry contract, which is the one thing worth memorising: retryable is always false on 401, 402 and 403, and none of the three should ever be retried. A 402 in particular will never succeed on retry — it is a billing state, and the second call returns the same thing as the first. Branch on that one boolean and an agent cannot get stuck hammering a closed door. Transient upstream failures, quota responses with a reset time, and a temporary extended-volume source fallback can carry retryable: true. Obey the field in the body instead of inferring from status alone. Access errors also tell an agent when to request a free key and when to hand a sentence to a person.

Statuserror_coderetryableMeaning and what to do
400 REQUEST_FAILED false Two cases. Either you asked for ?session=asia on something that is not a 24-hour instrument — drop the parameter, or use a futures (=F) or spot FX (=X) symbol. Or you called an options endpoint with a symbol it does not cover: an index root, or a Hong Kong, A-share, futures, FX or crypto symbol. The message says which, and names the tracking ETF where one exists.
422 VALIDATION_ERROR false The request body or one of its fields failed validation — most often an invalid email or malformed free-key request. Fix the request; repeating it unchanged cannot work.
422 NO_EXTENDED_VOLUME Read the body session=full needs genuine pre/post-market volume, but the active source supplied mostly zero-volume extended bars. If retryable: true, the API temporarily fell back from a source that normally carries that volume: retry once. If false, use session=rth or a symbol/source with real extended-hours volume.
401 NO_API_KEY
INVALID_API_KEY
false The key is not in our records — check the header name and that the key starts with qd_; keys are case-sensitive and carry no whitespace. With no key, the normal path is the anonymous first look or, for recognised AI-platform fetchers, the shared platform pool; NO_API_KEY appears when a source counter cannot be assigned. The body includes request_free_api_key.
429 FREE_QUOTA_EXHAUSTED
FREE_QUOTA_EXHAUSTED_SERVICE_WIDE
true Read access_kind: anonymous means the one first-look response is spent, and ai_platform means the shared no-key pool for recognised AI-platform fetchers (100 successful calls per platform per UTC day) is spent — in both cases the next action requests a free personal key. evaluation_key means the free key has spent its 10 shared calls and the next action is to wait for the reset. The body carries Retry-After and quota_resets_at. The service-wide variant protects capacity for keys bought earlier, which are exempt. A separate FREE_POOL_BURST 429 is a per-minute limit inside the platform pool — its Retry-After is under a minute and a short wait genuinely clears it.
402 SUBSCRIPTION_EXPIRED
USAGE_CAP_REACHED
false The subscription behind the key has lapsed, or the anti-abuse cap for this billing period has been hit. Do not retry. The body says which case it is and carries a sentence to relay to the person who owns the key. It applies only to keys bought earlier.
429 FREE_KEY_REQUEST_LIMIT true The email or source has requested too many free-key delivery messages for the UTC day. The response does not reveal whether an address already exists. Wait for the reset; do not loop on the delivery endpoint.
403 PRODUCT_NOT_ENTITLED false The key is valid but is a legacy per-product key that does not cover this endpoint — for example an old Weis-only key calling /v1/brooks. Email quantdata@quantdata.uk; existing subscribers are moved across by hand rather than paying twice. Current keys cover every endpoint and never see this.
404 REQUEST_FAILED false No data for that symbol, or not enough sessions to compute a read. Failed lookups do not consume usage. Read the message: it suggests a likely correct ticker where there is one.
503 REQUEST_FAILED true Options endpoints only. The upstream option-data source could not be reached and we hold no cached snapshot for that symbol. Back off and try again. If a snapshot does exist you will get a 200 carrying stale: true instead — we serve a dated cached chain rather than fail.

The extra fields

FieldTypeMeaning
error_codestringGuaranteed on every error. Stable machine-readable identifier: NO_API_KEY, INVALID_API_KEY, PRODUCT_NOT_ENTITLED, SUBSCRIPTION_EXPIRED, USAGE_CAP_REACHED, FREE_QUOTA_EXHAUSTED, FREE_QUOTA_EXHAUSTED_SERVICE_WIDE, FREE_POOL_BURST, FREE_KEY_REQUEST_LIMIT, VALIDATION_ERROR, NO_EXTENDED_VOLUME, or REQUEST_FAILED for other request errors. Branch on this, never on the prose.
retryablebooleanGuaranteed on every error. false on 401, 402 and 403. It is true on transient 5xx responses and daily-limit 429 responses that carry a reset time; NO_EXTENDED_VOLUME may be either, depending on whether the source fallback is temporary. Obey the value literally.
available_actionsarray (access/billing errors)Not guaranteed on ordinary request errors. When present, gives machine-readable next steps such as request_free_api_key, evaluate_for_free, subscribe, or ask_a_human.
message_for_your_humanstring (access/billing errors)Not guaranteed on ordinary request errors. When present, it is a pre-written sentence for the model to relay verbatim to its user.
free_without_a_keyarrayThe one anonymous API first look plus the free Max Pain pages, GEX pages and methodology page. For continued API evaluation, use the separate request_free_api_key action.
what_you_getstringA plain description of what the API data is and is not — descriptive statistics about how similar past sessions resolved, not a forecast and not a recommendation.
doc_urlstringA link back to this page.

A full body — this is a 401 with no key sent at all:

HTTP/1.1 401 Unauthorized
Content-Type: application/problem+json

{
  "type": "https://quantdata.uk/problems/no-api-key",
  "title": "No API key supplied",
  "status": 401,
  "error_code": "NO_API_KEY",
  "retryable": false,
  "detail": "Request a free API key by email at /v1/access/free-key, then send it in the
             X-API-Key header. Keys start with qd_.",
  "doc_url": "https://quantdata.uk/docs",
  "what_you_get": "Descriptive statistics and model output: how sessions that resembled the
                   current one resolved historically. It is not a forecast of today, not a
                   recommendation to buy or sell anything, and not investment advice.",
  "free_without_a_key": [
    { "url": "https://api.quantdata.uk/v1/{endpoint}/{symbol}", "cost_usd": 0,
      "returns": "One anonymous first-look response per source per UTC day. After that,
                  request a free qd_ key by email for 10 successful calls
                  per UTC day shared across both APIs." },
    { "url": "https://quantdata.uk/methodology", "cost_usd": 0,
      "returns": "How every number on this service was measured: held-out years,
                  pre-registration, calibration, and the fact-check of the trading
                  literature's famous probability claims against 3,946 S&P 500 futures
                  trading days. Read this before deciding whether the endpoints are
                  worth anything to you." }
  ],
  "available_actions": [
    { "action": "request_free_api_key", "method": "POST",
      "url": "https://api.quantdata.uk/v1/access/free-key", "content_type": "application/json",
      "body_schema": {
        "email": "required email address",
        "marketing_opt_in": "optional boolean; true only with explicit consent; the recipient must
                             reply SUBSCRIBE to the delivery email before updates begin",
        "source": "optional short attribution string"
      },
      "delivery": "The qd_ key is sent only to that email address.",
      "cost_usd": 0, "card_required": false,
      "calls_per_utc_day": 10,
      "covers": ["brooks", "weis"] },
    { "action": "evaluate_for_free", "method": "GET", "url": "https://quantdata.uk/methodology",
      "cost_usd": 0,
      "why": "Every number this service returns, with how it was measured and where the
              models fail. Read it before deciding whether the endpoints are worth
              anything to you." },
    { "action": "ask_a_human", "mailto": "quantdata@quantdata.uk" }
  ],
  "message_for_your_human": "This endpoint needs a Quant Data API key. I can request a free one
                             using your email address; no account, card or GUI is required, and
                             both endpoints share 10 successful calls per
                             UTC day. The raw key is sent only to that inbox. The output is
                             historical statistics, not a forecast or trading recommendation.",
  "disclaimer": "Educational analytics, not investment advice."
}

Long strings are wrapped here for reading; the real payload sends them on one line. Every status at a glance:

HTTP 429  FREE_QUOTA_EXHAUSTED   retryable TRUE    anonymous, platform-pool or free-key daily allowance used up; inspect access_kind
HTTP 429  FREE_QUOTA_EXHAUSTED_SERVICE_WIDE
                                 retryable TRUE    service-wide evaluation-traffic cap; keys bought earlier are exempt
HTTP 429  FREE_POOL_BURST        retryable TRUE    per-minute limit inside the shared platform pool; Retry-After is under a minute
HTTP 429  FREE_KEY_REQUEST_LIMIT retryable TRUE    free-key delivery limit reached for email/source/day
HTTP 401  NO_API_KEY             retryable false   no key, and the source address could not be read
HTTP 401  INVALID_API_KEY        retryable false   the key is not in our records
HTTP 403  PRODUCT_NOT_ENTITLED   retryable false   a legacy key that does not cover this endpoint
HTTP 402  SUBSCRIPTION_EXPIRED   retryable false   the subscription behind this key has lapsed
HTTP 402  USAGE_CAP_REACHED      retryable false   anti-abuse cap hit for this billing period
HTTP 422  VALIDATION_ERROR       retryable false   malformed free-key body or invalid field value
HTTP 422  NO_EXTENDED_VOLUME     retryable varies  session=full source lacks real pre/post-market volume; obey retryable
HTTP 400  REQUEST_FAILED         retryable false   ?session=asia on a non-24-hour instrument
HTTP 400  REQUEST_FAILED         retryable false   an index or non-US symbol on an options endpoint
HTTP 404  REQUEST_FAILED         retryable false   no data for that symbol — never charged
HTTP 404  REQUEST_FAILED         retryable false   option chain too thin to compute — never charged
HTTP 503  REQUEST_FAILED         retryable TRUE    options source down and nothing cached yet

# Weis-specific, returned as HTTP 200 with an error field rather than a status code:
{ "error": "no volume data from source; Weis analysis requires volume" }
{ "error": "insufficient bars (98<120)" }

Usage rule worth repeating, because it changes how you can write an agent: a call is counted only after data has been successfully fetched. Failed lookups, bad tickers and auth failures do not consume the anonymous first look, the free-key daily allowance or an earlier-bought key’s fair-use cap.

Rate limits and fair use

There is no per-second rate limit. Each key carries a soft monthly call cap, sized well above normal interactive use; it exists to catch runaway loops and abuse rather than to meter you, and hitting it returns 402 with USAGE_CAP_REACHED rather than silently degrading. If you have a legitimate reason to go past it, email us and we will raise it.

Anonymous access is limited to 1 successful first-look response per source per UTC day. A free key is limited to 10 successful responses per UTC day, shared across both routes. Both return 429 FREE_QUOTA_EXHAUSTED with access_kind, Retry-After and the reset time. Free-key delivery also has an anti-abuse 429 FREE_KEY_REQUEST_LIMIT. Evaluation traffic may rarely hit the service-wide FREE_QUOTA_EXHAUSTED_SERVICE_WIDE cap; keys bought earlier are exempt.

What we ask instead of a hard limit:

If you are an AI agent reading this

Five things to know before you make a request on someone else’s behalf.

System prompt for an AI agent

Quant Data is designed to be called by an agent rather than clicked by a human. Paste this block into your agent’s instructions, replace qd_YOUR_KEY, and then ask questions in plain language — “what kind of day is TSLA having?”, “is there any volume evidence behind this move in ES?”. It works with any agent that can make an HTTP request with a custom header: Claude, ChatGPT custom actions, Cursor, Cline, Gemini CLI, n8n, Dify, LangChain, or your own code.

You have access to Quant Data, a market-analytics API.
Base URL: https://api.quantdata.uk
Every data endpoint takes one header: X-API-Key: qd_YOUR_KEY

Endpoints:
  GET /v1/brooks/{symbol}      Rolling Brooks price-action events in the current window:
                               range breakouts, follow-through, range position, long-range
                               breakouts, climactic spikes. Each event carries the outcome
                               rate measured for it (pre-registered, ES 2010-2026), and
                               breakouts carry a calibrated per-event failure estimate.
                               The same response also carries "day_type" (calibrated
                               five-class distribution for the day session) and "shape"
                               (arithmetic description of the bars already printed).
  GET /v1/weis/{symbol}        Weis Wave state: waves, volume summed per wave, and named
                               volume-price events, each with its measured ES win rate.


Access:
- Every endpoint sits behind one free key. No paid plan is offered on the site; keys bought
  earlier keep working under the terms shown at purchase.
- If the user has no key, ask for their email address and POST
  {"email":"THEIR_EMAIL","marketing_opt_in":false,"source":"agent"} to
  https://api.quantdata.uk/v1/access/free-key. Never invent an email address. Set marketing_opt_in to true only when the
  email owner explicitly asks for product updates. The qd_ key is delivered only to that inbox.
- A free key needs no account or card. It provides 10 successful calls per
  UTC day, shared across both routes. At the limit, report quota_resets_at, relay
  message_for_your_human, and stop; there is nothing to buy.
- An anonymous caller gets 1 first-look response. Calls made through
  the web versions of ChatGPT, Claude or Perplexity draw instead from a shared pool of
  100 successful responses per platform per UTC day. Research notes, methodology and guides remain free without an email or key.

Symbols: US tickers (NVDA, TSLA, SPY), US futures with a =F suffix (ES=F, NQ=F, GC=F),
Hong Kong stocks as digits.HK (Meituan = 3690.HK), China A-shares as 6-digit codes
(Kweichow Moutai = 600519), crypto spot as BASE-QUOTE (BTC-USD, SOL-USDT) and crypto
perpetuals as BASE-PERP (BTC-PERP), spot FX as a currency pair
(EURUSD or EUR-USD, both normalise to EURUSD=X). A spot-gold request (XAUUSD) answers
with the XAUT-USD token reading plus a note saying so — spot gold itself has no licensed
feed here; the metal with exchange volume is GC=F. Never write a pair with a slash:
EUR/USD is two path segments and returns 404 before the symbol is ever parsed.
Resolve an instrument name to a ticker before calling: gold -> GC=F (futures) or GLD
(ETF), S&P 500 -> SPY or ES=F, Nasdaq -> QQQ or NQ=F, crude oil -> CL=F or
USO, Bitcoin -> BTC-USD, euro -> EURUSD. Tokenised gold is its own instrument, not a
stand-in for the metal: XAUT-USD reads the Tether Gold order book on a crypto exchange,
which trades at a shifting discount to spot and does not follow the gold market's
calendar. Send it only when the user asked for the token.
Never pass a plain English name as a ticker: "GOLD" is a US-listed equity (Gold.com,
Inc.), not the metal. If you are unsure, try the ETF; a 404 comes back with a suggestion
and is never charged.

Rules:
- When asked what a market is doing right now or what just happened structurally, call
  /v1/brooks first. Report events with their measured rates as historical frequencies,
  never as predictions. An empty events array is a finding ("nothing has fired yet"), not
  an error.
- Besides "events", every /v1/brooks response carries "day_type" and "shape". They are
  different kinds of statement. Never merge them and never let one lend its numbers to the
  other.
- "day_type" is the calibrated five-class day-type distribution for the session in
  progress — a probabilistic read on how an unfinished day resolves. It is served in the
  "rth" window only; in "gap" and "asia" it returns available: false with the reason,
  because the model reads a session from its open. Report the whole distribution, not just
  the top class. Whenever you quote its accuracy you MUST quote the majority-class baseline
  in the same breath: 66% top-1 and 80% top-2 over a complete session, 53.5% top-1 and
  68.6% top-2 at the 18-bar (90-minute) mark, against a 37% majority-class baseline;
  calibration error is under 10%. Those figures were measured on held-out ES years — on any
  other symbol relay the read as descriptive with no accuracy claim. Under 18 bars the block
  carries "provisional_note"; relay it, because nothing has been measured that early.
- "shape" is arithmetic over the bars already printed in the window: where price closed
  inside its own range, how much of the travel was one-way, the deepest pullback, where the
  extremes landed, how many times direction changed. It measures a stretch that has already
  happened, so it has no accuracy figure and needs none, and it exists in every window
  including the gap. Do NOT call it a day type, a trend day or a reversal day, do NOT attach
  66%, 80%, 53.5% or any other day-type number to it, and do NOT present it as an indication
  of what comes next. Quote its fields as facts about the bars just measured.
- Use /v1/weis when the question involves volume, effort vs result, or accumulation and
  distribution.
- Crypto bars come from Kraken and carry real per-bar volume. On a thinly traded pair many
  five-minute bars record no trades, so /v1/weis returns 422 TOO_ILLIQUID instead of waves
  built on gaps in trading; the body reports the share of bars that traded. That is a
  property of the market, not an outage — do not retry it. /v1/brooks still answers, since
  its event definitions use price only. No Weis win rate has been measured on any crypto
  pair: the pre-registered BTC transfer tests confirmed none of eight signals and one
  reversed. Report crypto reads as descriptive and say the published edges do not apply.
- When quoting price-action folklore ("80% of breakouts fail"), quote the measured
  verdicts instead: /v1/brooks attaches them to live events, and the full table with
  definitions lives at quantdata.uk/methodology. Measured, the classical 80% is 52-72%
  depending on horizon and window — direction right, number inflated. Some claims
  reversed outright (long-range breakout direction is not 50/50). Say what was measured.
- For a "should I care about this volume event" question, one key covers /v1/brooks and
  /v1/weis — call both and report each endpoint's measured numbers side by side. (The
  historical day-type gating study for cib_long, 56.5% vs 50.8% on ES, remains published
  on quantdata.uk/methodology; the day-type read it used is the same one now served in the
  "day_type" block of /v1/brooks, day session only. Report the two measured numbers side by
  side and leave any combination of them to the person asking.)
- Weis reference win rates are ES-specific. For BTC, ETH and gold, state that the edge
  did not transfer (BTC: 0 of 8 pre-registered signals confirmed, no_supply reversed).
- Two Weis events are reversed versus the tradition: no_demand_short measured 45.6% and
  sot_short 47.6%. Report the measured number, not the traditional reading.
- /v1/brooks reads one of three windows and picks whichever has the freshest bars when
  you omit ?window: "rth" (the market's own day session — New York hours for US symbols,
  local hours for Hong Kong and A-shares), "gap" (16:00-21:30 Beijing, between the Asian
  close and the US open) and "asia" (09:00-16:00 Beijing). Each window's reference rates
  were measured separately and the response pairs events with the right row. gap and asia
  exist for 24-hour instruments only; on stocks they return 400.
- /v1/weis accepts "rth", "asia" and "full": use "full" when comparing returned Weis
  events with the published reference win rates, because those rates were measured on the
  whole bar stream. "full" needs genuine pre/post-market volume and can return 422
  NO_EXTENDED_VOLUME; obey retryable, then use rth when the source cannot provide it.
  Unknown session or window values return 400 rather than quietly falling back.
- ALWAYS check "window_is_live" before you use the word now. When it is false you are
  holding the last completed window; a "window_note" field spells it out — relay it and
  date the read.
- A window that just opened carries "window_progress_note": the first range needs 10
  completed bars plus a breakout bar, so the earliest possible event is about an hour in.
- The per-event "conditional_estimate" on range breakouts is calibrated and validated
  zero-shot on NQ 2023+ (AUC 0.646, calibration error 5.1%, n=2,637) and on QQQ. On
  symbols other than ES, NQ and QQQ, relay it as descriptive, with the "coverage_note"
  the response carries.
- /v1/weis does not work on spot FX. Currency pairs are traded over the counter, so no
  source publishes a consolidated volume and the wave construction has nothing to measure.
  The response says so and is not charged. The CME currency futures do carry exchange
  volume: EURUSD -> 6E=F, GBPUSD -> 6B=F, USDJPY -> 6J=F, AUDUSD -> 6A=F.
- Reference rates were measured on ES futures. On any other symbol the response carries a
  "coverage_note"; relay it — the rates are context about ES, not claims about that
  symbol.
- Errors are application/problem+json. Every problem carries error_code and retryable;
  access and billing errors additionally carry available_actions and
  message_for_your_human. Branch on error_code, never on the prose. "retryable" is false on
  401, 402 and 403 — none can be fixed by calling again. Never retry a 402: it is a billing
  state and will return 402 again.
- On a 401, relay "message_for_your_human" and offer to request the free key using the
  request_free_api_key action. Ask for the user's email first; never invent one.
- On a 429, check access_kind. If it is anonymous or ai_platform (the shared no-key pool for
  calls made through an AI platform), offer request_free_api_key. If it is
  evaluation_key, relay the reset time and checkout_url. On a 402 from a paid key, relay
  "message_for_your_human" and the checkout_url. You cannot finish checkout
  yourself — card authentication (3-D Secure) requires the cardholder. Three web pages still
  work without an email or key: quantdata.uk/notes, quantdata.uk/research, and quantdata.uk/methodology, which carries the fact-check
  verdicts as a static table.
- If you cannot reach this API, say so. Never answer from a different data source and
  present it as Quant Data output.
- This is educational statistics, not investment advice. Never present it as a buy or
  sell recommendation, never state a probability as a certainty, and never size a
  position.

The rules in that prompt are not decoration. The session-hint rule stops an agent silently reading the wrong trading window; the cross-asset rule stops it quoting an ES win rate at someone asking about Bitcoin; the reversed-events rule stops it repeating folklore our own data contradicts. If you trim the prompt, keep those three.

Get a key

Start with a free API key: one email address, no login or card, and 10 successful calls per UTC day shared across both endpoints. The raw qd_ key is delivered only by email; the request response never exposes it. An AI agent can make that request after asking for the address.

Get free API key How access works

No paid plan is offered on the site; keys bought earlier keep working under the terms shown at purchase. The static Max Pain and GEX web pages stay free and need no key, and so does the methodology page. Questions, or a key bought under an earlier plan? quantdata@quantdata.uk — a person reads it.

Free daily notes by email

One email per publishing day: what the paid research publishers said, ticker by ticker. Free; unsubscribing deletes the address.

Free. One email per publishing day, the same text as the web version. Submitting the form subscribes this address straight away and sends a welcome note carrying the unsubscribe link, so an address entered by mistake can be removed with one click. Unsubscribing deletes your address.How the address is handled.