API reference

Four market-analysis endpoints, one header, no SDK. Start with a free key by email, then use the same key for paid access when you need the daily limit removed.

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.

For a useful trial, 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":"you@example.com","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": "delivery_started",
  "delivery": "email",
  "message": "Check that inbox for your qd_ API key.",
  "free_key": {
    "price_usd": 0,
    "card_required": false,
    "account_required": false,
    "calls_per_utc_day": 10,
    "shared_across": ["brooks", "weis", "maxpain", "gamma"],
    "auth": { "header": "X-API-Key", "prefix": "qd_" }
  },
  "marketing_updates": "not_subscribed"
}

The raw key is sent only to that inbox. 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 checkout for a person who needs uninterrupted access. A source address is hashed rather than stored for the anonymous counter, as the privacy page spells out. The same four endpoints are also a remote MCP server — per-client connect instructions on the MCP page.

Or connect over MCP

The same four 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 four 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 issues a free key for an email address without a GUI. Unlike the HTTP endpoint, it returns the key in the tool result so the assistant can keep working in the same turn, and emails a copy to that address; the email states that an assistant requested it and how to have it disabled. Keys issued this way expire after 30 days and are never a key that already belonged to that address. Send X-API-Key as a request header for the shared 10-response UTC-day allowance or paid access. The tool descriptions carry the same coverage limits and caveats as this page.

What costs money, and what does not

Access What it covers Price
Free web pages — no key, no signup The Max Pain and GEX pages at /max-pain/{ticker} and /gex/{ticker}, the methodology page and the guides. Web pages, not API endpoints — nothing to call, nothing to authenticate. $0
Free API key — email delivery, no login or card All four endpoints, sharing 10 successful responses per UTC day. Requested from the HTTP endpoint, the raw key is delivered only to the submitted inbox. Requested through the MCP tool, it is also returned to the assistant so it can continue without a copy-paste step, and expires after 30 days. $0
Brooks-only paid API /v1/brooks/{symbol} and /v1/brooks/backtest. No trial; personalized AI-workspace prompt delivered after checkout. $9.90/mo
Full paid API /v1/brooks/{symbol}, /v1/weis/{symbol}, /v1/maxpain/{symbol}, /v1/gamma/{symbol} $149/mo

Without a key, one source gets 1 first-look response per UTC day. The free key is the intended evaluation path after that.

Brooks-only is $9.90 a month with no trial. Full access is $149 a month, every endpoint, with the first 3 days free. In either case, use the same email as the free key and that key is upgraded in place; otherwise a new qd_ key is shown after checkout. The research behind the endpoints is written up on the Brooks Daily Bias and Weis Wave pages.

Keys bought under the older two-product pricing keep working at their original price. If yours covers one product and you now need the other, email quantdata@quantdata.uk — we move existing subscribers across by hand rather than charge them twice. That legacy case is the only thing that returns 403 rather than 401, so you can tell “wrong key” apart from “key does not cover this endpoint” without guessing.

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"

# Max pain for every expiration inside 45 days
curl -H "X-API-Key: qd_9f3c..." \
     https://api.quantdata.uk/v1/maxpain/AAPL

# Dealer gamma positioning, with per-strike detail
curl -H "X-API-Key: qd_9f3c..." \
     "https://api.quantdata.uk/v1/gamma/SPY?by_strike=true"

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. Part of the paid plan; 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. Part of the paid plan; 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.

GET /v1/maxpain/{symbol}

The max pain strike for every expiration inside 45 days, computed from the latest open-interest settlement available from the upstream source. Part of the paid plan; use a key for ongoing calls. What is max pain? works the calculation through by hand.

This endpoint is arithmetic, not a model. It takes open interest, tries every listed strike, and returns the one that minimises the total payout to option buyers if the underlying settled there. There is no volatility input, no pricing assumption and no fitted parameter, which means anyone holding the same option chain computes the same number. That is worth stating because the next endpoint on this page is not like that.

ParameterInTypeDefaultNotes
symbol path string required US listed stock or ETF with options. Not normalised the way the other endpoints are — see the coverage note below.
distribution query boolean false Adds oi_by_strike to each expiration: call and put open interest per strike, within 15% of that expiration’s max pain. Roughly an order of magnitude more data, so it is off by default.
{
  "symbol": "AAPL",
  "as_of": "2026-07-23",
  "spot": 333.02,
  "spot_date": "2026-07-24",
  "contracts": 1536,
  "max_pain": [
    { "expiration": "2026-07-24", "dte": 1,
      "max_pain": 322.5, "spot_vs_max_pain_pct": 3.26,
      "call_oi": 161348, "put_oi": 113197, "put_call_ratio": 0.702,
      "highest_call_oi_strike": 330.0, "highest_put_oi_strike": 320.0 },
    { "expiration": "2026-07-27", "dte": 4,
      "max_pain": 325.0, "spot_vs_max_pain_pct": 2.47,
      "call_oi": 17290, "put_oi": 15748, "put_call_ratio": 0.911,
      "highest_call_oi_strike": 340.0, "highest_put_oi_strike": 320.0 }
  ],
  "method": "For each expiration, the strike that minimises the total payout to option
             buyers if the underlying settled there. Computed from open interest alone:
             no pricing model, no volatility assumption, no fitted parameter.",
  "note": "Open interest from the latest settlement available upstream. Check as_of rather
           than assuming it is yesterday; dte is counted from that measured settlement date.
           highest_call_oi_strike and highest_put_oi_strike are simply the strikes carrying
           the most open interest; we have not measured whether price respects them, and
           they are not support or resistance levels. Descriptive output only: no forecast
           and no directional claim is made.",
  "disclaimer": "Educational analytics, not investment advice."
}

Two of the ten expirations returned by that call are shown. Every expiration inside 45 days comes back, and you should always quote one alongside its dte — max pain is computed per expiration, and the near-dated and far-dated figures routinely disagree.

FieldTypeMeaning
as_ofstringThe latest upstream settlement date used for the open-interest calculation. This is the date that matters; check it rather than assuming the source has already published the immediately previous trading day.
spotfloatLast daily close used for the distance calculations, with spot_date giving its date. It can be one day newer than as_of.
contractsintegerDistinct contracts in the chain after de-duplication.
max_pain[].dteintegerCalendar days from as_of to that expiration. Never negative — expired contracts inside the fetch window are dropped.
max_pain[].max_painfloatThe strike itself.
max_pain[].spot_vs_max_pain_pctfloatHow far spot sits above (positive) or below (negative) that strike, as a percentage.
max_pain[].call_oi / put_oiintegerTotal open interest on each side for that expiration, with put_call_ratio as puts divided by calls.
max_pain[].highest_call_oi_strikefloatThe strike carrying the most call open interest. A description of where contracts sit, nothing more — see the warning below.
max_pain[].highest_put_oi_strikefloatSame for puts.
max_pain[].oi_by_strikearray (optional)Present only with ?distribution=true.
methodstringHow the figure was computed, in one sentence. Stable across calls; safe to show a user.
staleboolean (optional)Present and true only when the upstream option-data source could not be reached and this is the last snapshot we hold. Check as_of before using it. We would rather serve a dated cached chain than a 503.
disclaimerstringAlways present. Surface it; do not strip it.

These are not support and resistance levels, and we have not measured whether price respects them. highest_call_oi_strike and highest_put_oi_strike tell you where contracts are concentrated, which is a fact about positioning, not a fact about price. A put wall sits below spot on almost every ticker on almost every day, because puts are bought below spot as protection — so its existence carries close to no information on its own. Report where the strikes are; leave what to make of them to the person asking.

Coverage, and how this differs from the free pages. The Max Pain pages cover 25 stocks and 18 ETFs, as HTML, rebuilt nightly, free and with no key — and they stay that way. This endpoint exists for the broader set of liquid US stocks and ETFs, for JSON rather than a web page, and for an on-demand read from the latest settlement available upstream rather than the site’s last nightly build. On demand does not mean intraday: open interest still updates only after settlement. If the ticker you want is already on a free page and you only need to read it, use the page.

GET /v1/gamma/{symbol}

Estimated dealer gamma exposure: net and gross GEX, the zero gamma (flip) level, and the strikes carrying the most gamma. Part of the paid plan; use a key for ongoing calls. What is gamma exposure? explains the hedging mechanism from scratch, including what zero gamma means and where the estimate is known to be wrong.

Unlike /v1/maxpain, this is an estimate. It solves implied volatility from each contract’s end-of-day quote using Black-Scholes with a zero rate and no dividends, and it assumes dealers are long every call and short every put. That assumption is a public convention, not a measurement — open interest never reveals who holds which side. Two providers can publish different flip levels for the same ticker on the same day and both be applying their method correctly. Quote the sign and the rough magnitude; do not quote the decimals as if they were observed.

ParameterInTypeDefaultNotes
symbol path string required Same coverage rule as /v1/maxpain: US listed stocks and ETFs.
by_strike query boolean false Adds gamma.by_strike: net GEX at every strike within 15% of spot, sorted by strike. This is what a gamma profile chart is drawn from. Off by default because it is large.
{
  "symbol": "SPY",
  "as_of": "2026-07-24",
  "spot": 738.93,
  "spot_date": "2026-07-24",
  "gamma": {
    "net": -10365541161,
    "gross": 25880523413,
    "ratio": -0.401,
    "regime": "negative",
    "flip": 752.32,
    "flip_vs_spot_pct": 1.81,
    "flip_status": "resolved",
    "contracts_used": 3072,
    "heaviest_strikes": [
      { "strike": 740.0, "gex": -1504707244, "share": 0.058 },
      { "strike": 730.0, "gex": -1063055315, "share": 0.041 },
      { "strike": 720.0, "gex": -1054508436, "share": 0.041 }
    ],
    "by_expiration": [
      { "expiration": "2026-07-27", "net": -250129641, "abs": 2597715124 },
      { "expiration": "2026-07-28", "net": -779956392, "abs": 1623353292 },
      { "expiration": "2026-07-29", "net": -199724630, "abs": 1326588006 }
    ]
  },
  "method": "Black-Scholes with zero rate and zero dividend, implied volatility solved from
             the bid/ask mid of each contract's end-of-day quote, then gamma x open interest
             x 100 x spot^2 x 1%, calls positive and puts negative. The flip level is the
             zero crossing nearest spot on a 0.9x-1.1x grid. This is an estimate that assumes
             dealers are long every call and short every put — a convention, not a
             measurement.",
  "note": "An estimate, not observed data — see method. It uses the latest settlement available
           upstream and cannot update intraday whatever the timestamp suggests. Positive net gamma is
           associated with hedging that leans against price, negative with hedging that leans
           with it; that is a description of a mechanism, not a forecast and not a directional
           recommendation. We have not published a measured hit rate for these levels.",
  "disclaimer": "Educational analytics, not investment advice."
}

Three of the fifteen expirations in by_expiration are shown, and the heaviest strikes list is truncated to three of five.

FieldTypeMeaning
gamma.netintegerDollars of stock the estimate says dealers must trade per 1% move, calls positive and puts negative. The sign is the headline: positive means hedging leans against price, negative means it leans with it.
gamma.grossintegerThe same sum ignoring sign — how much hedging is going on in total. A small net inside a large gross means the two sides nearly cancel and a modest shift could flip the sign.
gamma.ratiofloatnet / gross, so a scale-free measure of how one-sided the book is. Near zero means finely balanced; near ±1 means lopsided.
gamma.regimestringpositive or negative, derived from the sign of net. Provided so you do not have to re-derive it.
gamma.flipfloat | nullThe zero gamma level: the price at which net gamma would cross zero, taken as the crossing nearest spot on a grid from 0.9× to 1.1× spot. null is a valid answer — read flip_status.
gamma.flip_vs_spot_pctfloat | nullDistance from spot to the flip as a percentage. This is usually more useful than the level itself: a wide gap means the current regime has some staying power, a narrow one means an ordinary session could invert it.
gamma.flip_statusstringresolved, or no_sign_change_within_10pct. The second is a state, not a failure — gamma keeps one sign right across the traded strikes, so the whole range reads as a single regime and there is no crossing to quote. If the chain were simply too thin, you would have received a 404 instead.
gamma.contracts_usedintegerContracts that had a usable quote and a solvable implied volatility. Deep in-the-money and badly quoted contracts are skipped, so this is lower than the chain size.
gamma.heaviest_strikesarrayThe five strikes with the largest absolute GEX: strike, signed gex, and share of gross. Concentrations of estimated hedging, not levels with a measured hit rate.
gamma.by_expirationarrayNet and absolute GEX per expiration, oldest first. Gamma is not spread evenly across expirations and the split differs by symbol, so read this array rather than assuming the front expiration dominates — a large expiry rolling off can reset the picture.
gamma.by_strikearray (optional)Present only with ?by_strike=true.
methodstringThe full calculation and its assumptions, in one string. Show it to anyone who asks where the number came from.
staleboolean (optional)Same meaning as on /v1/maxpain: upstream was unreachable and this is the last snapshot held.
disclaimerstringAlways present.

Both options endpoints use the latest settlement available from the upstream source. Check as_of: it may not be the immediately previous trading day, and neither endpoint can update intraday. That limitation bites hardest close to expiry, which is exactly when the same-day flow you cannot see is largest. Say so when you quote either endpoint.

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.

The two options endpoints are narrower than the table above. /v1/maxpain and /v1/gamma take US listed stocks and ETFs only; Hong Kong, A-share, futures and crypto symbols have no option chain here and return 400. Cash-settled index options — SPX, SPXW, NDX, RUT, VIX — also return 400, for a different reason worth being straight about: the chain is available to us, but our price source carries no index level, and every figure on both endpoints needs the underlying price. Rather than manufacture one from a tracking ETF we decline the request. Use the ETF itself: SPY for the S&P 500, QQQ for the Nasdaq 100, IWM for the Russell 2000.

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 paywall. 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 checkout 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 checkout. The body carries Retry-After and quota_resets_at. The service-wide variant protects paid capacity; paid keys 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, carries the checkout link, and carries a sentence to relay to the person who owns the key.
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 or use the checkout action; 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. On the options endpoints it also covers a chain too thin to produce a stable figure, and — on /v1/gamma only — too few contracts with a usable quote to estimate gamma at all. Failed lookups do not consume usage. Read the message: it suggests a likely correct ticker where there is one. A thin chain on /v1/gamma is worth one retry against /v1/maxpain, which needs no quotes and often still works.
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 all four APIs." },
    { "url": "https://quantdata.uk/max-pain/{ticker}", "cost_usd": 0,
      "returns": "Max pain, put/call ratio, call and put walls and open interest by
                  expiration — one page per covered stock or ETF (25 stocks and 18 ETFs), rebuilt nightly from
                  end-of-day option open interest. Lowercase tickers. Static pages, no key,
                  no account." },
    { "url": "https://quantdata.uk/gex/{ticker}", "cost_usd": 0,
      "returns": "Estimated dealer gamma exposure for the covered stocks and ETFs: net and gross GEX,
                  the zero gamma (flip) level and the heaviest strikes, rebuilt nightly.
                  Same static-page rules: no key, no account." },
    { "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 paid 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", "maxpain", "gamma"] },
    { "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 paid endpoints are worth
              anything to you." },
    { "action": "subscribe", "checkout_url": "https://buy.stripe.com/...",
      "price_usd_per_month": 149, "includes": "every endpoint",
      "free_trial_days": 3,
      "note": "A card is collected up front and the first 3 days are free. A person
               has to complete the checkout — card authentication (3-D Secure) requires the
               cardholder and cannot be done unattended by an agent. The key is shown on the
               page immediately after checkout. If checkout uses the same email as a free
               key, that key is upgraded in place." },
    { "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
                             all four endpoints share 10 successful calls per
                             UTC day. The raw key is sent only to that inbox. If you later need
                             uninterrupted access, the full service is $149/month with
                             the first 3 days free after adding a card. You would
                             need to complete that checkout yourself. 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; paid keys 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, free-key daily allowance or a paid account’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 all four 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; paid keys 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.
  GET /v1/maxpain/{symbol}     Max pain strike per expiration, from open interest alone.
                               Pure arithmetic: no pricing model, no volatility assumption.
  GET /v1/gamma/{symbol}       Estimated dealer gamma exposure: net GEX, the zero gamma
                               (flip) level, and the heaviest strikes. A Black-Scholes
                               ESTIMATE, not observed data.

Commercial access:
- Brooks-only access is $9.90/month with no trial. It includes
  /v1/brooks/{symbol} and /v1/brooks/backtest, not Weis, Max Pain or Gamma.
- Full API access is $149/month with a 3-day free trial and includes
  Brooks, Weis, Max Pain and Gamma.
- 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 all four routes. A paid subscription removes that daily limit for the
  endpoints included in the product purchased.
- 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. The static Max
  Pain/GEX web pages, 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.)
- /v1/maxpain and /v1/gamma cover US listed stocks and ETFs only. Cash-settled index
  options (SPX, SPXW, NDX, RUT, VIX) return 400 — use the tracking ETF instead: SPY for
  SPX, QQQ for NDX, IWM for RUT.
- Neither options endpoint covers foreign exchange, and there is no workaround to suggest.
  Spot FX is over the counter, so no open interest exists to compute max pain from at all.
  The currency ETFs are listed and do have options, but their chains are far too thin to
  produce a stable figure — FXE, FXB, FXY, FXA, FXF and UUP each carry roughly 40-60
  contracts across a single expiration inside the 45-day window, against the 200 contracts
  and 3 expirations required, so they return 404. Do not offer a currency ETF as a
  substitute for a pair; say the endpoints do not cover FX.
- Both options endpoints read open interest as of the PRIOR SETTLEMENT. They describe
  yesterday's positioning and cannot update intraday. Say so when you quote them; do not
  present the figure as live.
- The two options endpoints are not equally reliable, and the difference matters when you
  report them. Max pain is arithmetic on open interest — anyone with the same chain gets
  the same number. Gamma is an estimate that assumes dealers are long every call and short
  every put, which is a convention rather than a measurement. Quote max pain as a figure;
  quote gamma as an estimate.
- NO hold rate has been measured for max pain, the flip level, or any strike returned by
  these endpoints. Never call them support, resistance, entry points or targets. Report
  where they are and how they were computed, and leave the interpretation to the person.
- On /v1/gamma, flip: null is not an error. Check flip_status: "no_sign_change_within_10pct"
  means gamma keeps one sign right across the chain, so the whole traded range reads as one
  regime. That is a state worth reporting, not a failure.
- On /v1/maxpain, always cite the expiration and its dte alongside the strike. Max pain is
  computed per expiration and the near-dated and far-dated figures routinely disagree; a
  max pain quoted without its expiry is meaningless.
- 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/max-pain/{ticker},
  quantdata.uk/gex/{ticker}, 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 all four 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 Buy full API access See pricing

Full access is $149 a month. A card starts the first 3 days free; use the same email at checkout and the free key is upgraded in place. Card authentication requires the person paying, so an agent hands off the checkout link. Cancel any time in the Stripe customer portal. 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 the older two-product pricing? quantdata@quantdata.uk — a person reads it.