1. API Specification

API Specification

The full specification for the live FlashAlpha API: every endpoint, parameter, response field, plan tier and error. It is the same text as api.md: read it here, copy it into your LLM, or download the file for code generators and tests.

Real-time options exposure analytics. Live gamma (GEX), delta (DEX), vanna (VEX), and charm (CHEX) exposure data, key levels, dealer hedging estimates, and verbal narrative analysis. Exposure metrics are structural: computed from settled open interest with a fixed sign convention. Classified options flow is a separate, opt-in layer - the /v1/flow/* endpoints and ?polarity=flow - not the basis of the default figures.

How the analytics are defined: the glossary defines every metric name and its formula, and the dealer-positioning methodology covers the sign conventions and the two polarity models - including why the structural and flow views of the same chain can disagree. Index and futures pricing specifics are in the futures methodology.


Playground

The trader-context reference documents expiry impact, exact-strike attribution, daily skew/IV-RV/term history and matured expected-move outcomes, plus the additive earnings and income diagnostics. Growth receives live positioning and latest stored context; normalized daily ranges require Alpha. Supported snapshot replay and daily range reconstruction have different contracts and availability semantics.

Try the API right now - no setup required.

Open the interactive playground to browse endpoints, send requests, and inspect live responses directly in your browser.

Enter your API key in the Authorize button at the top, then expand any endpoint and click Try it out.


Quick Start

1. Get your API key from flashalpha.com

2. Make your first call:

curl -H "X-Api-Key: YOUR_API_KEY" https://lab.flashalpha.com/v1/exposure/gex/SPY

3. Explore the response - you'll get gamma exposure by strike with OI, volume, and day-over-day changes.

That's it. Every US equity and ETF symbol is supported. Data is fetched on-demand. Paid plans serve live data. A Free API key gets delayed data. Access, fields and quotas remain tiered, and upstream data age still applies (see plan tiers).


Authentication

All endpoints (except /health, /v1/surface, and unauthenticated /v1/stock/{symbol}/summary) require an API key. Pass it in the X-Api-Key header:

curl -H "X-Api-Key: YOUR_API_KEY" https://lab.flashalpha.com/v1/symbols

Or as a query parameter:

https://lab.flashalpha.com/v1/symbols?apiKey=YOUR_API_KEY

Endpoints

Market Data

Exposure Analytics

Volatility (additional)

Macro

Universe

Flow Analytics

Simulation-aware exposure that reflects intraday position changes (not just morning-broadcast settled OI). Pure additive layer - every /v1/exposure/* endpoint is unaffected. See Flow vs Exposure for the difference.

Trade Flow Data

Alpha-only trade-flow proxy endpoints. These return pass-through JSON from the ingest service and use camelCase field names.

Pricing & Sizing

Volatility Analytics

VRP Analytics

  • GET /v1/vrp/{symbol} - Volatility Risk Premium dashboard (VRP spreads, z-score, percentile, directional VRP, term structure, GEX-conditioned regime, strategy scores, dealer risk, warnings, macro context) Alpha+
  • GET /v1/vrp/{symbol}/history - Daily VRP time series for charting and backtesting Alpha+ (series=normalized is release pending)

Strategy Signals

Decision-support endpoints that score one trading idea and return a unified strategy decision envelope.

Earnings

Structures

Pure-math multi-leg utilities - no market lookup.

Screener

Reference Data

Account & System


Response Envelope

Every successful JSON response carries two fields, added automatically by the API rather than by each endpoint:

Field Meaning
endpoint_version The deployed API version that produced the response. One value across the whole surface.
data_as_of Upstream feed delivery timestamps observed by the responding data store.

data_as_of has a fixed set of keys. Live quote and chain requests report feed deliveries observed during that request; feeds not read or lacking usable timestamps are null. Background-backed routes report the background feed clock. Chain requests do not advance that background clock. The timestamps are feed heartbeats, not identifiers for an atomic market snapshot.

Live quote and chain responses currently report oi_feed and macro_feed as null. The on-demand OI response carries no usable OI vintage, and the macro clock is maintained by a separate background refresh. This does not mean OI or macro values are absent. Those two timestamp slots are not copied from background activity, since they would not establish the vintage of the inputs used by this request. Public snapshot responses can also have null slots.

The example below shows a background-backed response. Its OI and macro timestamps are not values to expect on live chain responses.

{
  "symbol": "SPY",
  "underlying_price": 764.23,
  "as_of": "2026-08-24T18:49:00.697Z",
  "endpoint_version": "2026.08.25",
  "data_as_of": {
    "node": "fa2",
    "equity_feed": "2026-08-24T18:49:00.512Z",
    "equity_options_feed": "2026-08-24T18:48:58.204Z",
    "index_feed": null,
    "index_options_feed": null,
    "futures_feed": null,
    "futures_options_feed": null,
    "flow_feed": "2026-08-24T18:49:00.220Z",
    "oi_feed": "2026-08-21T20:00:00.000Z",
    "macro_feed": "2026-08-24T18:42:11.000Z"
  }
}

This background-clock example illustrates each feed's cadence relative to as_of. It was produced midday on Monday 2026-08-24, and oi_feed reports Friday's 16:00 ET close - the previous session, which is the newest settled open interest that exists. It is three days behind as_of and entirely correct; settled OI is published once per session, and Monday's will not exist until Monday settles.

Key Feed Expected cadence
node Which node answered. -
equity_feed Equity and ETF spot quotes seconds, during market hours
equity_options_feed Equity and ETF option quotes seconds, during market hours
index_feed Index spot (SPX, NDX, RUT, VIX, ...) seconds, during market hours
index_options_feed Index option quotes seconds, during market hours
futures_feed Futures prices seconds, during the futures session
futures_options_feed Futures option quotes seconds, during the futures session
flow_feed Classified options and stock trade tape seconds, during market hours
oi_feed Settled open interest daily, dated to the prior 16:00 ET close
macro_feed VIX, VVIX, SKEW, MOVE, SPX and Fear and Greed minutes. Reports its OLDEST field, so one lagging series is visible rather than hidden behind a fresh one.

How to read it

  • Check the feeds your query depends on. A GEX call on SPY is answered from equity_feed, equity_options_feed and oi_feed. futures_feed being null in that same response says nothing about the answer.
  • Compare against the cadence, not against the clock. oi_feed at yesterday's close is correct - open interest is settled data. equity_options_feed an hour behind during market hours is not.
  • null means no usable timestamp was observed. A chain request need not read every feed; other routes can report null for feeds their background store has not observed.
  • data_as_of is not as_of. as_of, where an endpoint returns it, is when the response was generated or when the newest contract in it last ticked. data_as_of describes the feeds.

A call without an API key to GET /v1/stock/{symbol}/summary returns a delayed snapshot and adds snapshot_as_of (captured_at, delayed: true).

Endpoints that return an array

A JSON array has nowhere to put a field, so a handful of endpoints carry the same information in the X-Endpoint-Version and X-Data-As-Of response headers instead, the latter as compact JSON. The only one on the public surface is:

  • GET /optionquote/{ticker} - when it returns multiple contracts. Narrowing to a single contract (expiry + type + strike) returns an object, which carries data_as_of in the body as usual.

The others are admin routes: /v1/admin/dq/sources, /v1/admin/dq/summary, /v1/admin/dq/sweep, /v1/admin/dq/sweep-cmp, /v1/admin/dq/parity and /v1/admin/dq/parity/run.

On the historical API

data_as_of describes LIVE feed deliveries. A historical node reads the archive and has no live feeds, so every slot there is null - honest rather than reusing the name for something else.

The archive vintage rides alongside as a second field, archive_as_of, in the same shape: the vintage of the rows actually replayed for the at you asked for. Feeds a request did not read are null.

This is worth reading. An archive gap is invisible in the payload otherwise: ask for 15:59 and the query happily returns the 14:00 row, and the two responses are identical apart from this field. A live staleness can be checked against the market; a historical one cannot be checked against anything.

"data_as_of": {
  "node": "f3",
  "equity_feed": null, "equity_options_feed": null,
  "index_feed": null, "index_options_feed": null,
  "futures_feed": null, "futures_options_feed": null,
  "flow_feed": null, "oi_feed": null, "macro_feed": null
},
"archive_as_of": {
  "node": "f3",
  "equity_feed": "2024-03-01T19:00:00.000Z",
  "equity_options_feed": "2024-03-01T20:59:58.000Z",
  "index_feed": null, "index_options_feed": null,
  "futures_feed": null, "futures_options_feed": null,
  "flow_feed": null,
  "oi_feed": "2024-02-29T21:00:00.000Z",
  "macro_feed": "2024-03-01T21:00:00.000Z"
}

Use it to confirm the replay landed where you asked. as_of tells you the timestamp resolved for the request; archive_as_of.equity_options_feed tells you how current the chain behind it actually was at that instant.


Rate Limits & Plans

Responses from endpoints that count toward the daily quota include rate limit headers (/v1/account and the historical /v1/tickers do not). Requests rejected with 403 still count. Responses are Brotli-compressed (Content-Encoding: br) when the client accepts it.

Header Description
X-RateLimit-Limit Max requests per day (or unlimited)
X-RateLimit-Remaining Requests remaining today
X-RateLimit-Reset Unix time (seconds) when the quota resets, 00:00 UTC
Retry-After Seconds until the quota resets (only on 429)

Plans

Plan Daily Requests Access
Free 5 Stock quotes, gamma exposure by strike (GEX, levels), BSM greeks, IV solver, tickers, options meta, symbols, surface, stock summary (delayed for unauthenticated, live for authenticated; CME futures delayed below Growth)
Basic 250 Everything in Free + delta/vanna/charm exposure (DEX/VEX/CHEX) + ETF & index symbols + max pain analysis
Growth 2,500 + Exposure summary, narrative, history, 0DTE analytics, volatility analytics, option quotes, full-chain GEX (no expiry filter), Kelly sizing, flow analytics (/v1/flow/levels, /pin-risk, /summary, /gex, /dex, /dealer-risk)
Alpha Unlimited + Advanced volatility (SVI, variance surfaces, arbitrage detection, greeks surfaces, var swap), VRP analytics (risk premium, z-score, percentile, directional VRP, term structure, GEX/vanna-conditioned regime, strategy suitability scores, dealer flow risk, macro context), SVI-smoothed IV on option quotes, raw OI simulator state (/v1/flow/oi), flow bundle (/v1/flow/live), unusual-flow signal feed (/v1/flow/signals), and raw option/stock flow data (/v1/flow/options/*, /v1/flow/stocks/*)
Enterprise Unlimited Everything in Alpha + the Enterprise snapshot trigger

Index & ETF Symbols

Index symbols (SPX, VIX, RUT, DJX, DJI, OEX, XSP, XEO, SPXW, MRUT, MNX, VVIX, RVX, VXST, VXEEM, GVZ, OVX, TYVIX, SRVIX, SKEW, BSZ, BVZ, TNX, TYX, FVX, IRX, MXEA, MXEF) require the Basic plan or higher on any endpoint. Free-tier requests for index symbols return 403.

ETF symbols are gated the same way: a maintained list of optionable ETFs requires the Basic plan or higher on any endpoint, and Free-tier requests for them return 403. The list covers broad-market (SPY, QQQ, IWM, DIA, VTI, VOO, IVV, RSP), sector SPDRs (XLF, XLE, XLK, ...), thematic/industry (SMH, SOXX, XBI, JETS, KWEB, ...), ARK funds, international (EEM, EFA, FXI, EWZ, ...), fixed income (TLT, HYG, LQD, ...), commodities (GLD, SLV, USO, GDX, ...), real estate (VNQ), and leveraged/inverse products (TQQQ, SQQQ, UVXY, SOXL, ...). The authoritative list is EndpointAccessMiddleware.EtfSymbols. Individual equities (AAPL, TSLA, NVDA, ...) remain available on Free.

Futures Symbols

CME futures are served and queried under the Yahoo continuous-front =F suffix, so /v1/exposure/gex/ES=F returns the E-mini S&P 500 future (priced Black-76 on the forward, with the per-root contract multiplier). The bare root is not the future - ES is the Eversource Energy equity (BSM, ×100), and the suffix keeps the two from ever colliding.

Coverage spans seven complexes. These roots carry the full options-analytics stack - GEX, DEX, VEX, CHEX, levels, max pain, surface, exposure summary, narrative and live flow:

Complex Serving symbols Multiplier
Equity index ES=F / MES=F 50 / 5
NQ=F / MNQ=F 20 / 2
RTY=F 50
YM=F 5
Metals GC=F gold 100 (troy oz)
SI=F silver 5,000 (troy oz)
HG=F copper 25,000 (lb)
PL=F platinum 50 (troy oz)
PA=F palladium 100 (troy oz)
Energy CL=F crude oil 1,000 (bbl)
BZ=F Brent 1,000 (bbl)
NG=F natural gas 10,000 (MMBtu)
RB=F RBOB gasoline 42,000 (gal)
HO=F heating oil 42,000 (gal)
Treasuries ZT=F 2-year 2,000
ZF=F 5-year 1,000
ZN=F 10-year 1,000
TN=F ultra 10-year 1,000
ZB=F bond 1,000
UB=F ultra bond 1,000
Grains ZC=F corn 50
ZS=F soybeans 50
ZW=F wheat 50
ZL=F soybean oil 600
ZM=F soybean meal 100 (short ton)
FX 6E=F euro 125,000
6B=F sterling 62,500
6A=F Australian dollar 100,000
6C=F Canadian dollar 100,000
6S=F Swiss franc 125,000
6N=F New Zealand dollar 100,000
6J=F Japanese yen 12,500,000
Crypto BTC=F bitcoin 5 (BTC)
MBT=F micro bitcoin 0.1 (BTC)
ETH=F ether 50 (ETH)
MET=F micro ether 0.1 (ETH)

Quote conventions. Treasuries are quoted in points of par and grains in cents, so the multiplier is the contract size divided by 100 - ZN=F at 108.625 is 108.625% of $100,000 face, i.e. $108,625, not $10.9M. Reading the raw contract size instead puts notional and dollar gamma out by two orders of magnitude.

Access. Live futures data requires the Growth plan or higher on every endpoint. The gate is a symbol-class check, so it applies to any route that names a futures symbol; anonymous callers are evaluated as Free and receive 403 tier_restricted. The one exception is GET /v1/stock/{symbol}/summary, which serves callers below Growth (anonymous, Free, Basic) the newest snapshot captured before today in US Eastern time, marked snapshot_as_of.delayed: true, with the plan's field restrictions.

Priced but no options analytics. Only the micro Russell (M2K=F) and micro Dow (MYM=F) fall in this category: they resolve for quotes, pricing and multiplier purposes, but CME lists no options on either, so options-analytics endpoints return an empty chain.

Every other root in the table carries an options chain. Copper, platinum, palladium, silver, the FX complex and the crypto micros previously returned an empty one - not because the options do not exist, but because CME lists them under a different product code from the future (silver options are product SO, not SI; euro FX options are EUU; the Russell's standard series is RTO and the Dow's is EYM). Those products are subscribed and merged into their futures root.

Series depth. The equity-index roots carry the standard series plus the daily/weekly/EOM products, so ES=F, NQ=F, RTY=F, MES=F and MNQ=F have same-day expiries; YM=F has Friday weeklies only, which is all CME lists. Gold (GC=F) also carries weekday weeklies alongside its standard OG series: Friday OG1-OG5, Monday G1M-G5M, Tuesday G1T-G5T, Wednesday G1W-G5W and Thursday G1R-G5R. Other non-equity-index roots carry their standard (monthly/quarterly) series, but not the short-dated weekly products, which CME lists as separate codes again (silver weeklies are S1T/M4S/R1S, euro FX weeklies are WE1-WE4). Use /v1/options/{ticker}, which returns the expirations and the strikes listed under each, to see exactly what a root carries rather than assuming an expiry exists.

A bare futures root with no equity namesake (NQ, RTY, YM, the micros) resolves as an equity and returns 404 (no such equity exists). Use the =F form for futures.

Historical replay (/v1/historical/*) does not yet cover futures; live analytics are available now.

0DTE Access

Requests with ?expiration= set to today's date (0DTE) require the Growth plan or higher. This applies to GEX, DEX, VEX, CHEX, and Levels endpoints. Non-0DTE expiration filters are available on all plans. Users who subscribed before 2026-03-20 are grandfathered and retain 0DTE access on their current plan.


GET /stockquote/{ticker}

Returns the latest available bid, ask, midpoint, and lastPrice field for a symbol. On the current equity/ETF quote path, lastPrice equals mid; it is not an independently sourced last-trade price.

Parameters

Name In Required Description
ticker path yes Stock symbol (e.g. SPY, QQQ, AAPL)

Example

curl -H "X-Api-Key: YOUR_API_KEY" https://lab.flashalpha.com/stockquote/SPY

Response 200

{
  "ticker": "SPY",
  "bid": 597.50,
  "ask": 597.51,
  "mid": 597.505,
  "lastPrice": 597.505,
  "lastUpdate": "2026-02-28T16:30:45Z"
}

Quote semantics and validation limits

  • mid is computed as (bid + ask) / 2 on the equity/ETF quote path. lastPrice is assigned the same value; do not interpret it as a trade print or use it where a last-trade price is required.
  • The upstream compatibility service can substitute a stored last-trade price for a missing bid or ask. These fields therefore do not always represent an observed two-sided market.
  • lastUpdate is the quote timestamp, not a separate last-trade timestamp. Outside trading hours, the latest available quote can be from an earlier session.
  • This path does not enforce an underlying spread-width limit before computing the midpoint. A 200 response does not certify that the spread is suitable for pricing. Equity/ETF analytics use this midpoint as spot, so an anomalous underlying quote can affect IV, Greeks, and exposures.
  • The historical daily-bar freshness guard (409 stale_data) is separate from live quote validation. Option-contract quote exclusions do not validate the underlying stock quote. See data-quality scope.

Errors

Status Description
403 symbol_not_in_free_universe: Free plan, stock outside the tracked universe (/v1/universe)
404 No quote available for symbol

GET /optionquote/{ticker}

Returns option quotes enriched with BSM greeks, open interest, and volume.

  • No filters → returns all contracts for the symbol (array)
  • Partial filters → returns matching subset (array)
  • All three filters → returns a single contract (object)

Requires Growth plan or higher. The svi_vol field (SVI-smoothed implied volatility) requires the Alpha plan or higher - on lower tiers it returns null with "svi_vol_gated": "requires_alpha_tier".

Parameters

Name In Required Description
ticker path yes Underlying symbol
expiry query no Expiration date (yyyy-MM-dd)
strike query no Strike price
type query no C / Call or P / Put

Example

curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/optionquote/SPY?expiry=2026-03-20&strike=590&type=C"

Response 200

{
  "underlying": "SPY",
  "type": "C",
  "expiry": "2026-03-20",
  "strike": 590.0,
  "bid": 15.25,
  "ask": 15.35,
  "mid": 15.30,
  "bidSize": 1200,
  "askSize": 1500,
  "lastUpdate": "2026-02-28T16:30:45Z",
  "implied_vol": 0.1823,
  "delta": 0.6543,
  "gamma": 0.0089,
  "theta": -0.0234,
  "vega": 0.0456,
  "rho": 0.1234,
  "vanna": 0.0078,
  "charm": -0.0045,
  "svi_vol": 0.1820,
  "open_interest": 45000,
  "volume": 3250
}

Errors

Status Description
400 Invalid expiry format or invalid type
403 Requires Growth plan or higher
404 No matching options found

GET /v1/surface/{symbol}

Returns the pre-computed volatility surface grid for a symbol. Public endpoint - no authentication required.

The surface is pre-computed from SVI fits and updated periodically during market hours.

Parameters

Name In Required Description
symbol path yes Underlying symbol (e.g. SPY)

Example

curl https://lab.flashalpha.com/v1/surface/SPY

Response 200

Returns the JSON response containing the vol surface grid data.

Errors

Status Description
503 Surface data is still loading - try again shortly

GET /v1/stock/{symbol}/summary

Returns a comprehensive stock summary: price, ATM IV, historical volatility, vol risk premium, 25-delta skew, IV term structure, options flow (OI, volume, put/call ratios), exposure data (GEX/DEX/VEX/CHEX, gamma flip, walls, max pain, hedging estimates, zero-DTE, top strikes, regime), and macro context (VIX, VVIX, SKEW, SPX, MOVE, VIX term structure, VIX futures basis, Fear & Greed index).

Access by tier:

  • Authenticated (any plan): live summaries with the plan's field restrictions.
  • Unauthenticated (no API key): stock and index summaries use the newest available snapshot captured before today in US Eastern time, with public field restrictions.
  • Futures (ES=F, NQ=F ...): live from Growth. Below Growth, including anonymous callers, the summary uses the newest snapshot captured before today in US Eastern time, with the plan's field restrictions. Every other futures endpoint returns 403 tier_restricted below Growth.

Delayed summaries never include data captured today. Failed captures can leave older data; this is not a guaranteed 48-hour delay. If no eligible snapshot exists, the endpoint returns 404 with error: "no_cached_data", without a live fallback.

Share-class tickers such as BRK.B and BF.B are not captured for delayed summaries.

Delayed responses preserve the snapshot's data_as_of feed timestamps and add snapshot_as_of: { "captured_at": "<UTC timestamp>", "delayed": true }. Live responses do not carry snapshot_as_of. Older snapshots without recorded feed timestamps report null timestamps.

Parameters

Name In Required Description
symbol path yes Underlying symbol (e.g. SPY)

Example (authenticated - live data)

curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/stock/SPY/summary"

Example (unauthenticated - previous day snapshot)

curl "https://lab.flashalpha.com/v1/stock/SPY/summary"

Response 200

{
  "symbol": "SPY",
  "as_of": "2026-03-06T14:30:00Z",
  "market_open": true,
  "price": {
    "bid": 580.50,
    "ask": 580.52,
    "mid": 580.51,
    "last": 580.51,
    "last_update": "2026-03-06T14:30:00Z"
  },
  "volatility": {
    "atm_iv": 18.45,
    "hv_20": 15.32,
    "hv_60": 16.78,
    "vrp": 3.13,
    "skew_25d": {
      "expiry": "2026-03-14",
      "days_to_expiry": 8,
      "put_25d_iv": 20.12,
      "atm_iv": 18.45,
      "call_25d_iv": 17.10,
      "skew_25d": 3.02,
      "smile_ratio": 1.177
    },
    "iv_term_structure": [
      { "expiry": "2026-03-14", "iv": 17.8, "days_to_expiry": 8 },
      { "expiry": "2026-03-21", "iv": 18.2, "days_to_expiry": 15 }
    ]
  },
  "options_flow": {
    "total_call_oi": 12500000,
    "total_put_oi": 9800000,
    "total_call_volume": 850000,
    "total_put_volume": 620000,
    "pc_ratio_oi": 0.784,
    "pc_ratio_volume": 0.729,
    "active_expirations": 12
  },
  "exposure": {
    "net_gex": 2850000000,
    "net_dex": -450000000,
    "net_vex": 1200000000,
    "net_chex": 850000000,
    "gamma_flip": 575.25,
    "call_wall": 585.0,
    "put_wall": 570.0,
    "max_pain": 578.0,
    "highest_oi_strike": 580.0,
    "regime": "positive_gamma",
    "interpretation": {
      "gamma": "Dealers long gamma - expect range-bound, mean-reverting price action",
      "vanna": "Positive vanna - benefits from vol compression",
      "charm": "Time decay favors dealers - supports decline into close"
    },
    "hedging_estimate": {
      "spot_down_1pct": { "dealer_shares": 1250000, "direction": "buy", "notional_usd": 725000000 },
      "spot_up_1pct": { "dealer_shares": 1250000, "direction": "sell", "notional_usd": 725000000 }
    },
    "zero_dte": {
      "net_gex": 450000000,
      "pct_of_total": 15.8,
      "expiration": "2026-03-06"
    },
    "top_strikes": [
      { "strike": 580.0, "net_gex": 850000000, "call_oi": 120000, "put_oi": 95000, "total_oi": 215000 },
      { "strike": 575.0, "net_gex": -620000000, "call_oi": 80000, "put_oi": 110000, "total_oi": 190000 }
    ],
    "oi_weighted_dte": 18.5
  },
  "macro": {
    "vix": { "value": 18.5, "change": -0.85, "change_pct": -4.39 },
    "vvix": { "value": 92.3, "change": 1.2, "change_pct": 1.32 },
    "skew": { "value": 138.5, "change": -0.5, "change_pct": -0.36 },
    "spx": { "value": 5820.0, "change": 15.3, "change_pct": 0.26 },
    "move": { "value": 95.2, "change": -2.1, "change_pct": -2.16 },
    "vix_term_structure": {
      "levels": { "vix9d": 16.2, "vix": 18.5, "vix3m": 19.8, "vix6m": 20.5 },
      "near_slope_pct": 14.2,
      "structure": "contango"
    },
    "vix_futures": {
      "front_month": 19.8,
      "spot": 18.5,
      "spread": 1.3,
      "basis_pct": 7.03,
      "basis": "contango"
    },
    "fear_and_greed": { "score": 42, "rating": "Fear" }
  }
}

Response Fields

Section Description
price Current bid/ask/mid/last from live market data
volatility.atm_iv At-the-money implied vol from nearest SVI slice (%)
volatility.hv_20 / hv_60 20-day and 60-day realized (historical) volatility (%)
volatility.vrp Vol risk premium = ATM IV − HV20
volatility.skew_25d 25-delta put/call skew from nearest SVI slice (put IV, ATM IV, call IV, skew spread, smile ratio)
volatility.iv_term_structure IV at ATM for each active expiration (filtered to 5-200% to exclude bad SVI fits)
options_flow Aggregate OI, volume, and put/call ratios across all active expirations
exposure.net_gex/dex/vex/chex Net gamma/delta/vanna/charm exposure
exposure.gamma_flip Nullable underlying price where aggregate repriced GEX crosses from negative to positive. Certified when gamma_flip_status is available, otherwise an unverified level; see gamma flip determination.
exposure.gamma_flip_status available when the level is certified. A numeric gamma_flip with any of sensitive_root, uncertain_root_path, quality_budget, uncertain_gamma_variance, insufficient_local_coverage or insufficient_quote_quality is an unverified level: the root exists but failed that publication gate, and regime is unknown. Any other status means no supportable root and gamma_flip is null.
exposure.call_wall / put_wall Strikes with highest call/put GEX concentration; null when the eligible strikes have no GEX on that side
exposure.max_pain Strike where total option holder loss is maximized
exposure.highest_oi_strike Strike with highest total open interest
exposure.regime positive_gamma or negative_gamma based on spot vs the published gamma flip; unknown when the flip is withheld or when spot sits at the published flip and its stressed side cannot be certified. See Gamma regime vs net GEX.
exposure.interpretation Verbal descriptions of gamma regime, vanna, and charm effects
exposure.hedging_estimate Estimated dealer hedging flow (shares + notional) for ±1% spot moves
exposure.zero_dte 0DTE contribution to total GEX (net GEX, % of total, expiration date)
exposure.top_strikes Top 5 strikes by absolute net GEX with OI breakdown
exposure.oi_weighted_dte OI-weighted average days to expiry across all active options
macro.vix/vvix/skew/spx/move Index values with daily change and change %
macro.vix_term_structure VIX9D, VIX, VIX3M, VIX6M levels with near-term slope and contango/backwardation
macro.vix_futures VIX futures basis approximated from VIX3M vs VIX spot (spread, basis %, contango/backwardation)
macro.fear_and_greed CNN Fear & Greed Index score (0-100) and rating

Errors

Status Description
404 No data for symbol (live), or no eligible delayed snapshot (anonymous, or futures below Growth)

Notes

  • Unauthenticated responses show the previous trading session's snapshot
  • Macro fields (vix, vvix, etc.) may be null if the external data source is unavailable
  • exposure is null if no options/greeks data is loaded for the symbol
  • HV is computed from Yahoo Finance historical closes using annualized log-return standard deviation (√252)
  • skew_25d uses SVI-parameterized vol surface; k_25d ≈ ±σ√t × 0.675 (inverse normal of 0.25)
  • vix_futures.basis is approximated from VIX3M vs VIX spot (not actual futures prices)
  • top_strikes returns up to 5 strikes sorted by absolute net GEX
  • max_pain is the strike where total intrinsic value × OI across all options is minimized
  • IV term structure filters out slices with IV < 5% or > 200% to exclude bad SVI fits

Exposure units

The four exposure greeks are not expressed on a common basis. Compare each against its own history, never against the others in the same payload.

Field Formula Unit
net_gex / call_gex / put_gex gamma x OI x multiplier x spot^2 x 0.01 Dollars of dealer delta to hedge per 1% move in spot. Calls positive, puts negated
net_dex / call_dex / put_dex delta x OI x multiplier x spot Dollars of delta notional. No per-move normalisation, so this is routinely 3-4 orders of magnitude larger than net_gex on the same chain. That is the definition, not a scaling error
net_vex / call_vex / put_vex vanna x OI x multiplier x spot Dollars of delta per 1.00 change in volatility (i.e. per 100 vol points), not per vol point. Divide by 100 to read it per vol point
net_chex / call_chex / put_chex charm x OI x multiplier Delta drift per day from time decay

multiplier is 100 for equities and ETFs, and the contract multiplier for futures roots.

Two consequences worth stating plainly, because they surprise people:

  • net_dex dwarfing net_gex in the same response is expected. A chain with net_gex of -3.6e8 can legitimately carry net_dex of +2.0e12.
  • net_vex is per 1.00 vol, not per vol point. This differs from vega, which the greeks endpoints report per 1 vol point. The two conventions are inconsistent with each other; net_vex is kept as-is because customers backtest against the published series.

GET /v1/exposure/gex/{symbol}

Returns gamma exposure by strike, including open interest, volume, and day-over-day OI changes.

Parameters

Name In Required Default Description
symbol path yes - Underlying symbol
expiration query no all Filter to single expiry (yyyy-MM-dd)
min_oi query no 0 Minimum open interest threshold

Note: Full-chain GEX (all expirations) requires the Growth plan. Add ?expiration=yyyy-MM-dd to filter by a single expiry on lower plans.

Example

curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/exposure/gex/SPY?min_oi=100"

Response 200

{
  "symbol": "SPY",
  "underlying_price": 597.505,
  "as_of": "2026-02-28T16:30:45Z",
  "gamma_flip": 595.25,
  "net_gex": 2850000000,
  "net_gex_label": "positive",
  "strikes": [
    {
      "strike": 575.0,
      "call_gex": 12500000,
      "put_gex": 8900000,
      "net_gex": 21400000,
      "call_oi": 15000,
      "put_oi": 12000,
      "call_volume": 250,
      "put_volume": 180,
      "call_oi_change": 500,
      "put_oi_change": -200
    }
  ]
}

Errors

Status Description
400 Invalid expiration format
403 Full-chain GEX requires Growth plan
403 symbol_not_in_free_universe: Free plan, stock outside the tracked universe (/v1/universe)
404 Symbol not found or no data

GET /v1/exposure/dex/{symbol}

Returns delta exposure by strike.

Requires Basic plan or higher.

Parameters

Name In Required Description
symbol path yes Underlying symbol
expiration query no Filter to single expiry (yyyy-MM-dd)

Example

curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/exposure/dex/SPY"

Response 200

{
  "symbol": "SPY",
  "underlying_price": 597.505,
  "as_of": "2026-02-28T16:30:45Z",
  "net_dex": -450000000,
  "strikes": [
    {
      "strike": 575.0,
      "call_dex": 5600000,
      "put_dex": 4200000,
      "net_dex": 9800000
    }
  ]
}

GET /v1/exposure/vex/{symbol}

Returns vanna exposure by strike. Vanna measures the sensitivity of delta to changes in implied volatility.

Requires Basic plan or higher.

Parameters

Name In Required Description
symbol path yes Underlying symbol
expiration query no Filter to single expiry (yyyy-MM-dd)

Example

curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/exposure/vex/SPY"

Response 200

{
  "symbol": "SPY",
  "underlying_price": 597.505,
  "as_of": "2026-02-28T16:30:45Z",
  "net_vex": 1200000000,
  "vex_interpretation": "Positive vanna - benefits from vol compression",
  "strikes": [
    {
      "strike": 575.0,
      "call_vex": 2300000,
      "put_vex": 1800000,
      "net_vex": 4100000
    }
  ]
}

GET /v1/exposure/chex/{symbol}

Returns charm exposure by strike. Charm measures the sensitivity of delta to the passage of time.

Requires Basic plan or higher.

Parameters

Name In Required Description
symbol path yes Underlying symbol
expiration query no Filter to single expiry (yyyy-MM-dd)

Example

curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/exposure/chex/SPY"

Response 200

{
  "symbol": "SPY",
  "underlying_price": 597.505,
  "as_of": "2026-02-28T16:30:45Z",
  "net_chex": 850000000,
  "chex_interpretation": "Positive charm - time decay benefits dealers",
  "strikes": [
    {
      "strike": 575.0,
      "call_chex": 950000,
      "put_chex": 620000,
      "net_chex": 1570000
    }
  ]
}

GET /v1/exposure/summary/{symbol}

Returns a comprehensive exposure summary: net GEX/DEX/VEX/CHEX totals, gamma regime, verbal interpretation, dealer hedging estimates for +/-1% spot moves, and 0DTE contribution.

Requires Growth plan or higher.

Parameters

Name In Required Description
symbol path yes Underlying symbol

Example

curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/exposure/summary/SPY"

Response 200

{
  "symbol": "SPY",
  "underlying_price": 597.505,
  "as_of": "2026-02-28T16:30:45Z",
  "gamma_flip": 595.25,
  "regime": "positive_gamma",
  "exposures": {
    "net_gex": 2850000000,
    "net_dex": -450000000,
    "net_vex": 1200000000,
    "net_chex": 850000000
  },
  "interpretation": {
    "gamma": "Dealers long gamma - expect range-bound, mean-reverting price action",
    "vanna": "Positive vanna - benefits from vol compression",
    "charm": "Time decay favors dealers - supports decline into close"
  },
  "hedging_estimate": {
    "spot_down_1pct": {
      "dealer_shares_to_trade": 4780000,
      "direction": "BUY",
      "notional_usd": 2852000000
    },
    "spot_up_1pct": {
      "dealer_shares_to_trade": -4780000,
      "direction": "SELL",
      "notional_usd": 2852000000
    }
  },
  "zero_dte": {
    "net_gex": 285000000,
    "pct_of_total_gex": 10.0,
    "expiration": "2026-02-28"
  }
}

Response Fields

Field Description
regime positive_gamma when spot >= gamma_flip, negative_gamma when below, unknown when no level is published or the stressed side at spot is uncertified. The label uses spot vs flip; the boundary calculation reconciles the same-chain spot-GEX sign. See Gamma regime vs net GEX.
gamma_flip Nullable negative-to-positive boundary of aggregate GEX repriced across underlying prices. Certified when gamma_flip_status is available, otherwise an unverified level.
gamma_flip_status available when the level is certified. A numeric gamma_flip with any of sensitive_root, uncertain_root_path, quality_budget, uncertain_gamma_variance, insufficient_local_coverage or insufficient_quote_quality is an unverified level: the root exists but failed that publication gate, and regime is unknown. Any other status means no supportable root and gamma_flip is null. See gamma flip determination.
hedging_estimate Estimated dealer hedging flow if spot moves +/-1%
zero_dte Same-day expiration contribution to total GEX

GET /v1/exposure/levels/{symbol}

Returns key technical levels derived from options exposure: gamma flip, call/put walls, max gamma strikes, highest OI strike, and 0DTE magnet.

Parameters

Name In Required Description
symbol path yes Underlying symbol

Example

curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/exposure/levels/SPY"

Response 200

{
  "symbol": "SPY",
  "underlying_price": 597.505,
  "as_of": "2026-02-28T16:30:45Z",
  "levels": {
    "gamma_flip": 595.25,
    "max_positive_gamma": 600.0,
    "max_negative_gamma": 585.0,
    "call_wall": 600.0,
    "put_wall": 595.0,
    "highest_oi_strike": 600.0,
    "zero_dte_magnet": 598.0
  }
}

Response Fields

Field Description
gamma_flip Nullable underlying price where aggregate repriced GEX crosses from negative to positive. Publication first checks discrete single-strike roots within [root - 0.001 * spot, root + 0.001 * spot], then permits [root - 0.0025 * spot, root + 0.0025 * spot] within the same total work budget; an exhausted strict-check budget prevents a retry. Coverage includes the window that certified the root. A certified level can coexist with regime: unknown when spot sits inside the certified window and the stressed side at spot cannot be certified. A failed fallback retains the strict failure reason, and the root is still published as an unverified level under that status with regime: unknown; the same applies to coverage and quote-quality failures. Quote checks and stress factors are unchanged. This is not a probability guarantee or a cumulative/per-strike-profile crossing. See gamma flip determination.
gamma_flip_status available when the level is certified. A numeric gamma_flip with any of sensitive_root, uncertain_root_path, quality_budget, uncertain_gamma_variance, insufficient_local_coverage or insufficient_quote_quality is an unverified level: the root exists but failed that publication gate, and regime is unknown. Any other status means no supportable root and gamma_flip is null.
call_wall Strike with highest call GEX - acts as resistance; null when eligible strikes have no call GEX
put_wall Strike with highest put GEX - acts as support; null when eligible strikes have no put GEX
max_positive_gamma Strike with highest positive net GEX
max_negative_gamma Strike with most negative net GEX
highest_oi_strike Strike with highest total open interest
zero_dte_magnet 0DTE strike with highest absolute net GEX; null when every 0DTE strike has zero net GEX

Gamma-flip availability: clients must handle frequent nulls. In the September 9, 2026 validation of 4,575 frozen captures across seven symbols and requested 0/7/28-day expiries, 1,613 levels were published (35.26%) and 2,962 were null (64.74%). These are sample results, not an availability promise. A withheld level also makes its gamma regime unknown and can remove gamma-dependent zero-DTE setups, especially late in the session. Read gamma_flip_status to distinguish quote/coverage/sensitivity policy from absence of a supported mathematical boundary. A numeric flip with regime: unknown means spot sits at the published flip and its stressed side could not be certified; gamma-dependent setups and scores are suppressed while the level and its geometric distance are retained. Do not reconstruct a directional regime from the numeric level in that state.

Errors

Status Description
403 tier_restricted: ETF or index symbol on the Free plan (Basic+)
403 symbol_not_in_free_universe: Free plan, stock outside the tracked universe (/v1/universe)

GET /v1/exposure/narrative/{symbol}

Returns a verbal analysis of the current options exposure landscape with day-over-day changes, key levels context, notable OI flow, vanna/charm interpretation, and market outlook. Includes structured data backing each section.

Requires Growth plan or higher.

Parameters

Name In Required Description
symbol path yes Underlying symbol

Example

curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/exposure/narrative/SPY"

Response 200

{
  "symbol": "SPY",
  "underlying_price": 597.505,
  "as_of": "2026-02-28T16:30:45Z",
  "narrative": {
    "regime": "Dealers are long gamma (net GEX +$2.9B) - expect mean-reverting, range-bound price action.",
    "gex_change": "Net GEX increased from +$2.6B to +$2.9B (+11.5%) - gamma cushion strengthening.",
    "key_levels": "Call wall at 600, Put wall at 595, Gamma flip at 595.25.",
    "flow": "Top OI changes: +5,000 call OI at 600 strike, -2,000 put OI at 595 strike.",
    "vanna": "Positive vanna (+$1.2B) with VIX at 18.5 - vol compression supports upside.",
    "charm": "Positive charm (+$850M) - time decay pushing dealers to buy, providing support.",
    "zero_dte": "0DTE accounts for 10% of total GEX - minimal intraday impact.",
    "outlook": "Positive gamma regime with strengthening cushion. Testing 600 call wall.",
    "data": {
      "net_gex": 2850000000,
      "net_gex_prior": 2600000000,
      "net_gex_change_pct": 9.6,
      "vix": 18.5,
      "gamma_flip": 595.25,
      "call_wall": 600.0,
      "put_wall": 595.0,
      "regime": "positive_gamma",
      "zero_dte_pct": 10.0,
      "top_oi_changes": [
        {
          "strike": 600.0,
          "type": "C",
          "oi_change": 5000,
          "volume": 1250
        }
      ]
    }
  }
}

Narrative Sections

Field Description
regime Current gamma regime and what it implies for price action
gex_change How net GEX changed vs. prior day
key_levels Call wall, put wall, gamma flip in context of spot
flow Largest OI changes - where is new positioning concentrating
vanna Vanna exposure interpretation with VIX context
charm Charm/time-decay effect on dealer positioning
zero_dte Same-day expiration significance
outlook Overall synthesis and directional bias
data Raw numeric data backing the narrative

GET /v1/exposure/zero-dte/{symbol}

Returns a comprehensive 0DTE (zero days to expiration) analytics view for intraday options trading. Includes gamma regime (with distance-to-flip in dollars and σ), expected move, pin risk scoring with sub-score breakdown, dealer hedging estimates at ±0.10%, ±0.25%, ±0.50%, ±1.00% moves plus convexity-at-spot, time decay acceleration, vol context, flow data with ATM/top-3 concentration, key levels with wall strength and a level-cluster score, liquidity (spreads + execution score), snapshot metadata (age, contract count, data quality), and a per-strike breakdown.

Requires Growth plan or higher.

Parameters

Name In Required Default Description
symbol path yes - Underlying symbol
strike_range query no 0.03 Fraction of spot to include in strikes array (0.001-0.10). Aggregates always use full chain.
expiry query no today's 0DTE Target expiry (yyyy-MM-dd). Selects 1DTE / 2DTE / any expiry via the same 0DTE selector; omit for today's same-day expiry. 400 invalid_expiry if malformed.

Example

curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/exposure/zero-dte/SPY?strike_range=0.03"

Response 200

{
  "symbol": "SPY",
  "underlying_price": 590.42,
  "expiration": "2026-03-19",
  "as_of": "2026-03-19T14:45:12Z",
  "market_open": true,
  "time_to_close_hours": 1.25,
  "time_to_close_pct": 80.8,
  "regime": {
    "label": "positive_gamma",
    "description": "Dealers long gamma - moves dampened, mean reversion likely",
    "gamma_flip": 588.50,
    "spot_vs_flip": "above",
    "spot_to_flip_pct": 0.33,
    "distance_to_flip_dollars": 1.92,
    "distance_to_flip_sigmas": 1.83
  },
  "exposures": {
    "net_gex": 1842000000,
    "net_dex": 48200000000,
    "net_vex": -320000000,
    "net_chex": 95000000,
    "pct_of_total_gex": 62.4,
    "total_chain_net_gex": 2952000000
  },
  "expected_move": {
    "implied_1sd_dollars": 2.18,
    "implied_1sd_pct": 0.37,
    "remaining_1sd_dollars": 1.05,
    "remaining_1sd_pct": 0.18,
    "upper_bound": 591.47,
    "lower_bound": 589.37,
    "straddle_price": 1.62,
    "atm_iv": 0.123
  },
  "pin_risk": {
    "magnet_strike": 590,
    "magnet_gex": 580000000,
    "distance_to_magnet_pct": 0.07,
    "pin_score": 82,
    "components": {
      "oi_score": 78,
      "proximity_score": 92,
      "time_score": 81,
      "gamma_score": 70
    },
    "max_pain": 590,
    "oi_concentration_top3_pct": 41.2,
    "description": "Strong pin at 590. 82/100 pin score with 41% of OI in top 3 strikes."
  },
  "hedging": {
    "spot_up_10bp": { "dealer_shares_to_trade": -31200, "direction": "sell", "notional_usd": -18432000 },
    "spot_down_10bp": { "dealer_shares_to_trade": 31200, "direction": "buy", "notional_usd": 18432000 },
    "spot_up_25bp": { "dealer_shares_to_trade": -78050, "direction": "sell", "notional_usd": -46079000 },
    "spot_down_25bp": { "dealer_shares_to_trade": 78050, "direction": "buy", "notional_usd": 46079000 },
    "spot_up_half_pct": { "dealer_shares_to_trade": -156100, "direction": "sell", "notional_usd": -92158000 },
    "spot_down_half_pct": { "dealer_shares_to_trade": 156100, "direction": "buy", "notional_usd": 92158000 },
    "spot_up_1pct": { "dealer_shares_to_trade": -312200, "direction": "sell", "notional_usd": -184316000 },
    "spot_down_1pct": { "dealer_shares_to_trade": 312200, "direction": "buy", "notional_usd": 184316000 },
    "convexity_at_spot": -84210000.5
  },
  "decay": {
    "net_theta_dollars": -4820000,
    "theta_per_hour_remaining": -3856000,
    "charm_regime": "time_decay_dealers_buy",
    "charm_description": "Time decay pushing dealers to buy - supportive into close",
    "gamma_acceleration": 2.4,
    "description": "0DTE theta bleeding $3,856/hr. Gamma 2.4x higher than equivalent 7DTE."
  },
  "vol_context": {
    "zero_dte_atm_iv": 12.3,
    "seven_dte_atm_iv": 14.8,
    "iv_ratio_0dte_7dte": 0.83,
    "vix": 16.2,
    "skew_25d": 1.85,
    "vanna_exposure": -320000000,
    "vanna_interpretation": "vol_up_dealers_sell",
    "description": "0DTE IV at 12.3% vs 7DTE at 14.8%. Negative vanna - vol spike triggers dealer selling."
  },
  "flow": {
    "total_volume": 842000,
    "call_volume": 520000,
    "put_volume": 322000,
    "net_call_minus_put_volume": 198000,
    "total_oi": 1240000,
    "call_oi": 680000,
    "put_oi": 560000,
    "pc_ratio_volume": 0.619,
    "pc_ratio_oi": 0.824,
    "volume_to_oi_ratio": 0.679,
    "atm_volume_share_pct": 18.4,
    "top3_strike_volume_pct": 36.2
  },
  "levels": {
    "call_wall": 595,
    "call_wall_gex": 420000000,
    "call_wall_strength": 0.612,
    "distance_to_call_wall_pct": 0.78,
    "put_wall": 585,
    "put_wall_gex": -380000000,
    "put_wall_strength": 0.547,
    "distance_to_put_wall_pct": -0.92,
    "distance_to_magnet_dollars": 0.42,
    "highest_oi_strike": 590,
    "highest_oi_total": 48200,
    "max_positive_gamma": 592,
    "max_negative_gamma": 586,
    "level_cluster_score": 71
  },
  "liquidity": {
    "atm_spread_pct": 0.45,
    "weighted_spread_pct": 1.08,
    "execution_score": 86
  },
  "metadata": {
    "snapshot_age_seconds": 12.4,
    "chain_contract_count": 412,
    "data_quality_score": 96,
    "greek_smoothness_score": 82
  },
  "strikes": [
    {
      "strike": 590,
      "distance_from_spot_pct": -0.07,
      "call_symbol": "SPY-260319-C-590",
      "put_symbol": "SPY-260319-P-590",
      "call_gex": 450000000, "put_gex": -380000000, "net_gex": 70000000,
      "call_dex": 12500000, "put_dex": -15000000, "net_dex": -2500000,
      "net_vex": -2400000, "net_chex": 1850000,
      "call_oi": 25000, "put_oi": 30000,
      "call_volume": 15000, "put_volume": 12000,
      "gex_share_pct": 4.12, "oi_share_pct": 4.43, "volume_share_pct": 3.21,
      "call_iv": 0.18, "put_iv": 0.19,
      "call_delta": 0.50, "put_delta": -0.50,
      "call_gamma": 0.025, "put_gamma": 0.025,
      "call_theta": -1.0, "put_theta": -1.0,
      "call_mid": 1.04, "put_mid": 0.62,
      "call_spread_pct": 1.92, "put_spread_pct": 3.22
    }
  ]
}

Response Fields

Section Description
time_to_close_hours Hours until 4:00 PM ET. The single most important 0DTE context variable.
time_to_close_pct Percentage of trading day elapsed (0=open, 100=close)
regime 0DTE-specific gamma regime: spot vs a gamma_flip computed on the 0DTE-only chain, so it can legitimately differ from the full-chain regime for the same symbol at the same moment. Like elsewhere, it is not derived from the sign of net_gex. See Gamma regime vs net GEX.
regime.gamma_flip_status available when the level is certified. A numeric gamma_flip with any of sensitive_root, uncertain_root_path, quality_budget, uncertain_gamma_variance, insufficient_local_coverage or insufficient_quote_quality is an unverified level: the root exists but failed that publication gate, and regime is unknown. Any other status means no supportable root and gamma_flip is null. regime.label is unknown for an unverified level, when no level exists, or when spot sits at a certified flip and its stressed side cannot be certified.
regime.distance_to_flip_dollars Absolute spot-to-flip distance in dollars; null when the flip is unavailable.
regime.distance_to_flip_sigmas Nullable distance to flip in remaining-time 1σ during the session (uses ATM IV × √t_remain). Falls back to full-day 1σ when the market is closed. Null when the flip or required volatility input is unavailable; <1.0 means the flip is within a 1σ move.
exposures.pct_of_total_gex 0DTE GEX as % of full-chain GEX. >50% = 0DTE dominates intraday
expected_move.remaining_1sd_* Shrinks in real-time as close approaches. At 9:31 AM = full day; at 3:30 PM = 27.7% of full day
expected_move.straddle_price ATM 0DTE straddle mid - direct market-implied expected move
pin_risk.pin_score 0-100 composite: OI concentration (30%), magnet proximity (25%), time remaining (25%), gamma magnitude (20%)
pin_risk.components Each sub-score on 0-100 before weighting - lets you see why the composite is high or low
pin_risk.max_pain Strike where total option holder intrinsic value is minimized
hedging.spot_*_10bp / *_25bp / *_half_pct / *_1pct Dealer hedging at ±0.10%, ±0.25%, ±0.50%, ±1.00% spot moves. The 10/25 bp ticks are useful for 0DTE intraday wiggle
hedging.convexity_at_spot Second finite-difference of net GEX across the three strikes nearest spot, in GEX-units per dollar². Curvature of the GEX profile at ATM. Sign convention: a negative-GEX trough at ATM (the classic short-gamma trap) produces a positive value because the profile curves upward; a positive-GEX peak at ATM produces a negative value. Magnitude tells you how sharp the local feature is
decay.theta_per_hour_remaining net_theta_dollars / time_to_close_hours - accelerates as denominator shrinks
decay.gamma_acceleration 0DTE ATM gamma / 7DTE ATM gamma. Typically 2-5x, can hit 10x+ near close
vol_context.iv_ratio_0dte_7dte <1.0 = 0DTE is "cheap" vs term structure; >1.0 = event premium
vol_context.skew_25d 25-delta risk reversal in IV percentage points: IV of put nearest delta=−0.25 minus IV of call nearest delta=+0.25. Positive = put-skew (downside vol bid); negative = call-skew. null when neither side has a contract within 0.20 of 25Δ
flow.volume_to_oi_ratio >1.0 = heavy day-trading (intraday flow exceeds overnight positioning)
flow.net_call_minus_put_volume Call volume minus put volume (signed). Positive = bullish flow
flow.atm_volume_share_pct Share of total volume that traded within ±0.5% of spot
flow.top3_strike_volume_pct Share of total volume concentrated in the 3 most-active strikes
levels.call_wall_strength / put_wall_strength Wall GEX as fraction of same-side total GEX magnitude. 1.0 = single-strike concentration; <0.2 = wall is weak
levels.distance_to_call_wall_pct / distance_to_put_wall_pct Signed % from spot to each wall (positive = above spot)
levels.distance_to_magnet_dollars Dollar distance from spot to the magnet strike (in addition to the percent in pin_risk)
levels.level_cluster_score 0-100. Tightness of {flip, magnet, max_pain, highest_oi, walls} relative to 1σ expected move. High = levels stack at one strike (pin setup); low = levels scattered
liquidity.atm_spread_pct Avg of call+put bid-ask spread (%) at the strike nearest spot that has a quote on at least one side
liquidity.weighted_spread_pct OI-weighted bid-ask spread, computed across the strikes within the requested strike_range window (not the full chain)
liquidity.execution_score 0-100 composite: 70% spread, 30% ATM OI depth. Heuristic - read directionally, not as a tradable threshold
metadata.snapshot_age_seconds Seconds since the most recent contract update - staleness check for the whole snapshot
metadata.chain_contract_count Number of 0DTE contracts feeding the response
metadata.data_quality_score 0-100. Penalises NaN greeks, missing IV, and stale snapshots
metadata.greek_smoothness_score 0-100. Mean absolute consecutive-strike IV diff across strikes within the requested strike_range window (not the full chain), mapped to 0-100. Proxy for greek stability - without intraday history we can't measure true instability, so high score = smooth IV curve at this snapshot, not necessarily a stable surface over time

Per-strike fields

Inside strikes[], each entry exposes:

Field Description
strike Strike price
distance_from_spot_pct Signed % from spot (positive = above)
call_symbol / put_symbol OCC-style contract key (e.g. SPY-260319-C-590) - usable as a quote lookup
call_gex / put_gex / net_gex Per-strike gamma exposure, $ per 1% move (units)
call_dex / put_dex / net_dex Per-strike delta exposure, $ notional - orders larger than net_gex by definition (units)
net_vex / net_chex Per-strike vanna and charm exposure; net_vex is per 1.00 vol, not per vol point (units)
call_oi / put_oi Settled open interest by side
call_volume / put_volume Contracts traded this session by side, from the classified trade tape, cumulative from the open and updating intraday. Counts every print, including those that executed at the mid.
gex_share_pct / oi_share_pct / volume_share_pct Share of the 0DTE chain total at this strike
call_iv / put_iv / call_delta / put_delta / call_gamma / put_gamma / call_theta / put_theta Greeks
call_mid / put_mid Mid quote in dollars
call_spread_pct / put_spread_pct Bid-ask spread as % of mid

No 0DTE Expiry

If the symbol has no 0DTE expiry today (e.g. SPY on Tuesday/Thursday):

{
  "symbol": "SPY",
  "underlying_price": 590.42,
  "expiration": null,
  "as_of": "2026-03-17T15:30:00Z",
  "market_open": true,
  "no_zero_dte": true,
  "message": "No 0DTE expiry for SPY today (Tuesday). Next expiry: 2026-03-18.",
  "next_zero_dte_expiry": "2026-03-18"
}

Errors

Status Description
403 Requires Growth plan or higher
404 Symbol not found or no data

Notes

  • SPY has 0DTE on Mon/Wed/Fri. SPX (SPXW) has daily 0DTE.
  • theta_per_hour_remaining is null when time_to_close_hours is 0 (market closed)
  • Near close (<5 minutes), a warnings array is included noting potential greek instability
  • strike_range only filters the strikes array - all aggregate calculations use the full 0DTE chain
  • All calculations are pure static (no external calls beyond the initial data fetch)

GET /v1/exposure/sheet/{symbol}

Unified per-strike rowset joining GEX, DEX, VEX, CHEX, and DAG (delta-adjusted gamma) in one response, plus chain totals, the Line-in-the-Sand inflection strike, all gamma peaks (not just the single wall), and OPEX / triple-witching flags when an expiration filter is supplied.

Requires Growth plan or higher.

Parameters

Name In Required Default Description
symbol path yes - Underlying symbol
expiration query no all Single-expiry filter (yyyy-MM-dd). Triggers is_opex / is_triple_witching flags.
min_oi query no 0 Drops strikes whose call_oi + put_oi < min_oi.

Example

curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/exposure/sheet/SPY?expiration=2026-06-18"

Response 200

{
  "symbol": "SPY",
  "underlying_price": 597.50,
  "as_of": "2026-05-29T15:30:00Z",
  "expiration": "2026-06-18",
  "is_opex": true,
  "is_triple_witching": true,
  "totals": {
    "net_gex": 2850000000,
    "net_dex": -450000000,
    "net_vex": 1200000000,
    "net_chex": 850000000,
    "net_dag": 1425000000
  },
  "lis": { "strike": 595.0, "magnitude": 1.0 },
  "peaks": [
    { "strike": 600, "net_gex": 850000000, "strength": 1.00, "side": "call_wall" },
    { "strike": 590, "net_gex": -420000000, "strength": 0.49, "side": "put_wall" }
  ],
  "strikes": [
    {
      "strike": 595,
      "call_gex": 145000000, "put_gex": -89000000, "net_gex": 56000000,
      "call_dex": 9800000, "put_dex": -7200000, "net_dex": 2600000,
      "call_vex": 320000, "put_vex": -180000, "net_vex": 140000,
      "call_chex": 4500, "put_chex": -2200, "net_chex": 2300,
      "dag": 18900000,
      "call_oi": 15820, "put_oi": 12340
    }
  ]
}

Response Fields

Section / Field Description
is_opex True when expiration is the holiday-adjusted monthly OPEX (3rd Friday, shifted to Thursday when Friday is a US equity holiday - e.g. Juneteenth).
is_triple_witching True when is_opex AND month ∈ {Mar, Jun, Sep, Dec}.
totals.net_dag Σ over strikes of DAG = |delta| × gamma × OI × multiplier × spot² × 0.01 (multiplier is 100 for equities and index options, per-root for futures), sign-flipped for puts per dealer-short convention.
lis Strike with the largest |d²(net_gex)/dK²| using a non-uniform-spaced central second difference. null when the chain has <3 strikes or every second-difference is zero. magnitude is currently always 1.0 (field reserved for future relative-magnitude scoring - the strike itself is the load-bearing output today).
peaks[] Local maxima of |net_gex| whose strength ≥ 0.1 (fraction of max |net_gex| in the chain). side: call_wall when net_gex ≥ 0, otherwise put_wall.
strikes[].dag Per-strike DAG aggregated across calls + puts at that strike.
strikes[] greek triples call_*, put_*, and net_* for gex / dex / vex / chex use the same formulas as /v1/exposure/{gex,dex,vex,chex}.

Errors

Status Description
400 Invalid expiration format (must be yyyy-MM-dd)
403 Requires Growth plan or higher
404 Symbol not found or no data

Notes

  • The OPEX detector uses the same rule the historical pipeline applies: when the 3rd Friday is a US market holiday (Good Friday, Juneteenth), the OPEX date shifts to the Thursday before and the holiday Friday itself returns is_opex = false.
  • All exposure aggregates here are derived from settled OI. Use /v1/flow/* for the simulator-aware (live) versions.

GET /v1/exposure/term-structure/{symbol}

Per-greek exposure aggregated by DTE bucket and also rolled up per expiry - one call returns the same shape as four full-chain /v1/exposure/* calls, grouped by time. Buckets: 0-7d / 8-30d / 31-60d / 61-180d / 180d+.

Requires Growth plan or higher.

Parameters

Name In Required Description
symbol path yes Underlying symbol

Example

curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/exposure/term-structure/SPY"

Response 200

{
  "symbol": "SPY",
  "underlying_price": 597.50,
  "as_of": "2026-05-29T15:30:00Z",
  "by_dte_bucket": [
    { "bucket": "0-7d",    "dte_range": [0, 7],    "net_gex": 920000000,  "net_dex": -120000000, "net_vex":  85000000, "net_chex": 380000000, "contract_count": 412 },
    { "bucket": "8-30d",   "dte_range": [8, 30],   "net_gex": 1280000000, "net_dex": -200000000, "net_vex": 540000000, "net_chex": 340000000, "contract_count": 1810 },
    { "bucket": "31-60d",  "dte_range": [31, 60],  "net_gex":  460000000, "net_dex":  -85000000, "net_vex": 390000000, "net_chex":  90000000, "contract_count":  920 },
    { "bucket": "61-180d", "dte_range": [61, 180], "net_gex":  150000000, "net_dex":  -32000000, "net_vex": 145000000, "net_chex":  35000000, "contract_count":  640 },
    { "bucket": "180d+",   "dte_range": [181, 2147483647], "net_gex":  40000000, "net_dex":  -13000000, "net_vex": 40000000, "net_chex":  5000000, "contract_count":  220 }
  ],
  "by_expiry": [
    { "expiration": "2026-05-30", "dte": 1,  "is_opex": false, "is_triple_witching": false, "net_gex": 320000000, "net_dex": -50000000, "net_vex": 25000000, "net_chex": 180000000, "pct_of_chain_gex": 10.91 },
    { "expiration": "2026-06-18", "dte": 20, "is_opex": true,  "is_triple_witching": true,  "net_gex": 980000000, "net_dex": -160000000, "net_vex": 420000000, "net_chex": 260000000, "pct_of_chain_gex": 33.42 }
  ]
}

Response Fields

Section / Field Description
by_dte_bucket[].bucket Bucket name.
by_dte_bucket[].dte_range Inclusive [lower, upper] DTE bounds (final bucket carries int.MaxValue as the upper bound).
by_dte_bucket[].net_* Sum of GEX / DEX / VEX / CHEX over every contract whose DTE falls in the bucket.
by_dte_bucket[].contract_count Number of contracts feeding the bucket.
by_expiry[] One row per expiry, ordered ascending. is_opex / is_triple_witching use the holiday-adjusted rule.
by_expiry[].pct_of_chain_gex This expiry's |net_gex| as a share of Σ |net_gex| across all expiries (0-100). Legacy net-based share; it is not the gross share in gamma_concentration.
gamma_concentration Gross/net gamma distribution of the same captured chain: totals, by_expiry[] (with gross_share_pct), next_active_expiry, largest_gross_expiry, plus provenance (snapshot_id, pricing_basis, data_status, reasons). SPY/QQQ only; other symbols return the block with data_status: "unavailable" and reason unsupported_symbol. Definitions match expiry impact.

Errors

Status Description
403 Requires Growth plan or higher
404 Symbol not found or no data

Notes

  • Buckets that match no contracts are omitted from by_dte_bucket rather than emitted with zeros.
  • pct_of_chain_gex is 0 for every row when the chain's total |net_gex| is zero (safe-divide guard).
  • For QQQ, gamma_concentration uses the raw-quote common-anchor repricer that QQQ GEX uses. When no captured Greeks are available the legacy by_dte_bucket / by_expiry arrays can be empty while gamma_concentration is populated.

GET /v1/exposure/expiry-impact/{symbol}

Compares the current SPY or QQQ gamma distribution with the same captured inputs after excluding one expiry. Spot, Greeks and open interest are held fixed, so this measures how much of today's concentration depends on one maturity. It is not a forecast of post-expiry prices, dealer hedging or the next session's chain.

Requires Growth plan or higher. Supported symbols: SPY, QQQ.

Parameters

Name In Required Default Description
symbol path yes - SPY or QQQ.
expiry query no nearest active eligible expiry yyyy-MM-dd; must be one of available_expiries.
limit query no 5 1-20. Rows in impact.largest_gross_reductions and impact.strongest_remaining_concentrations. Totals and strikes[] always use the full eligible chain.

Example

curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/exposure/expiry-impact/SPY?limit=5"

Response 200

Abridged; values are illustrative.

{
  "symbol": "SPY",
  "underlying_price": 663.42,
  "snapshot_id": "4f1c9e…",
  "generated_at": "2026-10-16T18:30:00Z",
  "quote_at": "2026-10-16T18:29:58Z",
  "greeks_oldest_at": "2026-10-16T18:29:31Z",
  "greeks_newest_at": "2026-10-16T18:29:59Z",
  "calculation_version": "gamma-context-v2",
  "pricing_basis": "captured_cached_gamma_current_spot",
  "data_status": "partial",
  "reasons": [{ "code": "oi_vintage_unknown", "message": "The source does not carry a verified OI observation date; …" }],
  "oi_as_of": null,
  "oi_vintage_status": "unknown",
  "eligible_contract_count": 8412,
  "rejected_contract_count": 37,
  "gex_unit": "usd_delta_notional_per_1pct_move",
  "sign_convention": "calls_positive_puts_negative",
  "analysis_type": "expiry_removal_sensitivity",
  "selected_expiry": "2026-10-16",
  "selection_source": "nearest_active",
  "available_expiries": ["2026-10-16", "2026-10-19", "2026-10-20"],
  "current":          { "gross_gex_usd": 9100000000, "net_gex_usd": 2300000000, "call_gex_usd": 5700000000, "put_gex_usd": -3400000000, "contract_count": 8412 },
  "excluded_expiry":  { "gross_gex_usd": 1950000000, "net_gex_usd":  650000000, "call_gex_usd": 1300000000, "put_gex_usd":  -650000000, "contract_count": 1906 },
  "excluding_expiry": { "gross_gex_usd": 7150000000, "net_gex_usd": 1650000000, "call_gex_usd": 4400000000, "put_gex_usd": -2750000000, "contract_count": 6506, "status": "available" },
  "impact": {
    "removed_gross_share_pct": 21.43,
    "retained_gross_share_pct": 78.57,
    "net_gex_change_usd": -650000000,
    "net_sign_before": "positive",
    "net_sign_after": "positive",
    "net_sign_changed": false,
    "largest_gross_reductions": [
      { "strike": 665, "distance_from_spot_pct": 0.24, "current_gross_gex_usd": 610000000, "current_net_gex_usd": 140000000,
        "excluded_gross_gex_usd": 290000000, "excluded_net_gex_usd": 90000000,
        "remaining_gross_gex_usd": 320000000, "remaining_net_gex_usd": 50000000, "excluded_share_at_strike_pct": 47.54 }
    ],
    "strongest_remaining_concentrations": []
  },
  "strikes": [],
  "facts": [
    { "code": "expiry_gross_share", "text": "The selected expiry contributes 21.4% of captured gross gamma exposure.", "supporting_values": { "expiry": "2026-10-16", "gross_share_pct": 21.43, "gross_gex_usd": 1950000000 } }
  ]
}

Response Fields

Field Description
current / excluded_expiry / excluding_expiry Gross, net, call and put GEX (USD per 1% move) and contract counts for the whole eligible chain, the selected expiry, and the chain without it. excluding_expiry.status is available, zero_gross_exposure or no_remaining_contracts.
impact.removed_gross_share_pct 100 × excluded gross / current gross. Null when current gross is zero.
impact.net_gex_change_usd remaining net − current net (minus the excluded net).
impact.net_sign_before / net_sign_after / net_sign_changed Sign of the modeled net total before and after exclusion.
impact.largest_gross_reductions[] / strongest_remaining_concentrations[] Top limit strikes by gross removed / gross remaining.
strikes[] Every eligible strike with current, excluded and remaining gross/net GEX.
facts[] Plain-language statements with their supporting values.
pricing_basis captured_cached_gamma_current_spot (SPY: captured Greeks at current spot) or qqq_common_anchor_repriced (QQQ: raw quotes repriced at one spot and time).
data_status / reasons[] partial in normal operation (open-interest vintage is unknown), stale when an input predates the market-session freshness cutoff, unavailable when no contract qualifies. Reasons carry machine codes and excluded-contract counts.
snapshot_id SHA-256 fingerprint of the captured inputs; identical inputs give identical IDs.

Errors

Status error When
400 invalid_expiry expiry is not yyyy-MM-dd.
400 invalid_limit limit is outside 1-20.
403 tier_restricted Caller is below the Growth plan.
404 symbol_not_found No underlying quote is available.
404 expiry_not_found The requested expiry has no eligible active contracts; the body lists available_expiries.
404 no_eligible_contracts No eligible active contract remains.
422 unsupported_symbol Symbol other than SPY or QQQ.
503 invalid_spot No finite positive underlying price.

Notes

  • Contract GEX = gamma × OI × 100 × spot² × 0.01. Gross sums magnitudes; net applies calls positive / puts negative. The sign is a convention, not observed dealer inventory.
  • Eligible contracts use the standard root and a 100-share multiplier with valid strike, OI and gamma. An expiring contract leaves the model at 16:00 ET (valuation_horizon_elapsed) and is excluded from 16:15 ET (last_trading_elapsed); early-close expiries are excluded until their clocks are verified.
  • Historical replay: the same route on historical.flashalpha.com with at= (Alpha+). Date-only at uses 15:55 ET. See the trader-context reference.

GET /v1/exposure/strike-dependency/{symbol}

Attributes the gamma at one exact observed SPY or QQQ strike to its expiries: does the level depend on a single maturity? It reports concentration at that strike, not a support/resistance probability.

Requires Growth plan or higher. Supported symbols: SPY, QQQ.

Parameters

Name In Required Default Description
symbol path yes - SPY or QQQ.
strike query yes - Exact listed strike, positive. The request is never snapped to a nearby strike.

Example

curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/exposure/strike-dependency/SPY?strike=665"

Response 200

Abridged; values are illustrative. Metadata fields (snapshot_id, pricing_basis, data_status, reasons, timestamps, units) match expiry impact.

{
  "symbol": "SPY",
  "underlying_price": 663.42,
  "analysis_type": "strike_attribution",
  "strike": 665,
  "distance_from_spot_pct": 0.24,
  "gross_gex_usd": 610000000,
  "net_gex_usd": 140000000,
  "call_gex_usd": 375000000,
  "put_gex_usd": -235000000,
  "contract_count": 46,
  "by_expiry": [
    { "expiry": "2026-10-16", "valuation_cutoff_at": "2026-10-16T20:00:00Z", "last_trading_at": "2026-10-16T20:15:00Z",
      "last_trading_status": "scheduled_regular_session", "settlement_at": null, "settlement_status": "unknown",
      "calendar_days_to_expiry": 0, "gross_gex_usd": 290000000, "net_gex_usd": 90000000,
      "call_gex_usd": 190000000, "put_gex_usd": -100000000, "contract_count": 2,
      "gross_share_pct": 47.54, "share_of_strike_gross_pct": 47.54 }
  ],
  "dominant_expiry": "2026-10-16",
  "dominant_expiry_share_pct": 47.54
}

Errors

Status error When
400 invalid_strike strike is missing, not a number, or not positive.
403 tier_restricted Caller is below the Growth plan.
404 symbol_not_found No underlying quote is available.
404 strike_not_found The exact strike has no eligible active contracts; the body echoes requested_strike.
422 unsupported_symbol Symbol other than SPY or QQQ.
503 invalid_spot No finite positive underlying price.

GET /v1/exposure/basket

Weighted cross-symbol aggregate of GEX / DEX / VEX / CHEX across up to 50 user-supplied symbols. Equal weights when weights is omitted; otherwise normalised to sum to 1. Symbols with no available data are dropped, surviving weights are re-normalised, and the dropped tickers are reported in missing_symbols.

Requires Growth plan or higher. Targets the "multi-symbol scanners" Growth persona.

Parameters

Name In Required Description
symbols query yes Comma-separated symbols. Max 50, duplicates de-duped.
weights query no Comma-separated; same length as symbols. Defaults to equal weight. Negative values rejected; sum-zero rejected.

Example

curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/exposure/basket?symbols=AAPL,MSFT,NVDA&weights=0.4,0.3,0.3"

Response 200

{
  "as_of": "2026-05-29T15:30:00Z",
  "constituent_count": 3,
  "missing_symbols": [],
  "aggregate": {
    "net_gex": 1640000000,
    "net_dex": -210000000,
    "net_vex":  580000000,
    "net_chex": 420000000
  },
  "constituents": [
    { "symbol": "AAPL", "weight": 0.40, "underlying_price": 210.45, "net_gex":  920000000, "net_dex": -120000000, "net_vex": 340000000, "net_chex": 260000000, "contribution_pct": 42.13, "regime": "positive_gamma" },
    { "symbol": "MSFT", "weight": 0.30, "underlying_price": 432.80, "net_gex":  410000000, "net_dex":  -60000000, "net_vex": 140000000, "net_chex":  90000000, "contribution_pct": 18.77, "regime": "positive_gamma" },
    { "symbol": "NVDA", "weight": 0.30, "underlying_price": 854.20, "net_gex":  310000000, "net_dex":  -30000000, "net_vex": 100000000, "net_chex":  70000000, "contribution_pct": 14.19, "regime": "positive_gamma" }
  ]
}

Response Fields

Field Description
constituent_count Number of symbols that actually contributed (after drops).
missing_symbols[] Symbols requested but with no MarketDataStore data (invalid, illiquid, or still warming).
aggregate.net_* Σ wᵢ × net_{greek}_i after weight renormalisation.
constituents[].weight Renormalised weight applied to this symbol (sums to 1 across the response).
constituents[].contribution_pct |wᵢ × net_gex_i| / Σ |wⱼ × net_gex_j|, expressed 0-100. Uses weighted GEX (not raw |net_gex|) so a tiny-weight, large-GEX symbol doesn't dominate the display when its influence on the aggregate is small.
constituents[].regime positive_gamma when net_gex ≥ 0, else negative_gamma. Note: this is the sign of net GEX, which differs from the per-symbol endpoints - those key regime off spot vs gamma_flip. A basket has no single spot to compare a flip against, so the flip-based definition does not apply here. See Gamma regime vs net GEX.

Errors

Status Description
400 missing_symbols / too_many_symbols / invalid_weights / weight_count_mismatch
403 Requires Growth plan or higher
404 no_data - none of the requested symbols had data

Notes

  • Underlying greeks for each symbol come from the same on-demand poller as /v1/exposure/*. First-touch symbols may briefly surface in missing_symbols while data warms - repeat the call after a moment.
  • The endpoint does not pre-validate index/ETF tier gating per constituent - the rest of the API does that on direct requests.
  • One HTTP call = one quota debit, independent of how many symbols are passed in symbols=.

GET /v1/liquidity/{symbol}

Per-expiry execution score (0-100), ATM bid-ask spread %, OI-weighted spread %, ATM OI depth, plus chain-level OI-weighted score and best/worst expiry. Each expiry carries a label: tight (≥75) / normal (≥50) / wide (≥20) / illiquid (<20).

Requires Growth plan or higher.

Parameters

Name In Required Description
symbol path yes Underlying symbol

Example

curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/liquidity/SPY"

Response 200

{
  "symbol": "SPY",
  "underlying_price": 597.50,
  "as_of": "2026-05-29T15:30:00Z",
  "chain_execution_score": 78,
  "best_expiry": "2026-05-30",
  "worst_expiry": "2027-01-15",
  "thin_expiry_count": 1,
  "expiries": [
    { "expiration": "2026-05-30", "dte": 1,   "atm_spread_pct": 0.45, "weighted_spread_pct": 1.10, "atm_oi": 48200, "execution_score": 86, "label": "tight" },
    { "expiration": "2026-06-18", "dte": 20,  "atm_spread_pct": 0.78, "weighted_spread_pct": 1.95, "atm_oi": 31400, "execution_score": 68, "label": "normal" },
    { "expiration": "2027-01-15", "dte": 231, "atm_spread_pct": null, "weighted_spread_pct": null, "atm_oi":   120, "execution_score":  8, "label": "illiquid" }
  ]
}

Response Fields

Field Description
chain_execution_score OI-weighted average of per-expiry execution_score across the chain (0-100).
best_expiry / worst_expiry Expirations with the highest / lowest execution_score.
thin_expiry_count Number of expiries labelled illiquid.
expiries[].atm_spread_pct Average bid-ask spread % at the strike closest to spot, across the side(s) that quote. null when neither side quotes.
expiries[].weighted_spread_pct OI-weighted bid-ask spread % across contracts in the expiry that both carry OI (> 0) and quote a valid spread (ask > bid > 0). null when no contract satisfies both.
expiries[].atm_oi Sum of call+put OI at the strike closest to spot.
expiries[].execution_score 0-100. Composite: 70% spread-tightness (exp-decay) + 30% ATM-OI depth (5000 contracts saturates). Same formula as /v1/exposure/zero-dte's liquidity.execution_score.
expiries[].label Discrete bucket derived from execution_score per the thresholds above.

Errors

Status Description
403 Requires Growth plan or higher
404 Symbol not found or no options data

Notes

  • The score is heuristic - read the label and direction of the number rather than treating any threshold as a hard tradability rule.
  • Expirations with all-zero OI or only one-sided quotes still land in the response - they typically score 0 and label illiquid.
  • best_expiry is always non-null when at least one expiry exists. When every score ties at 0, the first-ranked row (stable order = first by expiry date) wins by default. Read chain_execution_score alongside it to judge whether "best" actually means tradable.

GET /v1/volatility/skew-term/{symbol}

Skew term structure with the vol-desk naming conventions vol-aware discretionary traders learn from Volland / Wizard-of-Ops content. For each expiry: ATM IV, 25Δ and 10Δ wing IVs, and the three named conventions - skew_25d, risk_reversal_25d, butterfly_25d - plus tail_convexity.

Requires Growth plan or higher.

Parameters

Name In Required Description
symbol path yes Underlying symbol

Example

curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/volatility/skew-term/SPY"

Response 200

{
  "symbol": "SPY",
  "underlying_price": 597.50,
  "as_of": "2026-05-29T15:30:00Z",
  "expiries": [
    {
      "expiry": "2026-05-30", "dte": 1,
      "atm_iv": 12.30,
      "put_25d_iv": 15.80, "call_25d_iv": 11.10,
      "put_10d_iv": 18.40, "call_10d_iv": 10.20,
      "skew_25d": 4.70,
      "risk_reversal_25d": -4.70,
      "butterfly_25d": 1.15,
      "tail_convexity": -0.90
    },
    {
      "expiry": "2026-06-18", "dte": 20,
      "atm_iv": 14.50,
      "put_25d_iv": 17.20, "call_25d_iv": 13.10,
      "put_10d_iv": 20.10, "call_10d_iv": 12.40,
      "skew_25d": 4.10,
      "risk_reversal_25d": -4.10,
      "butterfly_25d": 0.65,
      "tail_convexity": 0.20
    }
  ]
}

Response Fields

Field Definition
atm_iv Average IV (%) of up to four eligible OTM contracts nearest spot for the expiry; same calculation as /v1/volatility/{symbol}'s skew_profiles[], with no SVI fit involved.
put_25d_iv / call_25d_iv IV (%) of the eligible same-side OTM contract nearest the target delta of −0.25 / +0.25; not surface-interpolated.
put_10d_iv / call_10d_iv IV (%) of the eligible same-side OTM contract nearest the target delta of −0.10 / +0.10.
skew_25d put_25d_iv − call_25d_iv. Positive ⇒ put skew dominant.
risk_reversal_25d call_25d_iv − put_25d_iv (= −skew_25d). FX / vol-desk convention.
butterfly_25d (call_25d_iv + put_25d_iv) / 2 − atm_iv. Wing premium over ATM.
tail_convexity (put_10d − put_25d) − (put_25d − atm) - second difference of the put wing. Positive ⇒ steep tail.

Errors

Status Description
403 Requires Growth plan or higher
404 Symbol not found or no options data available

Notes

  • The math is identical to the skew_profiles[] block returned by /v1/volatility/{symbol}; this endpoint just projects to a flatter term-structure shape and adds the risk_reversal_25d / butterfly_25d aliases.
  • Wing selection has no maximum delta-distance tolerance. The same contract can be selected for both the 10-delta and 25-delta targets, producing identical IVs; the field names identify targets, not guaranteed observed deltas.
  • Profiles require eligible put and call wing contracts and an ATM estimate. The calculation filters contract IVs to greater than 1% and less than 300%, applies a vertical-arbitrage filter before selecting contracts, skips expiries with at most 0.002 years remaining, and omits profiles whose ATM or selected 25-delta IV is below 1%.

GET /v1/volatility/realized/{symbol}

Range-based realized (historical) volatility estimators over 10/20/30-day windows. Range estimators exploit the daily high/low/open/close and are 5-8× more statistically efficient than close-to-close. Price history is split-adjusted daily bars from the historical replay archive - coverage is limited to ramped symbols.

Requires Alpha plan or higher.

Parameters

Name In Required Description
symbol path yes Underlying symbol

Example

curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/volatility/realized/AAPL"

Response 200

{
  "symbol": "AAPL",
  "as_of": "2026-06-08T15:30:00Z",
  "underlying_price": 201.50,
  "estimators": {
    "close_to_close":  { "rv10": 18.40, "rv20": 21.20, "rv30": 22.70 },
    "parkinson":       { "rv10": 17.10, "rv20": 19.80, "rv30": 20.40 },
    "garman_klass":    { "rv10": 16.90, "rv20": 19.50, "rv30": 20.10 },
    "rogers_satchell": { "rv10": 17.30, "rv20": 20.10, "rv30": 20.60 },
    "yang_zhang":      { "rv10": 17.00, "rv20": 19.70, "rv30": 20.30 }
  }
}

Response Fields

Field Definition
underlying_price Most-recent daily close from the archive.
estimators.*.rv10 / rv20 / rv30 Annualized realized volatility (percent) over a 10/20/30-bar window, or null (see thresholds below).

Each estimator produces a daily variance over N bars in the window; every window value is then annualized as:

rv = 100 · sqrt(252 · variance)

Daily-variance formulas (O,H,L,C = open/high/low/close per bar; ln = natural log):

Estimator Daily variance
close_to_close sample variance of log-returns ln(Cᵢ/Cᵢ₋₁) (denominator N−1)
parkinson 1 / (4·N·ln(2)) · Σ ln(Hᵢ/Lᵢ)²
garman_klass 1/N · Σ [ 0.5·ln(Hᵢ/Lᵢ)² − (2·ln(2)−1)·ln(Cᵢ/Oᵢ)² ]
rogers_satchell 1/N · Σ [ ln(Hᵢ/Cᵢ)·ln(Hᵢ/Oᵢ) + ln(Lᵢ/Cᵢ)·ln(Lᵢ/Oᵢ) ]
yang_zhang σ²ₒ + k·σ²_c + (1−k)·σ²_RS

For Yang-Zhang: σ²ₒ is the overnight-return variance (ln(Oᵢ/Cᵢ₋₁), mean-removed, denominator N−1), σ²_c the open-to-close variance (ln(Cᵢ/Oᵢ), mean-removed, denominator N−1), σ²_RS the Rogers-Satchell variance above (denominator N), and k = 0.34 / (1.34 + (N+1)/(N−1)).

History thresholds (null vs 404)

  • Fewer than 11 daily bars available → 404 insufficient_history (11 = enough for the 10-bar window plus Yang-Zhang's prior-close seed).
  • 11+ bars but not enough for a longer window → the request succeeds (200) and the unavailable window(s) are null (e.g. 12 bars ⇒ rv10 populated, rv20/rv30 null).

Errors

Status Description
403 tier_restricted - requires Alpha plan or higher
404 insufficient_history - genuine no-coverage (symbol not in the archive) or fewer than 11 daily bars
429 rate_limit_exceeded - the historical archive is rate limiting; retry shortly
502 upstream_error - the historical archive rejected the forwarded request (auth / other 4xx)
503 upstream_unavailable - the historical archive is down or unreachable

Error responses are JSON: { "error": "insufficient_history", "message": "…" } (tier errors additionally include current_plan / required_plan). 404 is reserved for genuine no-coverage / too-few-bars - upstream archive failures surface as 429/502/503, never 404. See Authentication for 401 and Rate Limits & Plans for caller-quota 429.


GET /v1/volatility/forecast/{symbol}

Conditional volatility forecasts from daily price history: EWMA (RiskMetrics, λ=0.94), HAR-RV (Corsi), and GARCH(1,1) fitted by maximum likelihood with selectable Gaussian or Student-t innovations. Price history is split-adjusted daily bars from the historical replay archive - coverage is limited to ramped symbols.

The realized_models block adds five models fitted on 5-minute realized variance and 30-day implied vol: HAR-RV on 5-minute data, HAR-IV, HARQ, Realized GARCH and HEAVY. It is refreshed once a day, after each session reaches the archive.

Requires Alpha plan or higher.

Parameters

Name In Required Description
symbol path yes Underlying symbol
dist query no GARCH innovation distribution: student_t (default) or gaussian

Example

curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/volatility/forecast/AAPL?dist=student_t"

Response 200

{
  "symbol": "AAPL",
  "as_of": "2026-06-08T15:30:00Z",
  "ewma":   { "lambda": 0.94, "vol_annualized": 19.60, "next_day_forecast": 19.60 },
  "har_rv": {
    "vol_annualized": 20.30,
    "components": { "daily": 18.40, "weekly": 21.20, "monthly": 22.70 },
    "next_day_forecast": 20.10
  },
  "garch": {
    "model": "garch_1_1",
    "distribution": "student_t",
    "params": { "omega": 0.000003, "alpha": 0.078, "beta": 0.905, "dof": 6.20 },
    "persistence": 0.9830,
    "long_run_vol_annualized": 21.40,
    "half_life_days": 40.2,
    "converged": true,
    "forecast": [
      { "horizon_days": 1,  "vol_annualized": 20.10 },
      { "horizon_days": 5,  "vol_annualized": 20.60 },
      { "horizon_days": 21, "vol_annualized": 21.00 }
    ]
  },
  "realized_models": {
    "as_of_session": "2026-10-30",
    "computed_at": "2026-10-31T04:41:12Z",
    "observations": 1000,
    "iv30": 28.55,
    "target": "mean_daily_close_to_close_variance_next_h_sessions",
    "models": [
      {
        "model": "har_rv_5m",
        "converged": true,
        "forecast": [
          { "horizon_days": 1, "vol_annualized": 23.87 },
          { "horizon_days": 5, "vol_annualized": 25.06 },
          { "horizon_days": 21, "vol_annualized": 26.2 }
        ]
      },
      {
        "model": "har_iv",
        "converged": true,
        "forecast": [
          { "horizon_days": 1, "vol_annualized": 32.4 },
          { "horizon_days": 5, "vol_annualized": 32.1 },
          { "horizon_days": 21, "vol_annualized": 30.24 }
        ]
      },
      {
        "model": "harq",
        "converged": true,
        "forecast": [
          { "horizon_days": 1, "vol_annualized": 24.01 },
          { "horizon_days": 5, "vol_annualized": 25.34 },
          { "horizon_days": 21, "vol_annualized": 26.48 }
        ]
      },
      {
        "model": "realized_garch",
        "converged": true,
        "forecast": [
          { "horizon_days": 1, "vol_annualized": 22.9 },
          { "horizon_days": 5, "vol_annualized": 23.53 },
          { "horizon_days": 21, "vol_annualized": 24.33 }
        ]
      },
      {
        "model": "heavy",
        "converged": true,
        "forecast": [
          { "horizon_days": 1, "vol_annualized": 22.21 },
          { "horizon_days": 5, "vol_annualized": 23.26 },
          { "horizon_days": 21, "vol_annualized": 24.86 }
        ],
        "params": {"omega": 3.5e-05, "alpha": 0.726414, "beta": 0.373056, "omega_r": 2.8e-05, "alpha_r": 0.505661, "beta_r": 0.341536}
      }
    ]
  }
}

Response Fields

Field Definition
ewma.vol_annualized Current EWMA conditional vol (%); next_day_forecast equals it (random walk in variance).
har_rv.components Daily / weekly (5d) / monthly (22d) realized-vol components, annualized %.
garch.params Fitted ω, α, β (and dof for Student-t).
garch.persistence α + β. Must be < 1 for a stationary fit.
garch.long_run_vol_annualized Annualized unconditional volatility: 100·sqrt(252 · ω/(1−α−β)) (the ω/(1−α−β) term is the unconditional daily variance); null for a non-stationary fit.
garch.half_life_days Half-life of a vol shock: ln(0.5)/ln(α+β).
garch.converged false when the MLE fails to converge or the fit is non-stationary; forecast is then null.
garch.forecast Terminal conditional vol (annualized %) at 1/5/21-day horizons, reverting toward the long-run level.
realized_models.as_of_session Forecasts use data through this session's close, normally the previous trading day.
realized_models.computed_at When this block was fitted (UTC).
realized_models.observations Sessions in the fit window, up to 1,000.
realized_models.iv30 30-day ATM implied vol (%) at the same close, for comparing with the forecasts.
realized_models.target What every forecast measures: mean daily close-to-close variance over the next h sessions, as annualized vol (%).
realized_models.models[].model har_rv_5m: HAR on 5-minute realized variance plus the overnight return. har_iv: adds 30-day implied variance. harq: lowers the daily weight when realized variance is noisy; a forecast outside the range of the fitted targets resets to their mean, as in the paper. realized_garch: log-linear Realized GARCH(1,1). heavy: HEAVY, two equations driven by intraday realized variance.
realized_models.models[].converged false when the fit failed or an input was missing; forecast is then omitted and note says why.
realized_models.models[].forecast Annualized vol (%) of the mean daily variance over the next 1, 5 and 21 sessions.
realized_models.models[].params Fitted parameters. The HAR family reports one set per horizon, for example h21_daily.

All annualized vols follow 100·sqrt(252·variance) from daily-return variance units.

Notes

  • har_rv uses squared daily returns as the realized-variance proxy; har_rv_5m uses 5-minute data.
  • realized_models is available for symbols with at least 300 archived sessions.
  • For forecasts as of any past session, call GET /v1/volatility/forecast/{symbol}?at=... on the historical API.
  • GARCH is fitted by maximum likelihood via a deterministic (seeded) Nelder-Mead optimizer, so results are reproducible.
  • A non-convergent or non-stationary GARCH fit is not an error: it returns 200 with converged: false and a null forecast. An unavailable archive symbol returns 404, not 500.

Errors

Status Description
400 invalid_dist - dist must be student_t (default) or gaussian
403 tier_restricted - requires Alpha plan or higher
404 insufficient_history - genuine no-coverage (symbol not in the archive) or fewer than 30 daily bars
429 rate_limit_exceeded - the historical archive is rate limiting; retry shortly
502 upstream_error - the historical archive rejected the forwarded request (auth / other 4xx)
503 upstream_unavailable - the historical archive is down or unreachable

Error responses are JSON: { "error": "invalid_dist", "message": "…" }. 404 is reserved for genuine no-coverage - upstream archive failures surface as 429/502/503, never 404. See Authentication for 401 and Rate Limits & Plans for caller-quota 429.


GET /v1/dispersion

Demeterfi-Derman-Kani implied correlation between an index and a user-supplied basket of constituents, paired with a 1-factor realized correlation over a configurable lookback. Returns correlation_premium = implied − realized and per-constituent contribution to basket vol (sorted descending).

Requires Alpha plan or higher. Vol-arb / dispersion-trading math - the "quants, vol desks, systematic researchers" Alpha persona.

Parameters

Name In Required Default Description
index query yes - Index symbol (e.g. SPX, NDX, RUT). Must have data loaded.
symbols query yes - Comma-separated constituent symbols (max 50, de-duped).
weights query no equal Comma-separated; non-negative; normalised to sum 1.
horizon_days query no 20 Lookback window in days for realized correlation. Clamped to [5, 252].

Example

curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/dispersion?index=SPX&symbols=AAPL,MSFT,NVDA&weights=0.5,0.3,0.2&horizon_days=20"

Response 200

{
  "as_of": "2026-05-29T15:30:00Z",
  "index": "SPX",
  "constituent_count": 3,
  "missing_symbols": [],
  "horizon_days": 20,
  "implied_correlation": 0.412,
  "realized_correlation": 0.385,
  "correlation_premium": 0.027,
  "implied_vol_index": 0.138,
  "implied_vol_basket": 0.224,
  "top_contributors": [
    { "symbol": "NVDA", "weight": 0.20, "iv": 0.46, "contribution_to_basket_vol": 0.092 },
    { "symbol": "AAPL", "weight": 0.50, "iv": 0.18, "contribution_to_basket_vol": 0.090 },
    { "symbol": "MSFT", "weight": 0.30, "iv": 0.14, "contribution_to_basket_vol": 0.042 }
  ]
}

Response Fields

Field Description
constituent_count Number of constituents that survived to the calculator (after drops in missing_symbols).
implied_correlation ρ = (σ²_idx − Σ wᵢ² σᵢ²) / ((Σ wᵢ σᵢ)² − Σ wᵢ² σᵢ²). null when the denominator is non-positive (degenerate basket - e.g. single constituent or all-zero vols).
realized_correlation Basket-weight-weighted average of Pearson(log_returns(constituent), log_returns(index)) over the horizon. Weights are the supplied basket weights (renormalised across survivors), not OI weights. null when no constituent's Pearson is computable (flat / empty closes).
correlation_premium implied − realized. null when either side is null. Positive ⇒ market pricing in more correlation than realized.
implied_vol_index The index's ATM IV (decimal, e.g. 0.138 = 13.8%).
implied_vol_basket Σ wᵢ σᵢ after weight renormalisation across surviving constituents.
top_contributors[] Sorted descending by contribution_to_basket_vol = wᵢ × σᵢ. Σ contribution == implied_vol_basket by construction.
missing_symbols[] Constituents requested but dropped at controller time (no quote, no greeks snapshots, or no computable ATM IV). Constituents whose historical-close fetch returns empty are kept - they just contribute zero to the realized term. Surviving weights are renormalised internally.

Errors

Status Description
400 missing_index / missing_symbols / too_many_symbols / invalid_weights / weight_count_mismatch
403 Requires Alpha plan or higher
404 index_not_found, no_index_iv, or no_data (no constituent survived)

Notes

  • Correlation fields serialise as JSON null (not NaN) when the underlying math is undefined. Treat null as "not computable from this basket / window."
  • Index IV comes from the same StockSummaryBuilder.ComputeAtmIvFromSnapshots path as /v1/stock/{symbol}/summary - the index must already have options/greeks loaded.
  • The 1-factor realized correlation here ≠ the true pairwise correlation matrix. It's the weighted average of each constituent's correlation to the index, which is what's directly comparable to the implied figure under a 1-factor assumption.
  • This endpoint deliberately does not ship hardcoded SPX/NDX weight tables - you supply the basket. Future work may add saved baskets keyed by index.

GET /v1/exposure/oi-diff/{symbol}

Day-over-day open-interest deltas - fills the gap behind /v1/exposure/narrative's top_oi_changes: [] placeholder. Per-contract deltas (today's OI minus prior trading day's OI), top-N changes sorted by absolute magnitude, and call/put aggregate totals.

Requires Growth plan or higher.

Parameters

Name In Required Default Description
symbol path yes - Underlying symbol
topN query no 10 Clamped to [1, 100]

Example

curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/exposure/oi-diff/SPY?topN=5"

Response 200

{
  "symbol": "SPY",
  "underlying_price": 597.50,
  "as_of": "2026-05-30T15:30:00Z",
  "prior_snapshot_available": true,
  "total_call_oi_change": 124000,
  "total_put_oi_change": -38000,
  "top_oi_changes": [
    { "strike": 600.0, "type": "C", "expiry": "2026-06-18", "today_oi": 35000, "prior_oi": 30000, "oi_change": 5000 }
  ]
}

Response Fields

Field Description
prior_snapshot_available false when no prior-day OI data has been written to the source table yet - total_*_oi_change are then 0 and top_oi_changes is empty.
total_call_oi_change Sum of per-contract deltas across all call contracts that have a prior-day match.
total_put_oi_change Same for puts.
top_oi_changes[] Sorted by |oi_change| descending. Each row carries the contract's strike, type, expiry, today's OI, prior OI, and the signed delta.

Notes

  • Contracts present today but with no prior-day entry are excluded from the top-N (no delta is computable).
  • The prior-day OI lookup currently points at a stub source - once the ExposureStrikeSnapshots writer pipeline ships, the flag flips to true and deltas populate without any client change.
  • For point-in-time historical OI diffs, use historical.flashalpha.com with two ?at= calls (today's date and prior trading day).

GET /v1/volatility/spot-vol-correlation/{symbol}

Daily Pearson correlation between spot log-returns and first-differences of ATM implied vol. Equity indices typically run strongly negative (vol spikes on spot down moves). Computed over 20-day and 60-day windows from the DailyVrpSnapshots table populated nightly by the VRP backfill job.

Requires Growth plan or higher.

Parameters

Name In Required Description
symbol path yes Underlying symbol

Example

curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/volatility/spot-vol-correlation/SPY"

Response 200

{
  "symbol": "SPY",
  "as_of": "2026-05-30T15:30:00Z",
  "spot_vol_correlation_20d": -0.62,
  "spot_vol_correlation_60d": -0.58,
  "data_points_20d": 20,
  "data_points_60d": 60,
  "daily_history_points": 143,
  "interpretation": "Strongly inverse - vol spikes on spot down moves (typical equity regime)."
}

Response Fields

Field Description
spot_vol_correlation_20d Pearson(log_returns(spot), iv_deltas) over the last 20 daily snapshots. null when either series has zero variance or fewer than 20 points exist.
spot_vol_correlation_60d Same over the last 60. null when history is too short.
data_points_* The actual count of returns/deltas behind each correlation (matches the window). 0 when null.
daily_history_points Usable daily return pairs available for the symbol, regardless of whether either window computed. Use this to tell "still accruing history" apart from "no history at all" - data_points_* are 0 in both cases. 20d needs 21 pairs, 60d needs 61.
interpretation Label keyed off the shorter window (preferred); falls back to 60d when 20d is null. Bands: < -0.5 strongly inverse, < -0.2 moderately inverse, < 0.2 decoupled, < 0.5 mildly positive, ≥ 0.5 strongly positive.

Errors

Status Description
404 insufficient_history Fewer than 3 daily snapshots exist for the symbol.

Notes

  • Daily IV here is the ATM IV stored in DailyVrpSnapshots (computed BSM from EOD option snapshots; same path as /v1/vrp/{symbol}/history).
  • Inputs require both a non-null AtmIv and a positive UnderlyingPrice on each row.

GET /v1/macro/vix-state

Wizard-of-Ops "overvixing / undervixing" regime label for the index complex - compares spot VIX against SPX 20-day realized vol.

Requires Growth plan or higher.

Parameters

(none)

Example

curl -H "X-Api-Key: YOUR_API_KEY" https://lab.flashalpha.com/v1/macro/vix-state

Response 200

{
  "as_of": "2026-05-30T15:30:00Z",
  "vix": 18.4,
  "spx_rv_20d": 11.2,
  "spread": 7.2,
  "ratio": 1.6428,
  "state": "overvixing",
  "interpretation": "VIX trades 7.2pp above SPX 20d realized - premium rich, favours short-vol setups."
}

Response Fields

Field Description
vix VIX spot from live MarketDataStore.StockQuotes["VIX"].Mid.
spx_rv_20d SPX 20-day annualised realized vol (%). Preferred source is the latest DailyVrpSnapshots row for SPX; falls back to live computation from Yahoo closes when no snapshot exists.
spread vix − spx_rv_20d (vol points).
ratio vix / spx_rv_20d. null when spx_rv_20d == 0.
state overvixing (spread ≥ 5), undervixing (spread ≤ 0), neutral (0 < spread < 5).

Errors

Status Description
404 no_vix VIX quote unavailable in the live store.
404 no_rv_data No SPX history sufficient to compute RV20d (neither snapshot nor live closes).

Notes

  • Thresholds (5 / 0 vol points) are calibrated against typical regimes: ~4pp average premium in quiet markets; ~0pp or inverted in crashes when RV catches up to implied.
  • This is a single-snapshot classifier - for time-series of the state, build on top of /v1/vrp/{symbol}/history for SPX.

GET /v1/universe

Curated tier-1 / tier-2 symbol directory - the symbols the screener background loop keeps pre-warmed in MarketDataStore. Public - no auth required, so retail evaluators can introspect coverage before subscribing.

Parameters

Name In Required Default Description
sort query no tier tier (tier-1 first, then tier-2; curator order preserved within each) or symbol (alphabetical).
limit query no 200 Clamped to [1, 1000].

Example

curl https://lab.flashalpha.com/v1/universe?sort=symbol&limit=20

Response 200

{
  "as_of": "2026-05-30T15:30:00Z",
  "count": 252,
  "returned": 20,
  "limit": 20,
  "sort": "symbol",
  "symbols": [
    { "symbol": "AAPL", "tier": 1, "is_pre_warmed": true, "has_0dte": false },
    { "symbol": "IWM",  "tier": 1, "is_pre_warmed": true, "has_0dte": true },
    { "symbol": "SPY",  "tier": 1, "is_pre_warmed": true, "has_0dte": true }
  ]
}

Response Fields

Field Description
count Total universe size (tier-1 ∪ tier-2, deduplicated).
returned min(count, limit).
sort Echoes the effective sort (unknown values fall back to tier).
symbols[].tier 1 = high-traffic loop (fast refresh); 2 = remaining curated symbols.
symbols[].is_pre_warmed Currently always true - the screener loop keeps every universe member populated.
symbols[].has_0dte true when the underlying lists same-day-expiry (0DTE) options every trading day - currently SPX/SPXW, SPY, QQQ, IWM. false symbols still have weekly/monthly expirations; the flag is specifically about daily same-day availability.

Notes

  • Symbols outside the universe still work on any authenticated endpoint (they fetch on-demand). The universe is "guaranteed warm," not "exhaustive."
  • On the Free plan, requests for off-universe stocks 403 with symbol_not_in_free_universe (gex, levels, stockquote) - this endpoint lets evaluators see exactly which symbols are reachable on Free.
  • 0DTE routing without trial-and-error: call /v1/universe once and filter has_0dte == true to get the full daily-expiry basket up front, instead of probing /v1/exposure/zero-dte/{symbol} per symbol and inferring exclusion from the no_zero_dte flag. IWM has carried every-weekday 0DTE since Nov 2023, so it is in the basket.

GET /v1/flow/options/{symbol}/dealer-premium

Full-tape Net Dealer Premium roll-up over a configurable window. Sums each side of the customer-flow tape weighted by VWAP per minute bucket. Distinct from /v1/flow/signals/{symbol}/summary, which only rolls up block-sized signal prints.

Requires Alpha plan or higher.

Parameters

Name In Required Default Description
symbol path yes - Underlying symbol
windowMinutes query no 240 Clamped to [1, 10080]
expiry query no all Filter to a single expiry, yyyy-MM-dd

Example

curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/flow/options/SPY/dealer-premium?windowMinutes=240"

Response 200

{
  "symbol": "SPY",
  "as_of": "2026-05-30T15:30:00Z",
  "window_minutes": 240,
  "expiry": null,
  "dealer_buy_premium": 12450000.0,
  "dealer_write_premium": 18200000.0,
  "net_dealer_premium": -5750000.0,
  "total_premium": 30650000.0,
  "trade_count": 18420,
  "bucket_count": 240
}

Response Fields

Field Description
dealer_buy_premium Σ (sellVolume × vwap × 100) across minute buckets - dealer is the BUYER when customer hits the bid.
dealer_write_premium Σ (buyVolume × vwap × 100) - dealer is the WRITER when customer lifts the ask.
net_dealer_premium dealer_buy_premium − dealer_write_premium. Positive ⇒ dealers net long premium in the window.
total_premium dealer_buy_premium + dealer_write_premium - total directional premium traded (excludes mid prints).
trade_count Sum of tradeCount across buckets.
bucket_count Number of minute buckets that fed the aggregate.

Errors

Status Description
400 invalid_expiry Bad expiry format.
502 upstream_unavailable Flow ingest didn't respond.
502 upstream_invalid Flow ingest response couldn't be parsed.

Notes

  • Per-bucket premium uses the bucket's VWAP - a minute-resolution approximation. For trade-by-trade exact premium, drive from /v1/flow/options/{symbol}/recent.
  • Buckets with vwap = 0 (no usable price reference) contribute zero - defensive against corrupt input.
  • Mid prints (midVolume) aren't attributed to either dealer side - they're customer-customer crosses where the dealer is sometimes the matching engine, not the counterparty.

Flow vs Exposure

The /v1/exposure/* endpoints compute against the morning-broadcast settled OI - stable through the session, the same value the OPRA tape reports. Use them for "what is dealer positioning right now according to the books."

The simulation-aware /v1/flow/{levels,pin-risk,summary,gex,dex,dealer-risk,oi,live} endpoints compute against effective OI - settled value plus an intraday simulator estimate of position change driven by today's option flow. The simulator applies a confidence weight (currently 0.43) to side-classified buy/sell volume to estimate how many contracts opened or closed today. Use them for "what would dealer positioning look like if today's flow really did add/remove the positions our model thinks it did."

The two surfaces are independent - /v1/exposure/* outputs are NOT affected by anything under /v1/flow/*. Field names disambiguate cleanly: gex vs live_gex, net_gex vs live_net_gex, etc.

Shared simulator state

Every simulation-aware flow analytics response uses the same per-contract OI state:

Field Meaning
official_oi Last OPRA-broadcast settled value - stable through the session
intraday_oi_delta Simulator's signed estimate of contracts opened (+) or closed (−) since open
oi_delta_confidence Per-trade confidence weight (currently 0.43 - fraction of side-classified volume assumed to open new positions)
simulated_oi official_oi + intraday_oi_delta (unclamped - may be negative on overshoot, surfaces simulator diagnostic state)
effective_oi max(0, simulated_oi) per contract - analytics-safe input fed into GEX/DEX/walls/etc.

flow_direction (returned by /summary and /dealer-risk) classifies the change in dealer exposure between settled and live:

Label Meaning
no_flow Simulator has reported zero per-contract movement on every contract - contracts_with_flow is 0. Distinct from neutral so consumers can tell "no flow yet" from "flow exists but is small"
neutral Flow exists but |shift| is under the 5% threshold
amplifying Same-sign net GEX, magnitude grew (dealers more exposed)
dampening Same-sign net GEX, magnitude shrank (positions resolving)
regime_flip Sign change (positive ↔ negative gamma regime) OR regime created from zero baseline

Expiry filter

Every /v1/flow/* endpoint accepts an optional ?expiry=YYYY-MM-DD query parameter that filters the chain to a single expiry before running the math. Omit the parameter to aggregate across all expiries (the default). Invalid formats return 400 Bad Request with {"error":"invalid_expiry"}; an expiry with no contracts in the chain returns 404 Not Found.

curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/flow/summary/SPY?expiry=2026-05-15"

GET /v1/flow/levels/{symbol}

With default polarity=convention, live_gamma_flip is a nullable repriced zero-gamma boundary, accompanied by gamma_flip_status (available or a withholding reason). The same convention applies to flow GEX and live bundle responses. polarity=flow is an explicit exception: its flip remains a crossing of the dealer-position-signed per-strike profile and does not carry the convention repricing certificate. See gamma flip determination.

Live (simulation-aware) versions of the key dealer-defended price anchors: gamma flip, call wall, put wall, and max pain. Same shape as /v1/exposure/levels/ but computed using effective OI instead of settled.

Requires Growth plan or higher.

Parameters

Name In Required Description
symbol path yes Underlying symbol
expiry query no Filter the chain to a single expiry, YYYY-MM-DD. Omit to aggregate across all expiries
polarity query no convention (default) or flow. flow returns dealer-position-signed walls + gamma flip (see /v1/flow/gex for the signing model). live_max_pain is OI-based and unchanged.

Example

curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/flow/levels/SPY?expiry=2026-05-15"

Response 200

{
  "symbol": "SPY",
  "as_of": "2026-05-12T16:30:45Z",
  "underlying_price": 597.50,
  "expiry": "2026-05-15",
  "live_gamma_flip": 595.50,
  "live_call_wall": 600,
  "live_put_wall": 590,
  "live_max_pain": 595
}

Errors

Status Description
400 Invalid expiry format - must be YYYY-MM-DD. Response body: {"error":"invalid_expiry","message":"..."}
403 Growth plan or higher required
404 Symbol not found, no greeks data, or no contracts on the requested expiry

GET /v1/flow/pin-risk/{symbol}

Live pin-risk score (0-100) with the gamma magnet strike and a four-component breakdown (OI concentration, magnet proximity to spot, time-to-close, gamma magnitude). The current implementation uses the live full-chain GEX profile and the nearest non-past expiry for the time-to-close component. Score weights: 30% OI + 25% proximity + 25% time + 20% gamma.

Requires Growth plan or higher.

Parameters

Name In Required Description
symbol path yes Underlying symbol
expiry query no Filter the chain to a single expiry, YYYY-MM-DD. Omit to aggregate across all expiries. When supplied, time_to_close_hours is measured to that expiry

Example

curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/flow/pin-risk/SPY?expiry=2026-05-15"

Response 200

{
  "symbol": "SPY",
  "as_of": "2026-05-12T16:30:45Z",
  "underlying_price": 597.50,
  "expiry": "2026-05-15",
  "live_pin_risk": 67,
  "magnet_strike": 597.0,
  "distance_to_magnet_pct": 0.084,
  "time_to_close_hours": 1.234,
  "breakdown": {
    "oi_score": 78,
    "proximity_score": 92,
    "time_score": 45,
    "gamma_score": 60
  }
}

Errors

Status Description
400 Invalid expiry format - must be YYYY-MM-DD. Response body: {"error":"invalid_expiry","message":"..."}
403 Growth plan or higher required
404 Symbol not found, no greeks data, or no contracts on the requested expiry

Notes

  • magnet_strike is the strike with the largest |NetGex| in the live profile
  • On a perfectly-balanced chain where every strike's NetGex is zero, the magnet selection is order-dependent - flagged here for completeness; very rare in practice
  • Filtering by expiry is the natural way to compute a 0DTE-only pin score: pass today's date

GET /v1/flow/summary/{symbol}

At-a-glance flow card - designed to be cheap to call across a watchlist. Returns the headline direction label, the simulator's aggregate intraday delta, and the live GEX number with its percent shift from settled.

Requires Growth plan or higher.

Parameters

Name In Required Description
symbol path yes Underlying symbol
expiry query no Filter the chain to a single expiry, YYYY-MM-DD. Omit to aggregate across all expiries

Example

curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/flow/summary/SPY?expiry=2026-05-15"

Response 200

{
  "symbol": "SPY",
  "as_of": "2026-05-12T16:30:45Z",
  "underlying_price": 597.50,
  "expiry": "2026-05-15",
  "flow_direction": "amplifying",
  "intraday_oi_delta": 12450,
  "contracts_with_flow": 1842,
  "contracts_total": 4586,
  "live_gex": 12500000000,
  "flow_gex_pct_shift": 0.067
}

Errors

Status Description
400 Invalid expiry format - must be YYYY-MM-DD. Response body: {"error":"invalid_expiry","message":"..."}
403 Growth plan or higher required
404 Symbol not found, no greeks data, or no contracts on the requested expiry

Notes

  • flow_gex_pct_shift is null when settled GEX is zero and live GEX is non-zero (mathematically undefined - regime created from no baseline). When both are zero, the value is 0
  • flow_direction enum: no_flow / neutral / amplifying / dampening / regime_flip
  • expiry echoes the filter param back (or null when the request omitted it); contracts_total reflects the filtered chain when present

GET /v1/flow/gex/{symbol}

Full live (simulation-aware) GEX surface - same shape as /v1/exposure/gex/ but every per-strike call_oi/put_oi reflects the simulator's effective OI estimate, and the aggregate live_net_gex differs from net_gex by the flow contribution.

Requires Growth plan or higher.

Parameters

Name In Required Description
symbol path yes Underlying symbol
expiry query no Filter to a single expiry, YYYY-MM-DD. Omit for the full-chain GEX surface. Useful for term-structure analysis when called per expiry
polarity query no convention (default) or flow. convention is the fixed calls-positive/puts-negative dealer-gamma model applied to settled OI - the structural view (unchanged). flow signs each strike's gamma by the dealer inventory change inferred from session volume classified with a Lee-Ready-style NBBO quote rule (dealer_pos = sells − buys), so net_gex > 0 = session flow left dealers longer gamma = stabilising. Not an absolute dealer book: dealer_pos accumulates from zero at the 09:30 ET open, so a negative flow reading against a positive convention reading means today's tape is pushing against the structural regime, not that the regime has reversed. Alpha+ only.

Example

curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/flow/gex/SPY?expiry=2026-05-15"

# Flow-signed (dealer-position) gamma for the 0DTE pin regime:
curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/flow/gex/SPY?expiry=2026-05-15&polarity=flow"

polarity=flow returns the same fields with dealer-position-signed values, plus a top-level "polarity": "flow" and per-strike call_net_customer / put_net_customer (= buys − sells) and call_dealer_pos / put_dealer_pos (= −net_customer) diagnostics. Untraded strikes contribute 0. It does not use OI for the sign, nor the 0.43 opening-fraction constant. Available live and historically via ?at= (backtestable where the classified trade tape is covered).

Structural vs flow. These are two independent views of the same chain, not a right and a wrong answer:

polarity=convention polarity=flow
Position source Settled open interest Session volume classified by aggressor
Sign source Fixed convention (calls +, puts −) dealer_pos = sells − buys
Reads as The structural gamma map carried into today What today's tape is doing to it
Window Prior-night OI snapshot Cumulative from 09:30 ET, starting at zero

When the two agree, session flow is reinforcing the structural regime. When they disagree, intraday flow is pushing against it - which is information about the structure being challenged, not proof that the structural sign is wrong. A structural +$8bn against −$1bn of session transfer is still net long gamma.

Classification and its limits. Trades are tagged buyer- or seller-initiated with a quote rule against the concurrent NBBO (Lee-Ready-style): prints at or near the ask are buys, at or near the bid are sells, and midpoint prints with no clear aggressor are excluded rather than guessed. The passive side is attributed to liquidity providers. OPRA carries no participant identity, so FlashAlpha does not - and cannot from this feed - distinguish market-maker from retail flow; "customer" throughout means "the aggressing side".

flow_coverage accompanies every polarity=flow response so an all-zero profile is never ambiguous:

Field Meaning
contracts_total Distinct contract identities in the chain slice
contracts_with_flow Identities with at least one classified print this session
twin_contracts_merged Contracts whose flow could not be attributed to a specific OPRA root (AM/PM twins sharing an expiry - SPX third Fridays today). 0 for every ordinary symbol
flow_source_ok false only when the classified-flow source could not be read: unreachable, or a response that is not a valid flow envelope (wrong symbol echoed, missing contracts collection, an error payload). A response that echoes the requested symbol with an explicit empty contracts list is a quiet tape and reports true. A zero live_net_gex with flow_source_ok: false means unavailable, not flat

Response 200

{
  "symbol": "SPY",
  "as_of": "2026-05-12T16:30:45Z",
  "underlying_price": 597.50,
  "expiry": "2026-05-15",
  "live_net_gex": 12500000000,
  "live_net_gex_label": "positive",
  "live_gamma_flip": 595.50,
  "strikes": [
    {
      "strike": 595.0,
      "call_gex": 145000000,
      "put_gex": -89000000,
      "net_gex": 56000000,
      "call_oi": 15820,
      "put_oi": 12340,
      "call_volume": 2150,
      "put_volume": 1890,
      "call_oi_change": null,
      "put_oi_change": null
    }
  ]
}

Errors

Status Description
400 Invalid expiry format - must be YYYY-MM-DD. Response body: {"error":"invalid_expiry","message":"..."}
403 Growth plan or higher required
404 Symbol not found, no greeks data, or no contracts on the requested expiry

GET /v1/flow/dex/{symbol}

Full live (simulation-aware) DEX surface - same shape as /v1/exposure/dex/ but computed using effective OI.

Requires Growth plan or higher.

Parameters

Name In Required Description
symbol path yes Underlying symbol
expiry query no Filter to a single expiry, YYYY-MM-DD. Omit for the full-chain DEX surface

Example

curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/flow/dex/SPY?expiry=2026-05-15"

Response 200

{
  "symbol": "SPY",
  "as_of": "2026-05-12T16:30:45Z",
  "underlying_price": 597.50,
  "expiry": "2026-05-15",
  "live_net_dex": -450000000,
  "strikes": [
    {
      "strike": 595.0,
      "call_dex": 8500000,
      "put_dex": -7200000,
      "net_dex": 1300000
    }
  ]
}

Errors

Status Description
400 Invalid expiry format - must be YYYY-MM-DD. Response body: {"error":"invalid_expiry","message":"..."}
403 Growth plan or higher required
404 Symbol not found, no greeks data, or no contracts on the requested expiry

GET /v1/flow/dealer-risk/{symbol}

Composite settled-vs-live shift metric. Reports both the settled-OI and live-OI versions of net GEX/DEX, the absolute and percent shift, the direction classifier, and a human-readable description. The primary signal for "how much has dealer positioning moved since the open?"

Requires Growth plan or higher.

Parameters

Name In Required Description
symbol path yes Underlying symbol
expiry query no Filter to a single expiry, YYYY-MM-DD. Omit to aggregate across all expiries. Use this to compare flow impact on a specific cycle (e.g., monthly OPEX) versus the full book

Example

curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/flow/dealer-risk/SPY?expiry=2026-05-15"

Response 200

{
  "symbol": "SPY",
  "as_of": "2026-05-12T16:30:45Z",
  "underlying_price": 597.50,
  "expiry": "2026-05-15",
  "settled_net_gex": 11700000000,
  "live_net_gex": 12500000000,
  "flow_gex_adjustment": 800000000,
  "flow_gex_pct_shift": 0.068,
  "settled_net_dex": -425000000,
  "live_net_dex": -450000000,
  "flow_dex_adjustment": -25000000,
  "flow_dex_pct_shift": 0.059,
  "total_abs_delta_contracts": 47820,
  "contracts_with_flow": 1842,
  "flow_direction": "amplifying",
  "description": "Flow has amplified dealer GEX by 6.8% since open. 1,842 contracts saw 47,820 contract-units of repositioning."
}

Errors

Status Description
400 Invalid expiry format - must be YYYY-MM-DD. Response body: {"error":"invalid_expiry","message":"..."}
403 Growth plan or higher required
404 Symbol not found, no greeks data, or no contracts on the requested expiry

Notes

  • flow_gex_pct_shift and flow_dex_pct_shift can be null when the corresponding settled value is exactly zero and live is non-zero (undefined ratio - regime created from no baseline). When both are zero, the value is 0
  • flow_direction enum: no_flow / neutral / amplifying / dampening / regime_flip
  • total_abs_delta_contracts sums |effective - settled| per contract; useful for sizing the breadth of repositioning

GET /v1/flow/oi/{symbol}

Raw OI simulator state - the model inputs other endpoints derive from. Surfaces all five named OI fields plus the confidence weight and contract counts. Aimed at quants, systematic researchers, and SaaS builders consuming the simulator output directly.

Requires Alpha plan or higher.

Parameters

Name In Required Description
symbol path yes Underlying symbol
expiry query no Filter to a single expiry, YYYY-MM-DD. Omit for the full-chain OI aggregate. Useful for per-expiry positioning research

Example

curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/flow/oi/SPY?expiry=2026-05-15"

Response 200

{
  "symbol": "SPY",
  "as_of": "2026-05-12T16:30:45Z",
  "expiry": "2026-05-15",
  "official_oi": 16222147,
  "simulated_oi": 16234597,
  "intraday_oi_delta": 12450,
  "oi_delta_confidence": 0.43,
  "effective_oi": 16234597,
  "contracts_total": 4586,
  "contracts_with_flow": 1842
}

Errors

Status Description
400 Invalid expiry format - must be YYYY-MM-DD. Response body: {"error":"invalid_expiry","message":"..."}
403 Alpha plan or higher required
404 Symbol not found, no greeks data, or no contracts on the requested expiry

Notes

  • official_oi is the morning-broadcast OPRA settled value - won't change until next morning's broadcast
  • simulated_oi is unclamped (official + intraday_delta) - may be negative on aggregate if the simulator overshoots zero on individual contracts. Surfaces the simulator's true intent for diagnostic visibility
  • effective_oi clamps each contract's value to ≥ 0 before summing - this is what /v1/flow/gex/, /dex/, etc. consume
  • oi_delta_confidence is the per-trade weight applied to side-classified volume (currently 0.43, calibrated against EOD residuals)

GET /v1/flow/live/{symbol}

Convenience bundle for headline flow analytics. It materialises the live snapshot list and per-strike profiles once, then returns the top-level OI state, live levels, live GEX/DEX totals, pin-risk score, and dealer-risk summary. It does not include the full per-strike strikes arrays from /gex or /dex, or the detailed pin-risk breakdown from /pin-risk.

Requires Alpha plan or higher.

Parameters

Name In Required Description
symbol path yes Underlying symbol
expiry query no Filter all sub-results in the bundle to a single expiry, YYYY-MM-DD. Omit for the full-chain bundle
polarity query no convention (default) or flow. flow re-signs only the gamma-derived fields (live_gex, live_gamma_flip, live_call_wall, live_put_wall) by dealer position and adds "polarity": "flow"; DEX + flow_adjusted_dealer_risk stay convention-signed. See /v1/flow/gex. Live only (historical ?at= bundle stays convention).

Example

curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/flow/live/SPY?expiry=2026-05-15"

Response 200

{
  "symbol": "SPY",
  "as_of": "2026-05-12T16:30:45Z",
  "underlying_price": 597.50,
  "expiry": "2026-05-15",
  "contracts": 4586,
  "contracts_with_flow": 1842,

  "official_oi": 16222147,
  "simulated_oi": 16234597,
  "intraday_oi_delta": 12450,
  "oi_delta_confidence": 0.43,
  "effective_oi": 16234597,

  "live_gex": 12500000000,
  "live_gex_delta": -450000000,
  "live_gamma_flip": 595.50,
  "live_call_wall": 600,
  "live_put_wall": 590,
  "live_max_pain": 595,
  "live_pin_risk": 67,

  "flow_adjusted_dealer_risk": {
    "settled_net_gex": 11700000000,
    "live_net_gex": 12500000000,
    "flow_gex_adjustment": 800000000,
    "flow_gex_pct_shift": 0.068,
    "settled_net_dex": -425000000,
    "live_net_dex": -450000000,
    "flow_dex_adjustment": -25000000,
    "flow_dex_pct_shift": 0.059,
    "total_abs_delta_contracts": 47820,
    "flow_direction": "amplifying",
    "description": "Flow has amplified dealer GEX by 6.8% since open. 1,842 contracts saw 47,820 contract-units of repositioning."
  }
}

Errors

Status Description
400 Invalid expiry format - must be YYYY-MM-DD. Response body: {"error":"invalid_expiry","message":"..."}
403 Alpha plan or higher required
404 Symbol not found, no greeks data, or no contracts on the requested expiry

Notes

  • live_gex_delta is the DEX value (delta exposure), named for consistency with the live_gex prefix
  • flow_adjusted_dealer_risk.flow_direction enum: no_flow / neutral / amplifying / dampening / regime_flip (same classifier as /v1/flow/dealer-risk)
  • Overlapping fields match the individual endpoints from the same math layer; call /v1/flow/gex, /dex, or /pin-risk when you need per-strike arrays or the full pin-risk breakdown

GET /v1/flow/signals/{symbol}

Scored, classified unusual-flow feed for one underlying. Each notable (block-sized) print in the window is coalesced into a signal, classified (block/sweep, NBBO aggressor, opening/closing bias from the OI simulator, directional intent), scored 0-100 with a transparent component breakdown, and enriched with chain context (greeks, IV-vs-ATM, moneyness, estimated delta-notional). Signals are ranked highest score first.

Requires Alpha plan or higher.

Parameters

Name In Required Description
symbol path yes Underlying symbol
minScore query no Drop signals below this score (0-100, default 0)
intent query no Filter by bullish / bearish / neutral
structure query no Filter by block / sweep (a lone print is a block; sweep = ≥2 same-side prints on one contract within ~500ms)
windowMinutes query no Look-back window in minutes (1-10080, default 240)
limit query no Max signals returned (1-500, default 50)
expiry query no Filter the chain to a single expiry, YYYY-MM-DD

Example

curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/flow/signals/NVDA?minScore=70&structure=sweep&windowMinutes=240"

Response 200

{
  "symbol": "NVDA",
  "as_of": "2026-05-16T16:30:45Z",
  "window_minutes": 240,
  "expiry": null,
  "underlying_price": 900.25,
  "chain": {
    "call_wall": 950.0,
    "put_wall": 850.0,
    "max_pain": 900.0,
    "gamma_flip": 905.0
  },
  "count": 1,
  "signals": [
    {
      "ts": "2026-05-16T15:58:12Z",
      "expiry": "2026-05-23",
      "strike": 950.0,
      "right": "C",
      "side": "buy",
      "price": 12.40,
      "size": 1500,
      "premium": 1860000.0,
      "dte": 7,
      "structure": "sweep",
      "aggressor": "above_ask",
      "open_close_bias": "opening_bias",
      "open_close_confidence": 0.43,
      "contract_net_oi_delta": 4200,
      "intent": "bullish",
      "score": 87,
      "conviction": "high",
      "tags": ["sweep", "opening", "whale", "golden"],
      "score_breakdown": {
        "premium": 22, "size_vs_oi": 18, "aggressor": 14,
        "sweep": 12, "opening_bias": 9, "tenor": 12
      },
      "enrichment": {
        "iv": 0.62,
        "delta": 0.28,
        "gamma": 0.0041,
        "iv_vs_atm": 0.08,
        "moneyness": "OTM",
        "estimated_delta_notional": 37810500.0,
        "hypothetical_gex_impact_if_opening": 4984267.88
      }
    }
  ]
}

Notes

  • score_breakdown components sum to score; weights are server-tunable, so absolute contributions may shift over time while ordering stays stable
  • open_close_bias (opening_bias / closing_bias / unknown) is a contract-level signal from the OI simulator's net intraday delta - it is not a per-print opening/closing label. contract_net_oi_delta is that raw signed estimate; open_close_confidence is the simulator's confidence weight
  • intent is neutral whenever open_close_bias is closing_bias (direction can't be attributed on unwinds) or the trade side is mid (note: side - the upstream buy/sell/mid aggressor classification - is distinct from the NBBO aggressor label)
  • structure is always block or sweep (a lone block-sized print is a block); single is reserved and not currently emitted, so filtering structure=single returns nothing
  • tags may include sweep, block, opening, closing, 0dte, whale (premium ≥ $1M), and golden (score in the top decile within this response set and ≥ 70 absolute - so a weak set may yield no golden at all)
  • enrichment.* fields are null and moneyness is "unknown" when the signal's contract isn't in the settled chain snapshot (illiquid / just-listed)
  • hypothetical_gex_impact_if_opening is explicitly conditional - the standalone gamma-$ this single print would add if it were opening and fully dealer-absorbed. It is not applied to the live chain (which already folds in intraday OI), so don't sum it against /v1/flow/gex
  • chain levels are settled-chain reference context, computed once per request
  • If the settled chain snapshot is unavailable for the symbol, the feed degrades rather than 404s: scoring still runs on trade + OI-simulator context, but underlying_price is 0, all chain levels are null, and every enrichment block is null/"unknown"
  • Aggressor strength uses NBBO at trade (above_ask/at_ask/mid/at_bid/below_bid); the score weights conviction in the trade's own direction

GET /v1/flow/signals/{symbol}/summary

Roll-up of the scored feed for one underlying - net directional and opening/closing premium plus the top signals. Cheap to call across a watchlist for a "smart-money tilt" read.

Requires Alpha plan or higher.

Parameters

Name In Required Description
symbol path yes Underlying symbol
windowMinutes query no Look-back window in minutes (1-10080, default 240)
expiry query no Filter the chain to a single expiry, YYYY-MM-DD

Example

curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/flow/signals/NVDA/summary?windowMinutes=240"

Response 200

{
  "symbol": "NVDA",
  "as_of": "2026-05-16T16:30:45Z",
  "window_minutes": 240,
  "expiry": null,
  "underlying_price": 900.25,
  "signal_count": 12,
  "bullish_premium": 18250000.0,
  "bearish_premium": 6400000.0,
  "net_directional_premium": 11850000.0,
  "opening_premium": 21300000.0,
  "closing_premium": 3350000.0,
  "top_signals": [
    {
      "ts": "2026-05-16T15:58:12Z",
      "expiry": "2026-05-23",
      "strike": 950.0,
      "right": "C",
      "side": "buy",
      "price": 12.40,
      "size": 1500,
      "premium": 1860000.0,
      "dte": 7,
      "structure": "sweep",
      "aggressor": "above_ask",
      "open_close_bias": "opening_bias",
      "open_close_confidence": 0.43,
      "contract_net_oi_delta": 4200,
      "intent": "bullish",
      "score": 87,
      "conviction": "high",
      "tags": ["sweep", "opening", "whale", "golden"],
      "score_breakdown": {
        "premium": 22, "size_vs_oi": 18, "aggressor": 14,
        "sweep": 12, "opening_bias": 9, "tenor": 12
      },
      "enrichment": {
        "iv": 0.62,
        "delta": 0.28,
        "gamma": 0.0041,
        "iv_vs_atm": 0.08,
        "moneyness": "OTM",
        "estimated_delta_notional": 37810500.0,
        "hypothetical_gex_impact_if_opening": 4984267.88
      }
    }
  ]
}

Notes

  • net_directional_premium = bullish_premium − bearish_premium; premiums sum signal premium (price × size × 100) by classified intent / open_close_bias
  • signal_count is the full count in the window; top_signals is the same signal shape as /v1/flow/signals/{symbol}, capped at the 10 highest-scoring

Zero-DTE Flow

Live, intraday-aware view of today's 0DTE dealer-positioning landscape. Computed on effective OI (settled + the simulator's intraday delta) so the response reflects how dealer GEX has shifted since open - unlike /v1/exposure/zero-dte/, which uses the morning OPRA broadcast and goes stale by 10:30 AM ET.

GET /v1/flow/zero-dte/snapshot/{symbol} - Growth+

When body.regime.gamma_flip is null, body.regime.label is unknown and body.regime.gamma_flip_status explains why the level was withheld. Gamma-dependent iron-fly and iron-condor setups are unavailable in this state; setup.primary can be wait. Other analytics can remain populated when their own inputs exist.

Current live 0DTE shape for {symbol}. The classic 0DTE payload now lives under a body object; the top level adds a human-readable layer (headline, setup, probabilities), the flow_direction block, identity/state fields, and a disclaimer. body is the full ZeroDteResponse - the same shape /v1/exposure/zero-dte/{symbol} returns (time_to_close_hours, regime, expected_move, pin_risk, decay, hedging, levels, vol_context, exposures, liquidity, strikes).

Envelope note - shape changed. The classic fields (regime, expected_move, pin_risk, exposures, levels, …) moved from the top level into body. Clients that read them at the root now see null/missing - read them under body. The degraded-state flags no_zero_dte / session_closed / message appear at the top level (wrapper state; body is null in those cases). headline / setup / probabilities / disclaimer are additive.

{
  "symbol": "SPY",
  "underlying_price": 522.34,
  "as_of": "2026-05-27T15:30:12Z",
  "market_open": true,
  "flow_direction": {
    "label": "amplifying",           // no_flow | neutral | amplifying | dampening | regime_flip
    "settled_net_gex": -1.2e9,
    "live_net_gex": -1.45e9,
    "flow_gex_adjustment": -2.5e8,
    "flow_gex_pct_shift": 0.208,     // null when settled GEX is 0
    "contracts_with_flow": 412,
    "total_abs_delta_contracts": 18432,
    "description": "Flow has amplified dealer GEX by 20.8% since open. ..."
  },
  "headline": {
    "narrative": "…",
    "trade_angle": "pin_trade",      // pin_trade | range_trade | breakout_risk | low_signal
    "severity": "watch",             // info | watch | alert
    "promoted": { "gamma_flip": 521, "magnet": 522, "pin_score": 68,
                  "pin_probability_pct": 41.2, "regime": "…",
                  "distance_to_flip_sigmas": 0.3, "remaining_1sd_pct": 0.42 }
  },
  "setup": { "primary": { /* SetupEvaluation */ }, "alternatives": [ … ],
             "anti_setups": [ … ], "evaluator_version": "v1" },
  "probabilities": { /* pin (heuristic_v1), range (normal_cdf_v1) */ },
  "disclaimer": "…",
  "body": {
    // full ZeroDteResponse - the classic payload:
    // time_to_close_hours, regime, expected_move, pin_risk, decay, hedging,
    // levels, vol_context, exposures, liquidity, strikes, no_zero_dte, message
  }
}

Scope - flow_direction is whole-chain, the 0DTE fields are today's expiry. body (exposures.net_gex, regime, levels, pin_risk, ...) covers today's expiry only. flow_direction (live_net_gex, settled_net_gex, flow_gex_adjustment, flow_gex_pct_shift, label, contracts_with_flow, total_abs_delta_contracts) covers every expiry of the requested symbol - for SPXW, every SPXW expiry (not the AM-settled SPX monthlies). The two net GEX values therefore differ in size and can have opposite signs (dealers short gamma in today's book while the whole chain is net long, or the reverse). Read the 0DTE regime from body.exposures.net_gex / body.regime.label; read flow_direction as how intraday flow has moved dealer gamma across the whole chain. For a 0DTE-only flow view use /v1/flow/gex/{symbol}?expiry=<today>, whose live_net_gex matches body.exposures.net_gex.

Degraded shapes (all return 200 OK; switch on the top-level flags - body is null):

  • No 0DTE expiry today (most non-SPX/SPY/QQQ/IWM symbols only have 0DTE Mon/Wed/Fri):
    { "symbol": "TSLA", "no_zero_dte": true, "next_zero_dte_expiry": "2026-05-29",
      "message": "No 0DTE expiry for TSLA today (Wednesday). Next expiry: 2026-05-29.", "body": null }
    
  • Market closed (weekend / holiday / outside 9:30-16:00 ET):
    { "symbol": "SPY", "session_closed": true, "last_session": "2026-05-23",
      "message": "Market closed. Last session: 2026-05-23.", "body": null }
    

Errors:

  • 403 tier_restricted - caller's plan is below Growth.
  • 404 symbol_not_found - symbol is unknown or has no live data.

The companion /series, /hedge-flow, /heatmap, and /strike-flow endpoints (documented below) are now available for intraday time-series, dealer hedge-flow, and per-strike charting. Still planned for future phases: /historical and /leaderboard.


GET /v1/flow/zero-dte/series/{symbol} - Growth+

Returns an intraday time series of today's 0DTE flow - one bar per sampled interval - for charting headline metrics (net GEX/DEX, gamma flip, walls, magnet, pin score/probability, regime, ATM IV, charm) and cumulative dealer hedge-flow over the session. Bars are read from the raw sampler tier (snapped to :00/:30 ET), windowed to the last minutes, then downsampled to the requested bar size by keeping the last sample in each bucket (so cumulative values stay current).

Requires Growth plan or higher.

Parameters

Name In Required Default Description
symbol path yes - Underlying symbol (e.g. SPY, SPX). Trimmed and upper-cased.
bar query no 30s Bar size. Allowed: 30s, 1m, 5m, 15m. Anything else → 400 invalid_bar. Raw storage is 30s; larger sizes are downsampled last-in-bucket.
minutes query no 60 Lookback window in minutes. Clamped to 1-390 (one full RTH session).
curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/flow/zero-dte/series/SPY?bar=1m&minutes=120"
{
  "symbol": "SPY",
  "expiration": "2026-06-05",          // today (ET); the 0DTE expiry being sampled
  "as_of": "2026-06-05T18:45:12Z",     // server time the response was built (UTC)
  "market_open": true,                 // true if ET-now is Mon-Fri 09:30-16:00 ET (no holiday calendar)
  "bar_size": "1m",                    // echoes the requested bar
  "bars": [
    {
      "t": "2026-06-05T18:44:00Z",     // bar timestamp (UTC, snapped to :00/:30 ET upstream)
      "spot": 590.42,
      "net_gex": 1842000000,
      "net_dex": 48200000000,
      "gamma_flip": 588.50,            // nullable
      "call_wall": 595.0,              // nullable
      "put_wall": 585.0,               // nullable
      "magnet": 590.0,                 // nullable
      "pin_score": 82,                 // 0-100
      "pin_probability_pct": 64.1,     // nullable
      "regime": "positive_gamma",
      "atm_iv": 0.123,                 // nullable
      "charm_dollars_per_hour": -3856000, // nullable
      "hedge_flow_call_cumulative": 18432000,  // $-units, since session open
      "hedge_flow_put_cumulative": -9210000,
      "hedge_flow_cumulative_all": 9222000     // call + put cumulative
    }
    // ... one entry per downsampled bucket in the window, ascending by t
  ]
}

Degraded shapes: there is no separate no_zero_dte / session_closed envelope here - when the symbol has no 0DTE samples today (no 0DTE expiry, session not yet started, market closed with no rows persisted, or an unknown/uncovered symbol), the endpoint still returns 200 with the normal wrapper and an empty bars array. Use market_open to distinguish "expected" empty (closed market) from "alert ops" empty (sampler outage during cash hours):

{
  "symbol": "SPY",
  "expiration": "2026-06-05",
  "as_of": "2026-06-05T11:02:00Z",
  "market_open": false,                // outside cash hours → empty bars is expected
  "bar_size": "30s",
  "bars": []                           // no samples in the requested window
}

Errors:

  • 400 invalid_bar - bar was not one of 30s / 1m / 5m / 15m. Body: { "error": "invalid_bar", "message": "bar must be 30s|1m|5m|15m" }.
  • 403 tier_restricted - caller is below the Growth plan. Body includes status, error, message, current_plan, and required_plan: "Growth".

GET /v1/flow/zero-dte/hedge-flow/{symbol} - Growth+

Returns the estimated dealer hedge-flow time series for today's 0DTE chain - signed delta-dollars dealers are inferred to be buying/selling to stay hedged - as both per-bar increments and a running cumulative since session open. Projectable to calls only, puts only, or the combined total via side, without re-scanning trades (the raw tier stores both legs). Same windowing/downsampling as /series (last-in-bucket).

Requires Growth plan or higher.

Parameters

Name In Required Default Description
symbol path yes - Underlying symbol. Trimmed and upper-cased.
side query no all Which leg to project. Allowed: all, calls, puts. Anything else → 400 invalid_side.
bar query no 30s Bar size. Allowed: 30s, 1m, 5m, 15m. Anything else → 400 invalid_bar.
minutes query no 60 Lookback window in minutes. Clamped to 1-390.
curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/flow/zero-dte/hedge-flow/SPY?side=calls&bar=5m&minutes=180"
{
  "symbol": "SPY",
  "expiration": "2026-06-05",          // today (ET)
  "as_of": "2026-06-05T18:45:12Z",     // UTC
  "market_open": true,                 // true if ET-now is Mon-Fri 09:30-16:00 ET (no holiday calendar)
  "side": "calls",                     // echoes the requested side (all | calls | puts)
  "bar_size": "5m",                    // echoes the requested bar
  "bars": [
    {
      "t": "2026-06-05T18:40:00Z",     // bar timestamp (UTC)
      "bar": 1240000,                  // per-bar signed delta-dollars in this bucket
      "cumulative": 18432000           // running sum since session open (for `side`)
    }
    // ... ascending by t
  ]
}

For side=all, bar and cumulative are the sum of the call and put legs; for calls / puts they are that single leg.

Degraded shapes: as with /series, there is no no_zero_dte / session_closed envelope - no samples (no 0DTE today, pre-session, closed with no rows, or unknown symbol) returns 200 with the normal wrapper and an empty bars array. Use market_open to tell "expected" empty (closed market) from "alert ops" empty (sampler outage during cash hours):

{
  "symbol": "SPY",
  "expiration": "2026-06-05",
  "as_of": "2026-06-05T11:02:00Z",
  "market_open": false,                // outside cash hours → empty bars is expected
  "side": "all",
  "bar_size": "30s",
  "bars": []
}

Errors:

  • 400 invalid_side - side was not all / calls / puts. Body: { "error": "invalid_side", "message": "side must be all|calls|puts" }.
  • 400 invalid_bar - bar was not one of 30s / 1m / 5m / 15m. Body: { "error": "invalid_bar", "message": "bar must be 30s|1m|5m|15m" }.
  • 403 tier_restricted - caller is below the Growth plan. Body includes status, error, message, current_plan, and required_plan: "Growth".

GET /v1/flow/zero-dte/heatmap/{symbol} - Alpha+

Per-strike value matrix over today's 0DTE session - the data behind a strike × time heatmap. Strikes are pulled into a top-level strikes_grid; each bar carries a values array that is parallel by index to strikes_grid (i.e. values[strike_idx]), the column-major shape every heatmap charting library expects.

Requires Alpha plan or higher.

Parameters

Name In Required Default Description
symbol path yes - Underlying symbol. Trimmed and upper-cased.
metric query no gex Per-strike value to project. One of gex, dex, vex, chex, oi, signed_flow. Invalid → 400 invalid_metric.
mode query no raw raw (absolute per-bar value) or delta (bar-over-bar change; bar 0 stays raw). For signed_flow, delta ≡ raw (already a per-bar increment). Invalid → 400 invalid_mode.
bar query no 1m Bar size. Only 1m is supported in this phase - 5m/15m are planned; any other value → 400 bar_unavailable.
minutes query no 60 Lookback window in minutes. Clamped to 1-390.
curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/flow/zero-dte/heatmap/SPY?metric=gex&mode=raw&bar=1m&minutes=120"
{
  "symbol": "SPY",
  "underlying_price": 590.42,
  "expiration": "2026-06-05",
  "metric": "gex",                     // echoes the requested metric
  "mode": "raw",                       // echoes the requested mode
  "bar_size": "1m",                    // echoes the requested bar
  "as_of": "2026-06-05T18:45:12Z",     // UTC
  "market_open": true,                 // true if ET-now is Mon-Fri 09:30-16:00 ET (no holiday calendar)
  "tier_used": "raw",
  "strikes_grid": [585, 586, 587, 588, 589, 590, 591, 592, 593, 594, 595],
  "bars": [
    {
      "t": "2026-06-05T18:44:00Z",     // bar timestamp (UTC)
      "spot": 590.42,
      "values": [ -1.2e8, -9.0e7, -4.1e7, 0, 3.3e7, 8.1e8, 2.2e8, 1.1e8, 4.0e7, 1.0e7, -5.0e6 ]
      // values[i] is the metric for strikes_grid[i]
    }
    // ... ascending by t
  ],
  "gap_intervals": []                  // reserved for sampler-gap intervals; not yet populated
}

Degraded shapes: no 0DTE samples in the window (no 0DTE today, pre-session, market closed with no rows, or unknown symbol) returns 200 with empty strikes_grid and bars arrays - there is no no_zero_dte / session_closed envelope. Use market_open to distinguish "expected" empty (closed market) from "alert ops" empty (sampler outage during cash hours).

Errors:

  • 400 bar_unavailable - bar is anything other than 1m.
  • 400 invalid_metric - metric not in gex|dex|vex|chex|oi|signed_flow.
  • 400 invalid_mode - mode is not raw/delta.
  • 400 no_strike_data_for_window - rows exist in the window but none carry per-strike data (legacy v1 rows).
  • 403 tier_restricted - caller is below the Alpha plan.

GET /v1/flow/zero-dte/strike-flow/{symbol} - Alpha+

Per-strike signed aggressor flow over today's 0DTE session - for each bar and each strike, the signed delta-dollars, signed gamma-dollars, and contract count (per-bar increments, not cumulative). Three parallel arrays per bar, each index-aligned to the top-level strikes_grid (the shape bubble-grid plotters want).

Requires Alpha plan or higher.

Parameters

Name In Required Default Description
symbol path yes - Underlying symbol. Trimmed and upper-cased.
bar query no 1m Bar size. Only 1m is supported in this phase - 5m/15m are planned; any other value → 400 bar_unavailable.
minutes query no 60 Lookback window in minutes. Clamped to 1-390.
curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/flow/zero-dte/strike-flow/SPY?bar=1m&minutes=120"
{
  "symbol": "SPY",
  "underlying_price": 590.42,
  "expiration": "2026-06-05",
  "bar_size": "1m",
  "as_of": "2026-06-05T18:45:12Z",     // UTC
  "market_open": true,                 // true if ET-now is Mon-Fri 09:30-16:00 ET (no holiday calendar)
  "tier_used": "raw",
  "strikes_grid": [588, 589, 590, 591, 592],
  "bars": [
    {
      "t": "2026-06-05T18:44:00Z",                    // bar timestamp (UTC)
      "spot": 590.42,
      "signed_delta_dollars": [ -120000, 45000, 880000, 210000, -30000 ],  // per strike
      "signed_gamma_dollars": [ 18000, 24000, 96000, 31000, 9000 ],
      "contracts": [ 1200, 1850, 5400, 2100, 640 ]
    }
    // ... ascending by t
  ],
  "gap_intervals": []
}

Degraded shapes: no 0DTE samples returns 200 with empty strikes_grid and bars (no flag envelope), same as /series and /heatmap. Use market_open to distinguish "expected" empty (closed market) from "alert ops" empty (sampler outage during cash hours). The gap_intervals array is reserved and not yet populated.

Errors:

  • 400 bar_unavailable - bar is anything other than 1m.
  • 403 tier_restricted - caller is below the Alpha plan.

Trade Flow Data

Formerly called Raw Flow Data.

These Alpha-only endpoints proxy intraday trade-flow JSON from the ingest service. Unlike the analytics endpoints above, these are pass-through responses and use camelCase field names. Cross-symbol scans refresh about every 30 seconds.

Common errors:

Status Description
403 Alpha plan or higher required
502 Flow data source did not respond

GET /v1/flow/options/{symbol}/recent

Recent option trades across all contracts for an underlying, newest first.

Requires Alpha plan or higher.

Parameters

Name In Required Default Description
symbol path yes - Underlying symbol
limit query no 50 Number of trades, clamped to 1..500
expiry query no - Filter to a single expiration cycle, YYYY-MM-DD. Omit to include all expiries. Useful for 0DTE-only or OPEX-only views

Response Fields

Field Type Description
symbol string Uppercase underlying symbol
expiry string/null Echo of the ?expiry= filter, or null when no filter was applied
count integer Number of trades returned
totalAvailable integer Trades available in the in-memory buffer (post-filter)
trades[] array Recent trade rows

trades[]: ts, instrumentId, expiry, strike, right, price, size, side, isBlock, bid, ask.

Errors

Status Description
400 Invalid expiry format. Response: {"error":"invalid_expiry","message":"..."}

GET /v1/flow/options/{symbol}/summary

Option trade-flow totals across all contracts for an underlying.

Requires Alpha plan or higher.

Parameters

Name In Required Default Description
symbol path yes - Underlying symbol
expiry query no - Filter to a single expiration cycle, YYYY-MM-DD. Aggregates only contracts on that expiry

Response Fields

Field Type Description
symbol string Uppercase underlying symbol
expiry string/null Echo of the ?expiry= filter, or null when no filter was applied
contractsWithTrades integer Option contracts with at least one buffered trade (post-filter)
totalTrades integer Total buffered option trades
buyVolume integer Buy-classified contract volume
sellVolume integer Sell-classified contract volume
midVolume integer Mid/unknown-side contract volume
netVolume integer buyVolume - sellVolume
biggestSingleTrade integer Largest single option trade size
lastTradeUtc string/null Latest trade timestamp

Errors

Status Description
400 Invalid expiry format. Response: {"error":"invalid_expiry","message":"..."}

GET /v1/flow/options/{symbol}/blocks

Large option trades across all contracts for an underlying, newest first.

Requires Alpha plan or higher.

Parameters

Name In Required Default Description
symbol path yes - Underlying symbol
minSize query no 100 Minimum contract size
expiry query no - Filter to a single expiration cycle, YYYY-MM-DD

Response Fields

Field Type Description
symbol string Uppercase underlying symbol
expiry string/null Echo of the ?expiry= filter, or null when no filter was applied
minSize integer Minimum block size applied
count integer Number of block trades returned
blocks[] array Block rows

blocks[]: ts, expiry, strike, right, price, size, side.

Errors

Status Description
400 Invalid expiry format. Response: {"error":"invalid_expiry","message":"..."}

GET /v1/flow/options/{symbol}/history

Minute option-flow buckets rolled up by underlying, newest first.

Requires Alpha plan or higher.

Parameters

Name In Required Default Description
symbol path yes - Underlying symbol
minutes query no 60 Window size, clamped to 1..10080
expiry query no - Filter the rollup to a single expiration cycle, YYYY-MM-DD. Each minute bucket then sums only contracts on that expiry

Response Fields

Field Type Description
symbol string Uppercase underlying symbol
expiry string/null Echo of the ?expiry= filter, or null when no filter was applied
minutes integer Requested/effective minute window
count integer Number of buckets returned
buckets[] array Minute buckets

buckets[]: ts, buyVolume, sellVolume, midVolume, netVolume, tradeCount, biggestTrade, vwap, high, low.

Retention

Minute buckets live in an in-memory ring sized at ~1,500 buckets (~25 hours of intraday history). Past that they evict. SQL snapshot persistence is current-day only.

Errors

Status Description
400 Invalid expiry format. Response: {"error":"invalid_expiry","message":"..."}

GET /v1/flow/options/{symbol}/cumulative

Running cumulative net option flow by underlying.

Requires Alpha plan or higher.

Parameters

Name In Required Default Description
symbol path yes - Underlying symbol
minutes query no 240 Window size, clamped to 1..10080
expiry query no - Filter the rollup to a single expiration cycle, YYYY-MM-DD

Response Fields

Field Type Description
symbol string Uppercase underlying symbol
expiry string/null Echo of the ?expiry= filter, or null when no filter was applied
minutes integer Requested/effective minute window
count integer Number of points returned
points[] array Cumulative flow points

points[]: ts, netVolume, cumulative, vwap, tradeCount.

Errors

Status Description
400 Invalid expiry format. Response: {"error":"invalid_expiry","message":"..."}

GET /v1/flow/stocks/{symbol}/recent

Recent stock trades for a symbol, newest first.

Requires Alpha plan or higher.

Parameters

Name In Required Default Description
symbol path yes - Stock symbol
limit query no 50 Number of trades, clamped to 1..500

Response Fields

Field Type Description
symbol string Uppercase stock symbol
count integer Number of trades returned
totalAvailable integer Trades available in the in-memory buffer
trades[] array Recent trade rows

trades[]: ts, price, size, side, isBlock, bid, ask.


GET /v1/flow/stocks/{symbol}/summary

Stock trade-flow totals for a symbol.

Requires Alpha plan or higher.

Response Fields

Field Type Description
symbol string Uppercase stock symbol
totalTrades integer Total buffered stock trades
buyVolume integer Buy-classified share volume
sellVolume integer Sell-classified share volume
midVolume integer Mid/unknown-side share volume
netVolume integer buyVolume - sellVolume
biggestSingleTrade integer Largest single stock trade size
lastTradeUtc string/null Latest trade timestamp; omitted when no stock trade buffer exists

GET /v1/flow/stocks/{symbol}/blocks

Large stock trades for a symbol, newest first.

Requires Alpha plan or higher.

Parameters

Name In Required Default Description
symbol path yes - Stock symbol
minSize query no 10000 Minimum share size

Response Fields

Field Type Description
symbol string Uppercase stock symbol
minSize integer Minimum block size applied
count integer Number of block trades returned
blocks[] array Block rows

blocks[]: ts, price, size, side, bid, ask.


GET /v1/flow/stocks/{symbol}/history

Minute stock-flow buckets, newest first.

Requires Alpha plan or higher.

Parameters

Name In Required Default Description
symbol path yes - Stock symbol
minutes query no 60 Window size, clamped to 1..10080

Response Fields

Field Type Description
symbol string Uppercase stock symbol
minutes integer Requested/effective minute window
count integer Number of buckets returned
buckets[] array Minute buckets

buckets[]: ts, buyVolume, sellVolume, midVolume, netVolume, tradeCount, biggestTrade, vwap, open, close, high, low.


GET /v1/flow/stocks/{symbol}/cumulative

Running cumulative net stock flow.

Requires Alpha plan or higher.

Parameters

Name In Required Default Description
symbol path yes - Stock symbol
minutes query no 240 Window size, clamped to 1..10080

Response Fields

Field Type Description
symbol string Uppercase stock symbol
minutes integer Requested/effective minute window
count integer Number of points returned
points[] array Cumulative flow points

points[]: ts, netVolume, cumulative, vwap, tradeCount.


GET /v1/flow/stocks/{symbol}/bars

Multi-resolution OHLCV+flow bars derived from the live trade tape. Backed by an in-memory 1-second ring per symbol; higher resolutions are computed on read by streaming roll-up. Bars are returned oldest-first for chart consumers (contrast /history, which is newest-first).

The leftmost bar's start time is always boundary-aligned to the resolution - for a 15-minute resolution requested at 10:17 with minutes=15, the leftmost bar starts at 10:00 (not 10:02). The right-most bar may be partial (the current bar is still accumulating).

Requires Alpha plan or higher.

Parameters

Name In Required Default Description
symbol path yes - Stock symbol (uppercased server-side)
resolution query yes - One of 1s, 1m, 5m, 15m, 30m, 1h, 4h. Anything else returns 400 {"error":"invalid_resolution"}.
minutes query no 60 Look-back window. Clamped to [1, 1440] (24h matches the in-memory ring cap).

Response Fields

Field Type Description
symbol string Uppercase symbol
resolution string Echo of requested resolution
minutes integer Effective window
count integer Number of bars returned
dataStartUtc string | null Timestamp of the oldest 1-second bucket in the in-memory ring (UTC), or null when no history exists. Use this to detect partial coverage when requesting minutes=1440 against a process that started recently - if dataStartUtc > now - minutes*60, the response covers a shorter window than requested.
bars[] array Oldest-first bars

bars[] entries: ts (bar start, UTC), closed (bool - see below), open, high, low, close, vwap, buyVolume, sellVolume, midVolume, netVolume, tradeCount, biggestTrade.

The closed flag is true once wall-clock time has advanced past [ts, ts + resolution) - i.e. the bar is final and won't accept further updates. The right-edge bar is typically closed: false (still accumulating); all preceding bars are closed: true. Chart libraries that distinguish in-progress from final bars (e.g. TradingView, Lightweight Charts) should use this flag directly rather than re-deriving it client-side, since client clock skew makes the derivation unreliable.

Example

curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/flow/stocks/SPY/bars?resolution=5m&minutes=60"

Notes

  • 1-second history is in-memory only and dropped across container restarts (charts re-fill forward from process start). dataStartUtc surfaces the effective coverage so clients can tell "process is too young to answer" from "symbol is quiet".
  • Seconds with zero trades are not emitted - gaps in the bar series reflect a quiet symbol, not data loss.
  • Use /history (returns 1-minute buckets newest-first, persisted across restarts) when you need long-term replay; /bars for live chart feeds.

GET /v1/flow/options/leaderboard

Cross-symbol option-flow buyers and sellers ranked by net notional.

Requires Alpha plan or higher.

Parameters

Name In Required Default Description
n query no 10 Rows per side; effective max is 50
windowMinutes query no 240 Lookback window, clamped to 1..10080

Response Fields

Field Type Description
generatedUtc string Generation timestamp
n integer Effective rows per side
windowMinutes integer Effective lookback window
buyers[] array Positive net-notional rows
sellers[] array Negative net-notional rows

buyers[] / sellers[]: symbol, netVolume, netNotional, buyVolume, sellVolume, avgPremium, tradeCount, lastTradeUtc.


GET /v1/flow/stocks/leaderboard

Cross-symbol stock-flow buyers and sellers ranked by net notional.

Requires Alpha plan or higher.

Parameters

Name In Required Default Description
n query no 10 Rows per side; effective max is 50
windowMinutes query no 240 Lookback window, clamped to 1..10080

Response Fields

Field Type Description
generatedUtc string Generation timestamp
n integer Effective rows per side
windowMinutes integer Effective lookback window
buyers[] array Positive net-notional rows
sellers[] array Negative net-notional rows

buyers[] / sellers[]: symbol, netVolume, netNotional, buyVolume, sellVolume, vwap, tradeCount, lastTradeUtc.


GET /v1/flow/options/outliers

Cross-symbol option-flow outliers ranked by absolute net notional.

Requires Alpha plan or higher.

Parameters

Name In Required Default Description
limit query no 20 Rows returned, clamped to 1..200
minTrades query no 20 Minimum trades for a symbol to qualify
windowMinutes query no 240 Lookback window, clamped to 1..10080

Response Fields

Field Type Description
generatedUtc string Generation timestamp
windowMinutes integer Effective lookback window
tracked integer Symbols/underlyings scanned
qualified integer Symbols that met filters
limit integer Effective output limit
outliers[] array Outlier rows

outliers[]: symbol, tradeCount, buyVolume, sellVolume, midVolume, netVolume, imbalancePct, skew, notional, netNotional, biggestTrade, biggestTradeUtc, biggestAgeSec, lastVwap, lastTradeUtc, lastTradeAgeSec.


GET /v1/flow/stocks/outliers

Cross-symbol stock-flow outliers ranked by absolute net notional.

Requires Alpha plan or higher.

Parameters

Name In Required Default Description
limit query no 20 Rows returned, clamped to 1..200
minTrades query no 20 Minimum trades for a symbol to qualify
windowMinutes query no 240 Lookback window, clamped to 1..10080

Response Fields

Same response shape as /v1/flow/options/outliers.


GET /v1/maxpain/{symbol}

Returns max pain analysis for a symbol - the strike where total option holder payout is minimized. Includes pain curve, OI breakdown, dealer alignment overlay (gamma flip, call/put walls), expected move context, pin probability, and multi-expiry calendar.

Requires Basic plan or higher.

Parameters

Name In Required Default Description
symbol path yes - Underlying symbol
expiration query no - Filter to a single expiry (yyyy-MM-dd). Omit for full-chain analysis with multi-expiry breakdown.

Example

curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/maxpain/SPY"

With expiration filter:

curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/maxpain/SPY?expiration=2026-04-17"

Response 200

{
  "symbol": "SPY",
  "underlying_price": 548.32,
  "as_of": "2026-04-09T15:30:00Z",
  "max_pain_strike": 545,
  "distance": {
    "absolute": 3.32,
    "percent": 0.61,
    "direction": "above"
  },
  "signal": "neutral",
  "expiration": "2026-04-17",
  "put_call_oi_ratio": 1.284,
  "pain_curve": [
    { "strike": 540, "call_pain": 0, "put_pain": 18500000, "total_pain": 18500000 },
    { "strike": 545, "call_pain": 2500000, "put_pain": 1200000, "total_pain": 3700000 },
    { "strike": 550, "call_pain": 8400000, "put_pain": 0, "total_pain": 8400000 }
  ],
  "oi_by_strike": [
    { "strike": 540, "call_oi": 12000, "put_oi": 28000, "total_oi": 40000, "call_volume": 3200, "put_volume": 5800 },
    { "strike": 545, "call_oi": 35000, "put_oi": 42000, "total_oi": 77000, "call_volume": 8100, "put_volume": 9400 },
    { "strike": 550, "call_oi": 18000, "put_oi": 8000, "total_oi": 26000, "call_volume": 4500, "put_volume": 2100 }
  ],
  "max_pain_by_expiration": [
    { "expiration": "2026-04-11", "max_pain_strike": 547, "dte": 2, "total_oi": 520000 },
    { "expiration": "2026-04-17", "max_pain_strike": 545, "dte": 8, "total_oi": 1840000 },
    { "expiration": "2026-05-16", "max_pain_strike": 540, "dte": 37, "total_oi": 980000 }
  ],
  "dealer_alignment": {
    "alignment": "converging",
    "description": "Max pain (545) near gamma flip (546) between walls (538-555) - strong converging magnet.",
    "gamma_flip": 546,
    "call_wall": 555,
    "put_wall": 538
  },
  "regime": "positive_gamma",
  "expected_move": {
    "straddle_price": 4.82,
    "atm_iv": 18.4,
    "max_pain_within_expected_range": true
  },
  "pin_probability": 68
}

Response Fields

Field Description
max_pain_strike Strike where total option holder intrinsic value is minimized
distance.absolute Dollar distance from spot to max pain
distance.percent Percentage distance from spot to max pain
distance.direction above, below, or at - spot relative to max pain
signal bullish (spot >= 5% below max pain), bearish (>= 5% above), neutral (within 5%)
put_call_oi_ratio Total put OI / total call OI. >1.0 = put-heavy chain
pain_curve Per-strike breakdown: call pain, put pain, total pain. Minimum total_pain matches max_pain_strike.
oi_by_strike Per-strike OI and volume for calls and puts
max_pain_by_expiration Per-expiry max pain with DTE and total OI. Only present when no ?expiration= filter.
dealer_alignment.alignment converging (max pain near gamma flip, between walls), moderate (between walls, far from flip), diverging (outside walls), unknown (insufficient data)
dealer_alignment.gamma_flip Nullable underlying price where aggregate repriced GEX crosses from negative to positive.
dealer_alignment.call_wall / put_wall Strikes with highest absolute call/put GEX
regime positive_gamma or negative_gamma based on spot vs gamma flip
expected_move.straddle_price ATM straddle mid price
expected_move.atm_iv ATM implied volatility (percentage, e.g. 18.4 = 18.4%)
expected_move.max_pain_within_expected_range Whether max pain is within the straddle-implied expected move
pin_probability 0-100 composite score: OI concentration (30%), magnet proximity (25%), time remaining (25%), gamma magnitude (20%)

Errors

Status Description
400 Invalid expiration format (must be yyyy-MM-dd)
403 Requires Basic plan or higher
404 Symbol not found, no options data, or no data for requested expiration

Notes

  • Without ?expiration=, the response uses the full options chain and includes max_pain_by_expiration for all available expiries
  • With ?expiration=, the response is scoped to that single expiry and max_pain_by_expiration is null
  • The dealer alignment overlay reuses the same GEX calculations as /v1/exposure/levels - call wall, put wall, and gamma flip values should be consistent
  • pin_probability is most meaningful for near-term expirations; for LEAPs it will be low regardless of OI distribution
  • Pain curve values are in USD notional (intrinsic value × OI × 100 contract multiplier)

GET /v1/pricing/greeks

Compute full Black-Scholes-Merton greeks from provided inputs. Returns theoretical price plus first-order, second-order, and third-order greeks. Pure calculation - no market data required.

Available on all plans (Free+).

Parameters

Name In Required Default Description
spot query yes - Underlying spot price
strike query yes - Strike price
dte query yes - Days to expiration
sigma query yes - Implied volatility (annualized, e.g. 0.18 = 18%)
type query no call call or put
r query no 0.045 Risk-free rate (annualized)
q query no 0.013 Dividend yield (annualized)

Example

curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/pricing/greeks?spot=580&strike=580&dte=30&sigma=0.18&type=call"

Response 200

{
  "inputs": {
    "spot": 580,
    "strike": 580,
    "dte": 30,
    "sigma": 0.18,
    "type": "call",
    "risk_free_rate": 0.045,
    "dividend_yield": 0.013
  },
  "theoretical_price": 12.687211,
  "first_order": {
    "delta": 0.53003,
    "gamma": 0.013276,
    "theta": -0.223599,
    "vega": 0.660706,
    "rho": 24.224395
  },
  "second_order": {
    "vanna": -0.055551,
    "charm": 0.00049,
    "vomma": 0.709126,
    "dual_delta": -0.508155
  },
  "third_order": {
    "speed": -0.00005694,
    "zomma": -0.07361041,
    "color": -0.00022304,
    "ultima": -17.241619
  },
  "additional": {
    "lambda": 24.2305,
    "veta": 1.092334
  }
}

Response Fields

Section Field Description
theoretical_price - BSM theoretical option price
First-order
first_order delta ∂V/∂S - sensitivity to spot price
first_order gamma ∂²V/∂S² - rate of change of delta
first_order theta ∂V/∂t - daily time decay (per day)
first_order vega ∂V/∂σ - sensitivity to 1% vol change
first_order rho ∂V/∂r - sensitivity to interest rate
Second-order
second_order vanna ∂²V/∂S∂σ - delta sensitivity to vol
second_order charm ∂²V/∂S∂t - delta decay per day
second_order vomma ∂²V/∂σ² - vega convexity (volga)
second_order dual_delta ∂V/∂K - sensitivity to strike price
Third-order
third_order speed ∂³V/∂S³ - gamma sensitivity to spot
third_order zomma ∂³V/∂S²∂σ - gamma sensitivity to vol
third_order color ∂³V/∂S²∂t - gamma decay per day
third_order ultima ∂³V/∂σ³ - vomma sensitivity to vol
Additional
additional lambda Delta × S / V - option leverage ratio (null if price ≤ 0)
additional veta ∂²V/∂σ∂t - vega decay per day

Errors

Status Description
400 Invalid or missing input parameter

GET /v1/pricing/iv

Compute implied volatility from a market price using Newton-Raphson root finding.

Available on all plans (Free+).

Parameters

Name In Required Default Description
spot query yes - Underlying spot price
strike query yes - Strike price
dte query yes - Days to expiration
price query yes - Market option price (mid)
type query no call call or put
r query no 0.045 Risk-free rate
q query no 0.013 Dividend yield

Example

curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/pricing/iv?spot=580&strike=580&dte=30&price=12.69&type=call"

Response 200

{
  "inputs": {
    "spot": 580, "strike": 580, "dte": 30, "price": 12.69,
    "type": "call", "risk_free_rate": 0.045, "dividend_yield": 0.013
  },
  "implied_volatility": 0.180042,
  "implied_volatility_pct": 18.0
}

Errors

Status Description
400 Invalid or missing input parameter
422 Could not determine IV (price outside arbitrage-free range)

GET /v1/pricing/kelly

Compute Kelly criterion optimal position sizing for an option trade. Uses numerical integration over the full lognormal distribution of the underlying to maximize expected log-wealth growth - not the simplified gambling formula.

Unlike standard Kelly (designed for fixed-odds bets), this handles the asymmetric, bounded-loss payoff structure of options: max loss is capped at the premium paid, while upside can be unbounded.

Requires Growth plan or higher.

Parameters

Name In Required Default Description
spot query yes - Underlying spot price
strike query yes - Strike price
dte query yes - Days to expiration
sigma query yes - Implied volatility (annualized)
premium query yes - Option premium paid (per share)
mu query yes - Expected annualized return of the underlying (your view, e.g. 0.12 = 12%). Range: -200% to +200%.
type query no call call or put
r query no 0.045 Risk-free rate
q query no 0.013 Dividend yield

How mu works

The key input is mu - your expected annual return for the underlying. This is what gives you an edge over the market price (which uses r):

  • mu > r → you're more bullish than the market → calls have positive edge
  • mu < r → you're more bearish → puts have positive edge
  • mu = r → no edge → Kelly recommends no position

For example, if SPY is priced with r = 4.5% but you expect mu = 12% annual return, your calls have an edge and Kelly will tell you how much to bet.

Example

curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/pricing/kelly?spot=580&strike=580&dte=30&sigma=0.18&premium=12.69&mu=0.12&type=call"

Response 200

{
  "inputs": {
    "spot": 580, "strike": 580, "dte": 30, "sigma": 0.18, "premium": 12.69,
    "mu": 0.12, "type": "call", "risk_free_rate": 0.045, "dividend_yield": 0.013
  },
  "sizing": {
    "kelly_fraction": 0.076842,
    "half_kelly": 0.038421,
    "quarter_kelly": 0.01921,
    "kelly_pct": 7.68,
    "half_kelly_pct": 3.84
  },
  "analysis": {
    "expected_roi": 0.160546,
    "expected_roi_pct": 16.05,
    "expected_payoff": 14.7273,
    "probability_of_profit": 0.391739,
    "probability_of_profit_pct": 39.17,
    "probability_itm": 0.557494,
    "probability_itm_pct": 55.75,
    "max_loss": 12.69,
    "breakeven": 592.69,
    "expected_growth_rate": 0.00584368
  },
  "recommendation": "Risk 3.8% of bankroll (half-Kelly). Probability of profit: 39.2%. Expected ROI: 16.1%."
}

Response Fields

Section Field Description
Sizing
sizing kelly_fraction Full Kelly - optimal fraction of bankroll to risk (0-1)
sizing half_kelly Half Kelly - recommended for practice (less volatile)
sizing quarter_kelly Quarter Kelly - very conservative
sizing kelly_pct / half_kelly_pct Same as above, in percent
Analysis
analysis expected_roi Expected return on investment (payoff/premium − 1)
analysis probability_of_profit P(option profit at expiry) - real-world measure using mu
analysis probability_itm P(option in-the-money at expiry) - real-world measure
analysis max_loss Maximum loss per share (= premium paid)
analysis breakeven Underlying price needed at expiry to break even
analysis expected_growth_rate Expected log-growth rate of bankroll at Kelly fraction
Recommendation
recommendation - Human-readable sizing recommendation

Negative Expected Value

If the option has negative expected value (your view doesn't justify the premium), Kelly returns zero:

{
  "sizing": { "kelly_fraction": 0, "half_kelly": 0, "quarter_kelly": 0 },
  "recommendation": "Negative expected value - Kelly recommends no position."
}

Errors

Status Description
400 Invalid or missing input parameter (including mu outside ±200%)
403 Requires Growth plan or higher

Notes

  • All probabilities use the real-world measure (with mu), not the risk-neutral measure (with r). This is what you actually expect to happen, not what's priced in.
  • Kelly fraction is bounded to [0, 1] - it will never recommend shorting or leveraging beyond 100%.
  • Half-Kelly is strongly recommended in practice - full Kelly is mathematically optimal but very aggressive and assumes perfect parameter estimation.
  • The numerical integration uses 2000 steps over ±6σ of the lognormal distribution, giving high accuracy for typical parameters.

GET /v1/tickers

Returns all available stock tickers from the Polygon catalog.

Example

curl -H "X-Api-Key: YOUR_API_KEY" https://lab.flashalpha.com/v1/tickers

Response 200

{
  "tickers": ["AAPL", "AMZN", "GOOGL", "META", "MSFT", "NVDA", "QQQ", "SPY", "TSLA"],
  "count": 6200
}

GET /v1/options/{ticker}

Returns option chain metadata for a symbol: all available expirations with their strikes.

Parameters

Name In Required Description
ticker path yes Underlying symbol

Example

curl -H "X-Api-Key: YOUR_API_KEY" https://lab.flashalpha.com/v1/options/SPY

Response 200

{
  "symbol": "SPY",
  "expirations": [
    {
      "expiration": "2026-03-07",
      "strikes": [550.0, 555.0, 560.0, 565.0, 570.0, 575.0, 580.0]
    },
    {
      "expiration": "2026-03-14",
      "strikes": [550.0, 555.0, 560.0, 565.0, 570.0]
    }
  ],
  "expiration_count": 2,
  "total_contracts": 12
}

Errors

Status Description
404 No options data for symbol

GET /v1/symbols

Returns the list of symbols that have been queried and currently have live data.

Example

curl -H "X-Api-Key: YOUR_API_KEY" https://lab.flashalpha.com/v1/symbols

Response 200

{
  "symbols": ["SPY", "QQQ"],
  "count": 2,
  "note": "Paid plans serve live data. Endpoint access, fields and quotas depend on your plan; upstream data age and public snapshot policies still apply.",
  "last_updated": "2026-02-28T16:30:45Z"
}

GET /v1/volatility/{symbol}

Returns comprehensive volatility analysis for any US equity or ETF. Includes realized vol, IV-RV spreads, skew profiles, term structure, GEX by DTE, theta decay, put/call breakdowns, OI concentration, multi-move hedging scenarios, and liquidity analysis.

Example

curl -H "X-Api-Key: YOUR_API_KEY" https://lab.flashalpha.com/v1/volatility/TSLA

Response 200

{
  "symbol": "TSLA",
  "underlying_price": 265.50,
  "as_of": "2026-03-09T15:30:00Z",
  "market_open": true,
  "realized_vol": {
    "rv_5d": 52.31,
    "rv_10d": 48.12,
    "rv_20d": 45.80,
    "rv_30d": 44.20,
    "rv_60d": 42.15
  },
  "atm_iv": 48.50,
  "iv_rv_spreads": {
    "vrp_5d": -3.81,
    "vrp_10d": 0.38,
    "vrp_20d": 2.70,
    "vrp_30d": 4.30,
    "assessment": "moderate_premium"
  },
  "skew_profiles": [
    {
      "expiry": "2026-03-14",
      "days_to_expiry": 5,
      "put_10d_iv": 62.50,
      "put_25d_iv": 54.20,
      "atm_iv": 48.50,
      "call_25d_iv": 45.10,
      "call_10d_iv": 43.80,
      "skew_25d": 9.10,
      "smile_ratio": 1.202,
      "tail_convexity": 2.60
    }
  ],
  "term_structure": {
    "near_slope_pct": 5.20,
    "far_slope_pct": 3.10,
    "state": "contango"
  },
  "iv_dispersion": {
    "cross_expiry": 3.45,
    "cross_strike": 12.80
  },
  "gex_by_dte": [
    { "bucket": "0-7d", "net_gex": 1200000, "pct_of_total": 35.2, "contract_count": 450 },
    { "bucket": "8-30d", "net_gex": 800000, "pct_of_total": 23.5, "contract_count": 1200 },
    { "bucket": "31-60d", "net_gex": 950000, "pct_of_total": 27.9, "contract_count": 800 },
    { "bucket": "60d+", "net_gex": 460000, "pct_of_total": 13.4, "contract_count": 600 }
  ],
  "theta_by_dte": [
    { "bucket": "0-7d", "net_theta": -450000, "contract_count": 450 },
    { "bucket": "8-30d", "net_theta": -320000, "contract_count": 1200 },
    { "bucket": "31-60d", "net_theta": -180000, "contract_count": 800 },
    { "bucket": "60d+", "net_theta": -95000, "contract_count": 600 }
  ],
  "put_call_profile": {
    "by_expiry": [
      {
        "expiry": "2026-03-14",
        "call_oi": 120000,
        "put_oi": 95000,
        "pc_ratio_oi": 0.792,
        "call_volume": 45000,
        "put_volume": 38000,
        "pc_ratio_volume": 0.844
      }
    ],
    "by_moneyness": {
      "otm_call_oi": 85000,
      "atm_call_oi": 25000,
      "itm_call_oi": 10000,
      "otm_put_oi": 60000,
      "atm_put_oi": 20000,
      "itm_put_oi": 15000
    }
  },
  "oi_concentration": {
    "top_3_pct": 18.5,
    "top_5_pct": 28.2,
    "top_10_pct": 45.0,
    "herfindahl": 0.032
  },
  "hedging_scenarios": [
    { "move_pct": -5, "dealer_shares": 850000, "direction": "buy", "notional_usd": 225575000 },
    { "move_pct": -2, "dealer_shares": 340000, "direction": "buy", "notional_usd": 90230000 },
    { "move_pct": -1, "dealer_shares": 170000, "direction": "buy", "notional_usd": 45115000 },
    { "move_pct": 1, "dealer_shares": -170000, "direction": "sell", "notional_usd": 45115000 },
    { "move_pct": 2, "dealer_shares": -340000, "direction": "sell", "notional_usd": 90230000 },
    { "move_pct": 5, "dealer_shares": -850000, "direction": "sell", "notional_usd": 225575000 }
  ],
  "liquidity": {
    "atm_avg_spread_pct": 1.85,
    "wing_avg_spread_pct": 45.20,
    "atm_contracts": 120,
    "wing_contracts": 340
  }
}

Response fields

Section Field Description
realized_vol rv_5d through rv_60d Annualized realized vol (%) for 5, 10, 20, 30, 60 day windows. Log-returns, sample std dev, ×√252.
atm_iv - Average IV (%) of up to four eligible OTM contracts nearest spot, using the first eligible expiry at least seven calendar days away, or the earliest eligible expiry if none qualifies. No SVI fit is used.
iv_rv_spreads vrp_Nd IV minus RV spread per window. Positive = IV rich.
iv_rv_spreads assessment VRP regime: very_high_premium, healthy_premium, moderate_premium, thin_premium, negative_spread, danger_zone
skew_profiles[] put_10d_iv, put_25d_iv, atm_iv, call_25d_iv, call_10d_iv Contract-based IVs per expiry, not SVI-interpolated. Wings use the nearest eligible same-side OTM contract by delta, without a maximum delta-distance tolerance; 10d and 25d may select the same contract. ATM averages up to four eligible OTM contracts nearest spot.
skew_profiles[] skew_25d 25-delta risk reversal: put_25d - call_25d
skew_profiles[] smile_ratio Put 25d IV / Call 25d IV. >1 = put skew dominant
skew_profiles[] tail_convexity Wing curvature: (put10-put25) - (put25-ATM). Positive = steep tail
term_structure near_slope_pct (30d IV - 7d IV) / 7d IV × 100
term_structure far_slope_pct (90d IV - 30d IV) / 30d IV × 100
term_structure state contango, backwardation, mixed, or unknown
iv_dispersion cross_expiry Std dev of ATM IVs across expirations (term structure dispersion)
iv_dispersion cross_strike Std dev of IVs across strikes at nearest expiry (smile dispersion)
gex_by_dte[] bucket, net_gex, pct_of_total, contract_count GEX bucketed by DTE: 0-7d, 8-30d, 31-60d, 60d+
theta_by_dte[] bucket, net_theta, contract_count Aggregate theta × OI × 100 per DTE bucket
put_call_profile by_expiry[] P/C ratio (OI and volume) per expiration
put_call_profile by_moneyness OI breakdown: OTM/ATM/ITM for calls and puts (ATM = within 5% of spot)
oi_concentration top_3_pct, top_5_pct, top_10_pct % of total OI in top N strikes
oi_concentration herfindahl Herfindahl index (0-1). Higher = more concentrated
hedging_scenarios[] move_pct, dealer_shares, direction, notional_usd Estimated dealer hedging flow for ±1%, ±2%, ±5% moves
liquidity atm_avg_spread_pct Average bid-ask spread % for ATM options (within 2% of spot)
liquidity wing_avg_spread_pct Average bid-ask spread % for wings (>10% OTM)
historical_context observation_date, age_calendar_days, atm_iv_pct, trailing_rv_pct, iv_minus_trailing_rv_vol_points Latest completed persisted nightly VRP observation with prior-only iv_reference / rv_reference / trailing_spread_reference (percentile, z-score, sample size). Describes that stored observation, not the live atm_iv above. status is partial, stale (older than the previous session) or unavailable with reasons (for example persisted_context_read_timeout); a storage failure never fails the live response.

Notes

  1. Realized vol uses close-to-close log-returns with sample standard deviation (N-1), annualized by √252. This matches Yahoo Finance, Bloomberg, and QuantLib conventions.
  2. Skew profiles select actual eligible OTM contracts nearest each target delta, without interpolation or a maximum delta-distance tolerance. The 10-delta and 25-delta fields can select the same contract and report identical IVs. See the skew term structure notes for eligibility rules.
  3. Tail convexity measures the second difference of the skew. Positive values indicate steep left-wing curvature (crash protection being bid).
  4. GEX by DTE uses the SpotGamma convention: gamma × OI × 100 × spot² × 0.01. Calls positive, puts negated.
  5. Herfindahl index ranges from 1/N (perfectly even) to 1.0 (all OI at one strike). Values above 0.10 suggest high concentration.
  6. Hedging scenarios assume dealers are net short options. Positive GEX + upward move = dealers sell shares (negative delta hedge).

GET /v1/adv_volatility/{symbol}

Returns advanced volatility analytics for quantitative analysis: raw SVI parameters per expiry, implied forward prices, total variance surface grid, butterfly and calendar arbitrage detection, second/third-order greeks surfaces (vanna, charm, volga, speed), and variance swap fair values.

Requires Alpha plan or higher.

Parameters

Name In Required Description
symbol path yes Underlying symbol (e.g. SPY)

Example

curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/adv_volatility/SPY"

Response 200

{
  "symbol": "SPY",
  "underlying_price": 580.51,
  "as_of": "2026-03-25T15:00:00Z",
  "market_open": true,

  "svi_parameters": [
    {
      "expiry": "2026-04-04",
      "days_to_expiry": 10,
      "forward": 581.25,
      "a": 0.004521,
      "b": 0.031245,
      "rho": -0.182345,
      "m": 0.012345,
      "sigma": 0.098765,
      "atm_total_variance": 0.008912,
      "atm_iv": 17.85
    }
  ],

  "forward_prices": [
    {
      "expiry": "2026-04-04",
      "days_to_expiry": 10,
      "forward": 581.25,
      "spot": 580.51,
      "basis_pct": 0.1275
    }
  ],

  "total_variance_surface": {
    "moneyness": [-0.5, -0.475, "...", 0.475, 0.5],
    "expiries": ["2026-04-04", "2026-04-17"],
    "tenors": [0.027397, 0.063014],
    "total_variance": [[0.0089, 0.0087, "..."], ["..."]],
    "implied_vol": [[18.05, 17.92, "..."], ["..."]]
  },

  "arbitrage_flags": [
    {
      "expiry": "2026-04-04",
      "type": "butterfly",
      "strike_or_k": -0.35,
      "description": "Negative butterfly at k=-0.350: d²w/dk²=-0.0152"
    },
    {
      "expiry": "2026-04-04/2026-04-17",
      "type": "calendar",
      "strike_or_k": 0.2,
      "description": "Calendar arb at k=0.2: w(2026-04-04)=0.0125 > w(2026-04-17)=0.0118"
    }
  ],

  "variance_swap_fair_values": [
    {
      "expiry": "2026-04-04",
      "days_to_expiry": 10,
      "fair_variance": 0.009234,
      "fair_vol": 18.35,
      "atm_iv": 17.85,
      "convexity_adjustment": 0.50
    }
  ],

  "greeks_surfaces": {
    "vanna": {
      "strikes": [555, 560, 565, "...", 600, 605],
      "expiries": ["2026-04-04", "2026-04-17"],
      "values": [[0.00012345, 0.00023456, "..."], ["..."]]
    },
    "charm": { "strikes": ["..."], "expiries": ["..."], "values": [["..."]] },
    "volga": { "strikes": ["..."], "expiries": ["..."], "values": [["..."]] },
    "speed": { "strikes": ["..."], "expiries": ["..."], "values": [["..."]] }
  }
}

Response Fields

Section Field Description
SVI Parameters Raw SVI (Stochastic Volatility Inspired) fit parameters per expiry slice
svi_parameters[] a, b, rho, m, sigma SVI raw parameterization: w(k) = a + b(ρ(k−m) + √((k−m)² + σ²))
svi_parameters[] forward Implied forward price from put-call parity
svi_parameters[] atm_total_variance w(0) - total variance at-the-money
svi_parameters[] atm_iv ATM implied vol (%) derived from total variance
Forward Prices Implied forward prices per expiry with cost-of-carry basis
forward_prices[] basis_pct (Forward − Spot) / Spot × 100
Total Variance Surface 2D grid of total variance w(k,T) and implied vol across moneyness and expiries
total_variance_surface moneyness Log-moneyness grid: −0.5 to +0.5 in 0.025 steps (41 points)
total_variance_surface tenors Time to expiry in years for each expiry
total_variance_surface total_variance w(k,T) values - rows are expiries, columns are moneyness
total_variance_surface implied_vol IV (%) derived from total variance: σ = √(w/T) × 100
Arbitrage Flags Detected no-arbitrage violations in the fitted surface
arbitrage_flags[] type butterfly (d²w/dk² < 0) or calendar (w decreasing in T)
arbitrage_flags[] strike_or_k Log-moneyness where violation occurs
Variance Swap Fair variance swap values per expiry via numerical SVI integration
variance_swap_fair_values[] fair_variance Integrated fair variance over [−15%, +15%] moneyness
variance_swap_fair_values[] fair_vol √(fair_variance / T) × 100
variance_swap_fair_values[] convexity_adjustment fair_vol − atm_iv - positive = convexity premium from wings
Greeks Surfaces 2D grids of higher-order greeks across strikes (±15% of spot) and expiries
greeks_surfaces.vanna ∂²V/∂S∂σ - delta sensitivity to vol changes
greeks_surfaces.charm ∂²V/∂S∂t - delta decay over time (call options)
greeks_surfaces.volga ∂²V/∂σ² - vega convexity
greeks_surfaces.speed ∂³V/∂S³ - gamma sensitivity to spot

Errors

Status Description
403 Requires Alpha plan or higher
404 Symbol not found or no data

Notes

  • SVI fitting uses the raw parameterization of Gatheral (2004). Fits are filtered - slices with poor ATM IV or too few data points are rejected.
  • Arbitrage detection uses a tolerance of 0.01 for butterfly and 0.001 for calendar to filter numerical noise.
  • Greeks surfaces are computed via BSM formulas from stored BSM IV per contract (not from SVI). Strike range is ±15% of spot.
  • Variance swap fair value uses trapezoidal integration over ±15% moneyness range (500 steps). Wing extrapolation beyond this range is excluded to avoid SVI noise.

GET /v1/vrp/{symbol}

Full Volatility Risk Premium dashboard. Combines live IV/RV/GEX data with historical percentiles, regime classification, strategy suitability scores, and macro context.

When the gamma regime is unknown, regime.gamma is unknown. A numeric flip can remain available while spot sits at it and its stressed side cannot be certified; a missing flip has its withholding reason in regime.gamma_flip_status. In either case, gex_conditioned, vanna_conditioned, regime.vrp_regime, gamma-dependent strategy scores (short_put_spread, short_strangle, iron_condor), and net_harvest_score are null. Core IV/RV/VRP measurements and the independent calendar_spread score remain available when their own inputs exist; missing gamma is not a bearish fallback or a zero score.

Requires Alpha plan or higher.

Parameters

Parameter Type Description
symbol path Stock/ETF ticker (e.g., SPY, QQQ, TSLA)
date query Optional historical date (yyyy-MM-dd). When supplied, returns the persisted VRP snapshot for that date instead of the live dashboard; 404 if no snapshot exists for that date.

Response

Field Type Description
Core VRP
vrp.atm_iv number ATM implied volatility (%, from BSM on live options via Thetadata)
vrp.rv_5d ... rv_30d number Realized volatility windows (annualized %, from Yahoo Finance closes)
vrp.vrp_5d ... vrp_30d number VRP spread = ATM_IV - RV for each window
vrp.z_score number? Z-score of current VRP vs 60-day history (null if < 5 days history)
vrp.percentile number? Percentile rank of current VRP (0-100, null if insufficient history)
Variance Premium
variance_risk_premium number VRP in variance space (IV^2 - RV^2), per Carr & Wu (2009)
convexity_premium number Fair vol (OI-weighted) minus ATM IV - positive = wings are rich
fair_vol number OI-weighted average IV across ±20% moneyness
Directional VRP
directional.put_wing_iv_25d number 25-delta put IV (%)
directional.call_wing_iv_25d number 25-delta call IV (%)
directional.downside_rv_20d number RV from negative returns only (downside semivariance)
directional.upside_rv_20d number RV from positive returns only
directional.downside_vrp number Put-wing IV minus downside RV
directional.upside_vrp number Call-wing IV minus upside RV
Term VRP Curve
term_vrp[] array VRP by expiry bucket (7, 14, 30, 60 DTE)
term_vrp[].dte number Days to expiry
term_vrp[].iv number ATM IV for this tenor
term_vrp[].rv number Term-matched realized vol
term_vrp[].vrp number VRP spread for this tenor
GEX-Conditioned
gex_conditioned object? Null when gamma flip or VRP inputs are unavailable.
gex_conditioned.regime string positive_gamma or negative_gamma when the block is available.
gex_conditioned.harvest_score number 0-1 harvestability score (higher = safer to sell premium)
gex_conditioned.interpretation string Human-readable regime interpretation
Vanna-Conditioned
vanna_conditioned object? Null when gamma flip or required VRP inputs are unavailable.
vanna_conditioned.outlook string Vol compression/expansion outlook
vanna_conditioned.interpretation string How vanna flows affect premium selling
Regime
regime.gamma string positive_gamma, negative_gamma, or unknown when the flip is unavailable.
regime.vrp_regime string? harvestable, event_only, toxic_short_vol, cheap_convexity, or surface_distorted
regime.net_gex number Net gamma exposure ($)
regime.gamma_flip number? Published underlying-price boundary of aggregate repriced GEX; null when withheld.
regime.gamma_flip_status string available when the level is certified. A numeric gamma_flip with any of sensitive_root, uncertain_root_path, quality_budget, uncertain_gamma_variance, insufficient_local_coverage or insufficient_quote_quality is an unverified level: the root exists but failed that publication gate, and regime is unknown. Any other status means no supportable root and gamma_flip is null.
Strategy Scores (0-100)
strategy_scores.short_put_spread number? Short put spread suitability; null without a published gamma flip.
strategy_scores.short_strangle number? Short strangle suitability; null without a published gamma flip.
strategy_scores.iron_condor number? Iron condor suitability; null without a published gamma flip.
strategy_scores.calendar_spread number Calendar spread suitability
Composite
net_harvest_score number? 0-100 composite; null when gamma flip or required VRP inputs are unavailable.
dealer_flow_risk number 0-100 dealer hedging amplification risk
warnings string[] Active warning flags: event_risk, negative_gamma, poor_wing_liquidity, term_inversion, credit_stress, extreme_vrp
Macro
macro.vix number VIX spot
macro.vix_3m number VIX3M (3-month VIX)
macro.vix_term_slope number VIX3M/VIX ratio (>1 = contango, <1 = backwardation)
macro.dgs10 number 10-Year Treasury yield
macro.hy_spread number ICE BofA High Yield OAS (credit stress indicator)
macro.fed_funds number Federal Funds rate

Errors

Status Description
403 Requires Alpha plan or higher
404 Symbol not found or no data

GET /v1/vrp/{symbol}/history

Daily VRP time series for charting and backtesting. Populated by nightly data service after market close.

Requires Alpha plan or higher.

Parameters

Parameter Type Default Description
symbol path Stock/ETF ticker
days query 30 Lookback days (1-365). Legacy series only.
series query legacy legacy (stored nightly values) or normalized (constant-tenor archive reconstruction; release pending).
start / end query end = yesterday ET, start = end − 29 days normalized only. YYYY-MM-DD, 2017 or later, at most 366 calendar days, end before today ET. SPY/QQQ.
tenor_days query 30 normalized only. IV tenor: 30, 60 or 90.

Response

Field Type Description
symbol string Ticker symbol
days number Requested lookback
data_points number Number of data points returned
history[].date date Snapshot date
history[].spot number Close price
history[].atm_iv number? ATM IV (%)
history[].rv_5d ... rv_30d number? Realized vol windows
history[].vrp_20d number? VRP spread (ATM_IV - RV20d)
history[].straddle number? ATM straddle price ($)
history[].expected_move_1d number? 1-day expected move ($)

Notes

  • History accumulates daily after market close (4:35 PM ET). Z-score and percentile become available after 5+ data points.
  • series=normalized is release pending: it returns 503 historical_source_unhealthy (reason: daily_series_disabled) until the daily-series release is enabled. It has a different response shape; see daily history series. Mixing days with normalized, or start/end/tenor_days with legacy, returns 400 invalid_history_request; any other series value returns 400 invalid_series.
  • ATM IV is computed via BSM from Thetadata option snapshots (standalone, not dependent on real-time pollers).
  • Realized vol is computed from Yahoo Finance historical closes using log returns and sample standard deviation.
  • Data sources: spot (Thetadata/Yahoo), RV (Yahoo), IV (Thetadata + BSM), macro (FRED + Yahoo VIX).

Daily history series (release pending)

Alpha+ daily range operations for SPY and QQQ, reconstructed from end-of-day archive quotes on historical.flashalpha.com. The live host forwards your credentials and returns the archive response unchanged.

Release pending: these routes return 503 historical_source_unhealthy with reason: daily_series_disabled until the archive, closing-time alignment and deployed-path checks pass.

Route Returns
GET /v1/volatility/skew-term/{symbol}/history?start=&end=&tenor_days=30 Constant-tenor ATM and 25-delta wing IV, skew, risk reversal, butterfly, 1/5-observation changes, prior-only percentile/z-score
GET /v1/volatility/term-structure/{symbol}/history?start=&end= 30/60/90-day ATM IV, 60−30 slope, 30→60 forward variance (negative values reported, never clipped)
GET /v1/expected-move/{symbol}/history?start=&end=&tenor_days=30 ATM-IV sigma band per date vs the matured terminal move and recorded intraperiod breaches; pending outcomes stay null
GET /v1/vrp/{symbol}/history?series=normalized&start=&end=&tenor_days=30 Constant-tenor IV minus trailing 20-session realized vol, IV/RV ratio, variance-spread proxy, prior-only reference

Bounds: dates in 2017 or later, start <= end, at most 366 calendar days, end before today ET; tenors 30, 60 or 90. Results are EOD model reconstructions with unknown original publication time (available_at: null), not an as-served signal archive. Formulas, coverage fields and failure semantics: trader-context reference.


GET /v1/account

Returns account details, plan, and usage quota for the authenticated user.

Example

curl -H "X-Api-Key: YOUR_API_KEY" https://lab.flashalpha.com/v1/account

Response 200

{
  "user_id": "d621e6ab-b4fc-4d62-86fc-08e489477e11",
  "email": "[email protected]",
  "plan": "growth",
  "daily_limit": "1000",
  "usage_today": 42,
  "remaining": "958",
  "resets_at": "2026-03-09T00:00:00Z"
}
Field Type Description
user_id string User's unique identifier (GUID)
email string User's email address
plan string Plan name: free, basic, growth, alpha, enterprise
daily_limit string Daily request limit, or "unlimited" for Business/Institutional tiers
usage_today integer Number of API requests made today (always 0 for unlimited plans)
remaining string Requests remaining today, or "unlimited"
resets_at string ISO 8601 timestamp when the daily quota resets (midnight UTC)

GET /health

Returns system health status. Public endpoint - no authentication required. Not rate limited.

Example

curl https://lab.flashalpha.com/health

Response 200

{
  "status": "Healthy"
}

Strategy Signals

The strategy-signal endpoints are decision-support tools: they read the same chain, exposure, and flow primitives that power the rest of the API and fold them into a single, uniform decision envelope. Each endpoint scores one trading idea (flow imbalance, pin risk, dealer gamma regime, vol carry, yield, surface/skew/term/tail richness) on a 0-100 scale, classifies a regime, and - where the strategy expresses a trade - returns ranked structure candidates with legs, credit/debit, and breakevens.

Every endpoint in this group returns the same StrategyDecisionResponse shape regardless of strategy, so a client can deserialize and rank results from any of them without strategy-specific handling. The fields metrics and regime are the only parts that vary per strategy: metrics is an open string-keyed bag of strategy-specific numbers, and regime is a short label drawn from a strategy-specific vocabulary. Both are documented per endpoint below.

All endpoints are GET, take the symbol in the path, accept optional tuning parameters on the query string, and require an API key via the X-Api-Key header. The score maps to decision through fixed bands: >= 70 → candidate, >= 40 → neutral, otherwise avoid; insufficient_data is emitted when inputs are missing.

Strategy decision envelope

{
  "strategy": "flow_anomaly",          // which strategy produced this result
  "symbol": "SPY",                     // resolved (upper-cased) underlying
  "timestamp": "2026-06-05T14:32:11Z", // server UTC time the decision was built
  "decision": "candidate",             // insufficient_data | avoid | neutral | candidate
  "score": 72,                         // 0-100; drives the decision band
  "confidence": 0.83,                  // 0-1 input-quality / sample-size weight
  "regime": "bullish_flow_imbalance",  // strategy-specific regime label (see each endpoint)
  "best_structures": [                 // ranked tradeable structures (may be empty)
    {
      "rank": 1,
      "structure": "short_put_spread",
      "expiry": "2026-06-19",
      "legs": [
        { "action": "sell", "type": "put", "strike": 588, "delta": -0.25, "premium": 1.42, "quantity": 1 },
        { "action": "buy",  "type": "put", "strike": 578, "delta": -0.12, "premium": 0.72, "quantity": 1 }
      ],
      "credit": 0.70,
      "debit": null,
      "max_profit": 0.70,
      "max_loss": 9.30,
      "breakevens": [587.30],
      "edge_score": 72,
      "liquidity_score": 0.88
    }
  ],
  "metrics": {                         // strategy-specific bag (varies per endpoint)
    "call_put_volume_ratio": 1.84,
    "underlying_price": 589.12
  },
  "risk_flags": [                      // optional risk callouts (often empty)
    { "severity": "medium", "code": "EARNINGS_BEFORE_EXPIRY", "message": "An earnings event falls between today and the target expiry." }
  ],
  "why": ["Call volume outweighs put volume by 1.84x."],  // human-readable rationale
  "avoid_if": ["Volume imbalance fades intraday or flow turns from sweep-driven to block-driven."],
  "data_quality": {                    // gate on this before trading
    "score": 90,
    "warnings": []
  }
}
Field Type Description
strategy string The strategy that produced the result (e.g. flow_anomaly, vol_carry).
symbol string Resolved, upper-cased underlying symbol.
timestamp string (ISO 8601 UTC) When the decision was built.
decision string One of insufficient_data, avoid, neutral, candidate. Derived from score via the decision bands.
score integer 0-100 strategy score.
confidence number 0-1 weight reflecting input quality / sample size.
regime string Strategy-specific regime label. The vocabulary differs per endpoint (documented below).
best_structures array Ranked candidate structures. Empty when no tradeable structure clears the filters (pure-signal endpoints always return an empty array). Each item carries rank, structure, expiry, legs[] (action, type, strike, delta, premium, quantity), credit, debit, max_profit, max_loss, breakevens[], edge_score, liquidity_score.
metrics object Strategy-specific key/value bag. Keys vary per endpoint (documented below); underlying_price is always present.
risk_flags array Optional risk callouts, each with severity (low/medium/high), code, and message.
why array of string Human-readable rationale for the decision.
avoid_if array of string Conditions under which the read should be discarded.
data_quality object score (0-100) and warnings[]. Gate on this before acting.

metrics and regime are the only parts of the envelope that change between strategies. Everything else has a fixed shape.

All strategy endpoints can also short-circuit with decision: insufficient_data and regime: symbol_not_covered_historically when the symbol is outside historical coverage.

Expiry rules (2026.10.23). Every structure in best_structures is built inside one expiry: all legs share the expiry shown, and credit is always below the wing width, so max_loss is always negative. When expiry is omitted, multi-day structures (flow anomaly, dealer regime, vol carry) and yield enhancement use the nearest expiry 7 to 45 days out that yields a valid structure. A same-day expiry stops counting once it settles (16:00 ET for equities and ETFs; index and futures roots use their own settlement time), so after the close no route offers it. An expiry before today returns 400 expiry_in_past.


GET /v1/strategies/flow-anomaly/{symbol}

Scores directional options-flow imbalance (call vs put volume) from the live chain and, when the flow is one-sided, proposes the matching short vertical spread.

Requires Growth plan or higher.

Parameters

Name In Required Default Description
symbol path yes - Underlying symbol (e.g. SPY).
expiry query no full chain (all upcoming expiries) yyyy-MM-dd. Restricts the analysis to a single expiry. When omitted, the flow read uses the full chain and the suggested spread uses the nearest expiry 7 to 45 days out.

Example

curl "https://lab.flashalpha.com/v1/strategies/flow-anomaly/SPY" \
  -H "X-Api-Key: YOUR_API_KEY"

Response 200

{
  "strategy": "flow_anomaly",
  "symbol": "SPY",
  "timestamp": "2026-06-05T14:32:11Z",
  "decision": "candidate",
  "score": 72,
  "confidence": 0.83,
  "regime": "bullish_flow_imbalance",
  "best_structures": [
    {
      "rank": 1,
      "structure": "short_put_spread",
      "expiry": "2026-06-19",
      "legs": [
        { "action": "sell", "type": "put", "strike": 588, "delta": -0.25, "premium": 1.42, "quantity": 1 },
        { "action": "buy",  "type": "put", "strike": 578, "delta": -0.12, "premium": 0.72, "quantity": 1 }
      ],
      "credit": 0.70,
      "debit": null,
      "max_profit": 0.70,
      "max_loss": 9.30,
      "breakevens": [587.30],
      "edge_score": 72,
      "liquidity_score": 0.88
    }
  ],
  "metrics": {
    "bias": "bullish",
    "call_put_volume_ratio": 1.84,
    "call_put_oi_ratio": 1.12,
    "total_volume": 412300,
    "total_open_interest": 1875400,
    "underlying_price": 589.12,
    "dominant_premium_usd": 38420000,
    "dominant_premium_pct": 0.63,
    "top3_strikes_volume_pct": 0.41
  },
  "risk_flags": [],
  "why": ["Call volume outweighs put volume by 1.84x."],
  "avoid_if": ["Volume imbalance fades intraday or flow turns from sweep-driven to block-driven."],
  "data_quality": { "score": 90, "warnings": [] }
}

Notable metrics: bias, call_put_volume_ratio, call_put_oi_ratio, total_volume, total_open_interest, dominant_premium_usd, dominant_premium_pct, top3_strikes_volume_pct, underlying_price.

regime values: bullish_flow_imbalance, bearish_flow_imbalance, neutral_flow.

Errors

Status error When
400 invalid_expiry expiry is present but not yyyy-MM-dd.
400 expiry_in_past expiry is a date before today (ET).
403 tier_restricted Caller is below the Growth plan.
404 symbol_not_found No market data for the symbol.

GET /v1/strategies/expiry-positioning/{symbol}

Scores OPEX pin risk for a single expiry from max-pain, OI concentration, and dealer levels (gamma flip, call/put walls), and proposes an iron fly when a pin is likely.

Requires Basic plan or higher.

Parameters

Name In Required Default Description
symbol path yes - Underlying symbol.
expiry query no nearest upcoming expiry yyyy-MM-dd. The expiry to analyse.
minOpenInterest query no 250 Minimum OI a leg must have to be selected.
wingWidth query no 5.0 Iron-fly wing width, in strike points.

Example

curl "https://lab.flashalpha.com/v1/strategies/expiry-positioning/SPY?expiry=2026-06-19&wingWidth=5" \
  -H "X-Api-Key: YOUR_API_KEY"

Returns the strategy decision envelope with strategy-specific metrics and regime.

Notable metrics: max_pain_strike, distance_to_pain_pct, oi_concentration_score, total_open_interest, expiry, days_to_expiry, gamma_flip, call_wall, put_wall, distance_to_flip_pct, spot_position_label, underlying_price.

regime values: strong_pin_likely, moderate_pin, no_pin_setup.

Errors

Status error When
400 invalid_expiry expiry is present but not yyyy-MM-dd.
400 expiry_in_past expiry is a date before today (ET).
403 tier_restricted Caller is below the Basic plan.
404 symbol_not_found No market data for the symbol.

GET /v1/strategies/zero-dte/{symbol}

Same-day (0DTE) range-compression read: combines pin/positioning with intraday time-of-day context (minutes to close, expected-move consumed, theta acceleration) and proposes an iron fly when the pin is strong enough.

Requires Growth plan or higher, and also requires 0DTE access (0DTE entitlement is gated separately from the plan tier). Defaults to today's expiry when expiry is omitted.

Parameters

Name In Required Default Description
symbol path yes - Underlying symbol.
expiry query no today (same-day expiry) yyyy-MM-dd. Overrides the same-day default.
minOpenInterest query no 500 Minimum OI a leg must have to be selected.
wingWidth query no 5.0 Iron-fly wing width, in strike points.

Example

curl "https://lab.flashalpha.com/v1/strategies/zero-dte/SPY" \
  -H "X-Api-Key: YOUR_API_KEY"

Returns the strategy decision envelope with strategy-specific metrics and regime.

Notable metrics: max_pain_strike, distance_to_pain_pct, oi_concentration_score, total_open_interest, gamma_flip, call_wall, put_wall, distance_to_flip_pct, spot_position_label, minutes_to_close, session_open_spot, expected_move_today, expected_move_consumed_pct, theta_acceleration, underlying_price.

regime values: pin_risk_positive_gamma, range_compression, trend_risk_or_no_setup; plus no_same_day_expiry / no_expiry_chain (returned with decision: insufficient_data when no chain exists for the selected expiry) and same_day_expiry_settled (today's expiry has already settled, for example after the 16:00 ET close).

Errors

Status error When
400 invalid_expiry expiry is present but not yyyy-MM-dd.
400 expiry_in_past expiry is a date before today (ET).
403 tier_restricted Caller is below the Growth plan (or lacks 0DTE access).
404 symbol_not_found No market data for the symbol.

GET /v1/strategies/dealer-regime/{symbol}

Classifies the dealer gamma regime (positive-gamma compression vs negative-gamma acceleration) from the sign of net GEX, dealer levels, and full vanna/charm exposure, and proposes an iron condor in compressive regimes.

net_gex and net_dex here are the canonical figures, computed with the formulas in Exposure units. Which exposure route they equal depends on whether you pass expiry:

Request Matches
expiry omitted /v1/exposure/summary - both aggregate every upcoming expiry
expiry=YYYY-MM-DD /v1/exposure/gex and /v1/exposure/dex with ?expiration= the same date

/v1/exposure/summary takes no expiry parameter and is always full-chain, so a filtered request here will not match it.

This endpoint classifies the sign of net GEX without requiring a published gamma flip. Exposure endpoints compare spot with the published repriced boundary and return unknown when it is withheld. For a published flip on the same chain and price anchor, the boundary calculation reconciles the spot-GEX sign. See Gamma regime vs net GEX.

Requires Growth plan or higher.

Parameters

Name In Required Default Description
symbol path yes - Underlying symbol.
expiry query no all upcoming expiries yyyy-MM-dd. Restricts exposure aggregation and the iron condor to a single expiry. Only expiries that have not settled are aggregated. A date in the past returns 400 expiry_in_past. When omitted, the condor uses the nearest expiry 7 to 45 days out.

Example

curl "https://lab.flashalpha.com/v1/strategies/dealer-regime/SPY" \
  -H "X-Api-Key: YOUR_API_KEY"

Returns the strategy decision envelope with strategy-specific metrics and regime.

Notable metrics: net_gex, net_dex, contracts_total, contracts_with_gamma, oi_with_gamma_pct, gamma_calculation, gamma_flip, call_wall, put_wall, distance_to_flip_pct, spot_position_label, net_vex, net_chex, underlying_price, plus the deprecated net_gamma, net_delta and gamma_source.

Field Description
net_gex Canonical net gamma exposure, gamma x OI x multiplier x spot^2 x 0.01. Equals /v1/exposure/summary unfiltered, or /v1/exposure/gex?expiration= when expiry is passed. This is the field regime is classified from.
net_dex Canonical net delta exposure, delta x OI x multiplier x spot. Equals /v1/exposure/summary unfiltered, or /v1/exposure/dex?expiration= when expiry is passed.
contracts_total Contracts in the chain for the selected expiries.
contracts_with_gamma Of those, how many carry solved greeks.
oi_with_gamma_pct Share of chain open interest carried by contracts with solved greeks. OI-weighted, not a contract count - a chain can be half priced by count and 90% priced by OI. Low coverage lowers confidence; it never rejects the request.
gamma_calculation Always priced_contracts_only. Contracts the pricing engine could not solve - a one-sided book, or a mid outside the no-arbitrage bounds - contribute zero gamma exposure. Their open interest still reaches every OI-based analytic (max pain, pin scoring, OI concentration).
gamma_source Deprecated. Describes the CHAIN (real / mixed / proxy by how many contracts are priced), not the calculation. The proxy token is retained for wire compatibility and no longer implies a substitution was made; use gamma_calculation and the coverage counts.
net_gamma Deprecated. Unscaled sum(sign x gamma x OI) over priced contracts. Carries no multiplier and no spot-squared term, so it is not comparable to net_gex and cannot be rescaled into it. Use net_gex.
net_delta Deprecated. Unscaled sum(sign x delta x OI) over priced contracts. Negates put delta, which the canonical DEX convention does not. Use net_dex.

Behaviour change, 2026.08.31: contracts without solved greeks previously had their missing gamma substituted with a dimensionless open-interest kernel, which could outweigh every priced contract on the chain and invert the published sign. That substitution is removed. regime, net_gamma, score and confidence all move as a result, and score in particular now reads a quantity roughly two to three orders of magnitude smaller than before - band recalibration is a separate change.

regime values: positive_gamma_compression (net_gex > 0), negative_gamma_acceleration (net_gex < 0), transition (net_gex is exactly flat, or no contract in the chain could be priced). There is no magnitude threshold: transition means the sign is undefined, not that exposure is small.

Errors

Status error When
400 invalid_expiry expiry is present but not yyyy-MM-dd.
400 expiry_in_past expiry is a date before today (ET).
403 tier_restricted Caller is below the Growth plan.
404 symbol_not_found No market data for the symbol.

GET /v1/strategies/vol-carry/{symbol}

Volatility risk-premium carry: scores implied vol richness against realized vol using historical VRP percentile/z-score, with skew, term-slope, RV-trend, and earnings context, and proposes short put spreads / iron condors when carry is favourable. Returns decision: insufficient_data (regime: vrp_unavailable) when the historical VRP path is unavailable for the symbol.

Requires Alpha plan or higher.

Parameters

Name In Required Default Description
symbol path yes - Underlying symbol.
expiry query no nearest upcoming expiry yyyy-MM-dd. Target expiry for structure selection and context. When omitted, context uses the nearest unsettled expiry and structures use the nearest expiry 7 to 45 days out.
minOpenInterest query no 250 Minimum OI a leg must have to be selected.
targetShortDelta query no 0.25 Target delta for the short leg.
maxWidth query no 10.0 Maximum spread/wing width, in strike points.
minCredit query no 0.10 Minimum credit a candidate must collect.

Example

curl "https://lab.flashalpha.com/v1/strategies/vol-carry/SPY?targetShortDelta=0.20&maxWidth=10" \
  -H "X-Api-Key: YOUR_API_KEY"

Returns the strategy decision envelope with strategy-specific metrics and regime.

Notable metrics: atm_iv, realized_vol, vrp, vrp_z_score, vrp_percentile, used_real_metrics, skew_25d, risk_reversal_25d, term_slope, rv_change_5d, earnings_before_expiry, recommended_side, underlying_price.

regime values: rich_iv_stable_realized_vol, fair_iv_neutral_realized_vol, cheap_iv_or_unstable_realized_vol; plus vrp_unavailable (insufficient-data case).

Errors

Status error When
400 invalid_expiry expiry is present but not yyyy-MM-dd.
400 expiry_in_past expiry is a date before today (ET).
403 tier_restricted Caller is below the Alpha plan.
404 symbol_not_found No market data for the symbol.

GET /v1/strategies/yield-enhancement/{symbol}

Income overlay selection: finds the best covered call or cash-secured put on the chain at a target delta, with annualized yield, assignment probability, breakeven, roll candidate, and an earnings filter. Emits EARNINGS_BEFORE_EXPIRY and EX_DIVIDEND_RISK_NOT_MODELED risk flags where applicable. metrics.income_diagnostics adds bid-based cash arithmetic for one standard 100-share equity contract.

Requires Growth plan or higher.

Parameters

Name In Required Default Description
symbol path yes - Underlying symbol.
expiry query no nearest expiry 7 to 45 days out with a candidate yyyy-MM-dd. Restricts selection to a single expiry. When omitted, expiries 7 to 45 days out are tried nearest first, and the contract nearest the target absolute delta in the first expiry with a candidate is chosen.
targetDelta query no 0.25 Target absolute delta for the short option.
minOpenInterest query no 250 Minimum OI a candidate must have (nonnegative).
structure query no covered_call covered_call or cash_secured_put.
excludeEarningsBeforeExpiry query no true When true, penalises/flags picks whose expiry spans an earnings event.
stockCostBasis query no - Positive USD per share. Required for covered-call income returns; spot is never substituted.
feesPerContract query no - Nonnegative USD opening fee per contract. Without it, net fields are null.

Example

curl "https://lab.flashalpha.com/v1/strategies/yield-enhancement/SPY?structure=cash_secured_put&targetDelta=0.20" \
  -H "X-Api-Key: YOUR_API_KEY"

Returns the strategy decision envelope with strategy-specific metrics and regime.

Notable metrics: structure, strike, delta, premium, annualized_yield_pct, assignment_probability, upside_surrender_pct, dte, earnings_before_expiry, ex_dividend_risk, breakeven_price, iv_rank_30d, roll_candidate, next_earnings_date, days_to_next_earnings, underlying_price, income_diagnostics, income_diagnostics_status, earnings_risk_status, absolute_delta_proxy, assignment_probability_method, legacy_quote_basis.

income_diagnostics (standard equity contracts; null for index and futures underlyings): bid_per_share, ask_per_share, mid_per_share, quote_at, quote_timestamp_status, gross_credit_usd (bid × 100), fees_per_contract_usd, net_credit_usd, collateral_usd (strike × 100 for puts; cost basis × 100 for calls), gross_income_period_return_pct, net_income_period_return_pct, period_days, gross_breakeven_price, net_breakeven_price, gross_max_profit_usd, gross_max_loss_usd, gross_capped_total_return_pct (covered calls), absolute_delta_proxy, earnings_risk_status, ex_dividend_risk_status (unknown) and methodology_url. The legacy premium and ranking still use mid; assignment_probability is the same absolute-delta proxy, not a calibrated probability, and annualized_yield_pct is an arithmetic extrapolation, not a return forecast.

earnings_risk_status: event_before_expiry, none_on_loaded_calendar or unknown (calendar unavailable, or a same-day event without reported actuals). Every listed contract for the symbol is eligible within the chosen expiry (for SPX this includes SPXW weeklies). earnings_before_expiry, the score penalty, the EARNINGS_BEFORE_EXPIRY flag, earnings_risk_status and income_diagnostics all describe the selected contract's expiry; diagnostics are produced only for a standard-root equity contract.

regime values: covered_call_income, cash_secured_put_income, no_yield_candidate.

Errors

Status error When
400 invalid_structure structure is not covered_call or cash_secured_put.
400 invalid_expiry expiry is present but not yyyy-MM-dd.
400 expiry_in_past expiry is a date before today (ET).
400 invalid_income_parameters stockCostBasis not positive or feesPerContract negative, or either not finite. Existing parameters are not newly validated.
403 tier_restricted Caller is below the Growth plan.
404 symbol_not_found No market data for the symbol.

GET /v1/strategies/surface-anomaly/{symbol}

Compares observed IVs against the calibrated SVI fit for an expiry to find rich/cheap wings (per-strike residuals, fit quality, cheap-convexity score), and proposes the obvious vertical-credit sale on a rich wing. Returns decision: insufficient_data (regime: no_fitted_surface) when no fitted SVI slice is available.

Requires Alpha plan or higher.

Parameters

Name In Required Default Description
symbol path yes - Underlying symbol.
expiry query no nearest fitted SVI slice yyyy-MM-dd. The expiry slice to analyse.

Example

curl "https://lab.flashalpha.com/v1/strategies/surface-anomaly/SPY?expiry=2026-06-19" \
  -H "X-Api-Key: YOUR_API_KEY"

Returns the strategy decision envelope with strategy-specific metrics and regime.

Notable metrics: expiry, put_wing_richness, call_wing_richness, dominant_wing, sample_count, forward, per_strike_residuals, fit_quality_score, cheap_convexity_score, underlying_price.

regime values: rich_put_wing, rich_call_wing, flat_residuals; plus no_fitted_surface (insufficient-data case).

Errors

Status error When
400 invalid_expiry expiry is present but not yyyy-MM-dd.
400 expiry_in_past expiry is a date before today (ET).
403 tier_restricted Caller is below the Alpha plan.
404 symbol_not_found No market data for the symbol.

GET /v1/strategies/skew/{symbol}

Pure-signal read of 25-delta skew (put vs call wing richness and the risk reversal) for one expiry. No structure is selected; best_structures is always empty and decision is neutral. Returns decision: insufficient_data (regime: no_skew_data) when both 25Δ wings can't be priced.

Requires Growth plan or higher.

Parameters

Name In Required Default Description
symbol path yes - Underlying symbol.
expiry query no nearest upcoming expiry yyyy-MM-dd. The expiry to read.

Example

curl "https://lab.flashalpha.com/v1/strategies/skew/SPY" \
  -H "X-Api-Key: YOUR_API_KEY"

Returns the strategy decision envelope with strategy-specific metrics and regime.

Notable metrics: expiry, days_to_expiry, atm_iv, call_iv_25d, put_iv_25d, risk_reversal_25d, skew_slope, dominant_wing, sample_count, underlying_price.

regime values: put_skew, call_skew, flat_skew; plus no_skew_data (insufficient-data case).

Errors

Status error When
400 invalid_expiry expiry is present but not yyyy-MM-dd.
400 expiry_in_past expiry is a date before today (ET).
403 tier_restricted Caller is below the Growth plan.
404 symbol_not_found No market data for the symbol.

GET /v1/strategies/term-structure/{symbol}

Pure-signal read of the ATM implied-vol term structure across all upcoming expiries (contango vs backwardation, front/back IV, slope, per-expiry points). No structure is selected; decision is neutral. Returns decision: insufficient_data (regime: no_term_structure_data) when no expiry has a usable ATM IV.

Requires Growth plan or higher.

Parameters

Name In Required Default Description
symbol path yes - Underlying symbol. This endpoint takes no tuning parameters.

Example

curl "https://lab.flashalpha.com/v1/strategies/term-structure/SPY" \
  -H "X-Api-Key: YOUR_API_KEY"

Returns the strategy decision envelope with strategy-specific metrics and regime.

Notable metrics: front_iv, back_iv, slope, shape, expiry_count, term_points (array of { expiry, dte, atm_iv }), underlying_price.

regime values: contango, backwardation, flat_term; plus no_term_structure_data (insufficient-data case).

Errors

Status error When
403 tier_restricted Caller is below the Growth plan.
404 symbol_not_found No market data for the symbol.

GET /v1/strategies/tail-pricing/{symbol}

Pure-signal read of downside-tail pricing for one expiry: 10-delta put IV vs ATM (downside skew), tail asymmetry, and whether the crash tail is rich or cheap. No structure is selected; decision is neutral. Returns decision: insufficient_data (regime: no_tail_data) when the ATM or 10Δ put quote is missing.

Requires Growth plan or higher.

Parameters

Name In Required Default Description
symbol path yes - Underlying symbol.
expiry query no nearest upcoming expiry yyyy-MM-dd. The expiry to read.

Example

curl "https://lab.flashalpha.com/v1/strategies/tail-pricing/SPY?expiry=2026-06-19" \
  -H "X-Api-Key: YOUR_API_KEY"

Returns the strategy decision envelope with strategy-specific metrics and regime.

Notable metrics: expiry, days_to_expiry, atm_iv, put_tail_iv_10d, call_tail_iv_10d, downside_skew, tail_asymmetry, sample_count, underlying_price.

regime values: expensive_tail, cheap_tail, symmetric_tail; plus no_tail_data (insufficient-data case).

Errors

Status error When
400 invalid_expiry expiry is present but not yyyy-MM-dd.
400 expiry_in_past expiry is a date before today (ET).
403 tier_restricted Caller is below the Growth plan.
404 symbol_not_found No market data for the symbol.

GET /v1/strategies/sell-window/{symbol}

A daily answer to one question, built from more than 5 million backtests: should you sell options today? For four listed expiries (same day, and the expiries closest to one week, two weeks and one month out) it says sell, skip or unavailable. On a sell it also suggests positions, with concrete strikes from the option chain, each contract's delta and quote, the credit at mid and the exit rule. On a skip there is nothing to do.

Covered symbols: SPY, QQQ, IWM. Any other symbol returns every window as unavailable.

The answer is set from the previous session's close and stays fixed for the session. It switches at the close (16:00 ET, 13:00 ET on early-close days): before the close it is for the current session, after the close and on closed days it is for the next session.

Plans: Basic or higher through November 2026, Growth or higher from December 1, 2026. Backtesting from Alpha: the historical API returns the answer for any past session with ?at=. On it, the strikes come from the archive chain at at; the backtests entered at 09:35 ET, so at=<session>T09:35:00 returns the backtest's entry strikes. From the live launch on, the action and trades match what this endpoint answered at that time. Before it they are rebuilt from the archive with the same rules. See the backtest and the trading guide.

How strikes are picked (the backtests' contract rules): only out-of-the-money contracts with a bid above zero, a bid/ask spread of at most 20% of the mid and open interest of at least 250. Among those, the contract whose delta is closest to the trade's target and within 0.05 of it; ties go to the tighter spread, then the higher open interest. A trade whose legs cannot all be found in the chain is left out, because the backtest could not have filled it either.

This endpoint does not return the strategy decision envelope; it has its own shape, below.

Parameters

Name In Required Default Description
symbol path yes - Underlying symbol. SPY, QQQ or IWM.

Example

curl "https://lab.flashalpha.com/v1/strategies/sell-window/SPY" \
  -H "X-Api-Key: YOUR_API_KEY"

A skip day (SPY, 2026-11-05):

{
  "symbol": "SPY",
  "session": "2026-11-05",
  "windows": [
    { "expiry": "2026-11-05", "days_to_expiry": 0, "action": "skip" },
    { "expiry": "2026-11-12", "days_to_expiry": 7, "action": "skip" },
    { "expiry": "2026-11-18", "days_to_expiry": 13, "action": "skip" },
    { "expiry": "2026-12-04", "days_to_expiry": 29, "action": "skip" }
  ]
}

A sell day (SPY, 2026-10-16, replayed at 09:35 ET; the first window and two of its trades shown):

{
  "symbol": "SPY",
  "session": "2026-10-16",
  "spot": 772.255,
  "quoted_at": "2026-10-16T13:35:00Z",
  "legs_indicative": false,
  "windows": [
    { "expiry": "2026-10-16", "days_to_expiry": 0, "action": "sell",
      "candidates": [
        { "structure": "short_put", "exit": "expiry",
          "legs": [ {"side": "sell", "quantity": 1, "type": "put", "strike": 769, "delta": -0.19, "bid": 0.39, "ask": 0.4} ],
          "mid_credit": 0.395 },
        { "structure": "short_strangle", "exit": "expiry",
          "legs": [ {"side": "sell", "quantity": 1, "type": "call", "strike": 774, "delta": 0.302, "bid": 0.63, "ask": 0.64},
                    {"side": "sell", "quantity": 1, "type": "put", "strike": 770, "delta": -0.264, "bid": 0.58, "ask": 0.59} ],
          "mid_credit": 1.22 }
      ] }
  ]
}

Response fields

Field Type Description
symbol string The symbol, upper case.
session string yyyy-MM-dd. The trading session the answer applies to.
spot number Underlying mid the strikes were picked against. Present when a window sells.
quoted_at string UTC time of the quotes behind the strikes. Present when a window sells.
legs_indicative boolean true when the quotes are from before that session's 09:35 ET entry: call again at or after 09:35 ET for the strikes to trade. Present when a window sells.
endpoint_version, data_as_of The response envelope fields every response carries.
windows array Always four entries, in this order: the same-day expiry, then the expiries closest to one week, two weeks and one month out.
windows[].expiry string yyyy-MM-dd. The listed expiry this window trades: the one closest to 0, 7, 14 or 30 calendar days after the session (within 0, 4-10, 11-17 and 25-35 days). Absent when no listed expiry falls in that range.
windows[].days_to_expiry integer Calendar days from session to expiry.
windows[].action string sell: a session to sell at this expiry. skip: stand aside, no new trade. unavailable: no answer (a symbol outside coverage or a missing day of data); treat it like a skip.
windows[].candidates array Present only when action is sell. The trades to consider, in a fixed order.
windows[].candidates[].structure string short_put, short_call, short_strangle, short_straddle, tail_hedged_put (short put with a far out-of-the-money put bought below it), put_credit_spread or put_bwb (put broken-wing butterfly: buy one put above, sell two, buy one further below, for a credit).
windows[].candidates[].exit string expiry: hold to expiration. tp50: close once the position can be bought back for half of the credit or less, otherwise hold to expiration.
windows[].candidates[].legs array The contracts of one unit of the trade.
legs[].side string sell or buy.
legs[].quantity integer Contracts per unit of the trade (2 for the sold body of put_bwb, otherwise 1).
legs[].type string call or put.
legs[].strike number Strike price.
legs[].delta number The contract's own delta when the answer was served (negative for puts).
legs[].bid, legs[].ask number The contract's quote when the answer was served.
windows[].candidates[].mid_credit number Net credit per share at mid: sold mids minus bought mids, times quantity. Multiply by 100 for one unit in dollars.

Errors

Status error When
403 tier_restricted Caller is below the required plan.

The historical API version (GET /v1/strategies/sell-window/{symbol}?at=... on the historical host, Alpha or higher) also returns 400 invalid_at when at is malformed, in the future, or a time that does not exist in ET (the daylight-saving gap).


Earnings

Earnings-event analytics derived from the upcoming/historical earnings calendar plus live options term structure: the calendar, earnings-implied expected move decomposition, IV-crush expectations, post-earnings VRP (implied vs. realized event moves), dealer positioning scoped to the event expiry, and strategy-suitability scoring. Earnings event data (dates, EPS/revenue actuals, fiscal periods, importance) is sourced from Finnhub; live IV, term structure, and exposure are computed from the on-demand options pipeline.


GET /v1/earnings/calendar

Upcoming earnings calendar over a forward window, optionally filtered to specific symbols and a minimum importance rating.

Requires Growth plan or higher.

Parameters

Name In Required Default Description
days query no 14 Forward window in days. Clamped to 1-90.
symbols query no - Comma-separated list of symbols to filter to (case-insensitive, e.g. AAPL,MSFT). Omit for the full calendar.
importance query no - Minimum importance rating; only events with importance >= this value are returned.

Example

curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/earnings/calendar?days=14&symbols=AAPL,MSFT&importance=3"

Response 200

{
  "events": [
    {
      "symbol": "AAPL",
      "company_name": "Apple Inc",
      "earnings_date": "2026-06-09",
      "timing": "amc",
      "is_confirmed": true,
      "fiscal_period": "Q3",
      "fiscal_year": 2026,
      "importance": 5,
      "eps_estimate": 1.42,
      "implied_move_pct": 4.8,
      "days_to_event": 4
    }
  ],
  "count": 1
}

Response Fields

Field Description
events[].timing Session of the report - typically bmo (before market open) or amc (after market close); may be null/unknown.
events[].is_confirmed Whether the date is confirmed (vs. estimated) by the provider.
events[].importance Provider importance rating (higher = more market-moving); nullable.
events[].eps_estimate Consensus EPS estimate; nullable.
events[].implied_move_pct Stored earnings-implied move (%), if available; nullable.
events[].days_to_event Calendar days from today (UTC) to the earnings date.

Errors

Status When
400 Invalid query parameter.
403 Caller is below the Growth tier.
404 no_data - no upcoming earnings in the requested window.

GET /v1/earnings/expected-move/{symbol}

Live earnings-implied move decomposition for the next event, splitting the front-expiry straddle into the earnings-jump component vs. baseline diffusion drift using the pre/post-event SVI term structure.

Requires Growth plan or higher.

Parameters

Name In Required Default Description
symbol path yes - Underlying symbol (case-insensitive).

Example

curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/earnings/expected-move/AAPL"

Response 200

{
  "symbol": "AAPL",
  "underlying_price": 212.34,
  "as_of": "2026-06-05T15:42:10Z",
  "earnings_date": "2026-06-09",
  "session": "amc",
  "days_to_event": 4,
  "expected_move": {
    "raw_straddle_pct": 5.1,
    "earnings_implied_pct": 4.6,
    "baseline_drift_pct": 1.2,
    "earnings_iv": 68.4,
    "term_iv_post_event": 41.2,
    "term_kink_pct": 27.2
  }
}

Response Fields

Field Description
underlying_price Spot (quote mid) used for the decomposition.
session Reporting session of the event (bmo/amc).
expected_move Null when the pre/post-event expiry IVs cannot be resolved from the live term structure (no valid straddling expiries).
expected_move.raw_straddle_pct Total expected move (%) implied by the front straddle.
expected_move.earnings_implied_pct Portion of the move (%) attributed to the earnings jump.
expected_move.baseline_drift_pct Portion of the move (%) attributed to ordinary baseline diffusion.
expected_move.earnings_iv ATM IV (%) of the pre-event (front) expiry.
expected_move.term_iv_post_event ATM IV (%) of the first post-event expiry.
expected_move.term_kink_pct Term-structure kink (%) across the event - elevation of front vs. post-event IV.

Errors

Status When
403 Caller is below the Growth tier.
404 no_data - no upcoming earnings for the symbol; or symbol_not_found - no live market data for the symbol.

GET /v1/earnings/history/{symbol}

Past earnings events for the symbol with EPS/revenue actuals and surprises, implied vs. actual moves, and realized IV crush.

Requires Growth plan or higher.

Parameters

Name In Required Default Description
symbol path yes - Underlying symbol (case-insensitive).
limit query no 12 Number of most-recent reported events to return. Clamped to 1-40.

Example

curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/earnings/history/AAPL?limit=12"

Response 200

{
  "symbol": "AAPL",
  "count": 2,
  "history": [
    {
      "date": "2026-04-30",
      "fiscal_period": "Q2",
      "fiscal_year": 2026,
      "eps_estimate": 1.50,
      "eps_actual": 1.58,
      "eps_surprise_pct": 5.33,
      "revenue_actual": 95800000000,
      "revenue_surprise_pct": 1.2,
      "implied_move_pct": 4.9,
      "actual_move_pct": -3.1,
      "iv_crush_pct": 38.5,
      "pre_atm_iv": 64.2,
      "post_atm_iv": 39.5
    }
  ]
}

Response Fields

Field Description
history[].eps_surprise_pct EPS surprise vs. estimate (%); positive = beat.
history[].revenue_surprise_pct Revenue surprise vs. estimate (%); nullable.
history[].implied_move_pct Earnings-implied move (%) priced before the event.
history[].actual_move_pct Realized post-earnings move (%); sign indicates direction.
history[].iv_crush_pct Realized IV crush (%) from pre- to post-event ATM IV.
history[].pre_atm_iv / post_atm_iv Stored ATM IVs (%). Legacy backfill took both from one pre-event snapshot (earnings-loaded vs clean expiry), so iv_crush_pct is a cross-expiry proxy, not a measured before/after decline.
history[].timing Report timing as stored (BMO, AMC, …); nullable.
history[].report_date_basis calendar_report_date when the date has confirmed Yahoo provenance, otherwise unknown.
history[].iv_decline_proxy_vol_points pre_atm_iv − post_atm_iv in vol points when both are finite.
history[].outcome_status / observation_window_status / historical_available_at historical_proxy_provenance_unavailable, not_persisted and null: legacy rows do not record their source windows or original availability.
historical_context Coverage of the returned sample: event, actual-move, implied-move, paired and IV-pair counts with their missing counts, proxy medians, first/last event dates, coverage_status and methodology_url. Descriptive only; verified_comparable_window_count is always 0.

history rows are unchanged: the latest stored events with an EPS actual. historical_context is computed separately from completed reports only: events dated before today (ET), one per fiscal quarter, where a finite EPS actual or realized move establishes the report; its canonical row prefers stored move/IV measurements, then Yahoo-confirmed dates, then the freshest write.

Errors

Status When
400 Invalid limit.
403 Caller is below the Growth tier.
404 no_data - no reported earnings history for the symbol.

GET /v1/earnings/iv-crush/{symbol}

Expected IV crush for the next event plus the symbol's historical IV-crush distribution. The live estimate is derived from the current pre/post-event term structure; the distribution uses the latest 20 stored iv_crush_pct values, as before. historical_context is computed separately from completed reports (one per fiscal quarter, dated before today), so its counts can differ from count.

Requires Growth plan or higher.

Parameters

Name In Required Default Description
symbol path yes - Underlying symbol (case-insensitive).

Example

curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/earnings/iv-crush/AAPL"

Response 200

{
  "symbol": "AAPL",
  "as_of": "2026-06-05T15:42:10Z",
  "earnings_date": "2026-06-09",
  "current_estimate": {
    "expected_crush_pct": 41.23,
    "pre_iv": 67.8,
    "post_iv": 39.85
  },
  "distribution": {
    "median": 38.5,
    "p25": 31.0,
    "p75": 45.2,
    "worst": 12.4,
    "best": 58.9,
    "count": 12
  }
}

Response Fields

Field Description
earnings_date Next event date; null when no upcoming event but history exists.
current_estimate Live crush estimate; null when no upcoming event or the term structure can't be resolved.
current_estimate.expected_crush_pct Expected ATM IV drop (%) from pre to post event = (pre_iv − post_iv) / pre_iv × 100.
current_estimate.pre_iv / post_iv ATM IV (%) of the latest pre-event and earliest post-event expiry.
distribution.median / p25 / p75 Historical realized crush (%) percentiles.
distribution.worst / best Smallest and largest historical crush (%).
distribution.count Number of historical events in the distribution.
historical_context Coverage counts, proxy medians and definitions link for the same 20-event sample; see earnings history.
methodology_url Definitions of the stored proxies and the sample rule.

Errors

Status When
403 Caller is below the Growth tier.
404 no_data - no upcoming event and no IV-crush history for the symbol.

GET /v1/earnings/vrp/{symbol}

Earnings volatility-risk-premium: the live event-implied move vs. the symbol's realized history of actual moves, with a richness assessment and surprise-reaction breakdown.

Requires Alpha plan or higher.

Parameters

Name In Required Default Description
symbol path yes - Underlying symbol (case-insensitive).

Example

curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/earnings/vrp/AAPL"

Response 200

{
  "symbol": "AAPL",
  "underlying_price": 212.34,
  "as_of": "2026-06-05T15:42:10Z",
  "earnings_date": "2026-06-09",
  "days_to_event": 4,
  "earnings_vrp": {
    "implied_move_pct": 4.6,
    "realized_median": 3.1,
    "realized_mean": 3.4,
    "premium_ratio": 1.48,
    "z_score": 0.92,
    "percentile": 70,
    "assessment": "slightly_rich",
    "directional_bias": "downside_overpriced"
  },
  "surprise_reaction": {
    "beat_avg_move_pct": 2.8,
    "miss_avg_move_pct": -5.1,
    "inline_avg_move_pct": 0.4
  }
}

Response Fields

Field Description
earnings_vrp.implied_move_pct Live earnings-implied move (%); falls back to the stored implied move if the term structure can't be decomposed.
earnings_vrp.realized_median / realized_mean Median / mean of historical absolute actual moves (%).
earnings_vrp.premium_ratio implied_move / realized_median - >1 means options are pricing more than history realized. Null when the implied move isn't priced yet (assessment: implied_unavailable) or there's no realized history (insufficient_data) - never 0.
earnings_vrp.z_score Implied move vs. realized distribution (z); null when fewer than 5 historical moves.
earnings_vrp.percentile Percentile of the implied move within historical realized moves; null when fewer than 5 events.
earnings_vrp.assessment Richness classification: rich, slightly_rich, fair, slightly_cheap, cheap, insufficient_data (no realized history), or implied_unavailable (the event-implied move can't be priced/derived yet - e.g. weeks before the report the front expiry carries no earnings premium - so richness is unassessable and premium_ratio is null).
earnings_vrp.directional_bias downside_overpriced / upside_overpriced when one side's historical moves dominate; otherwise null.
surprise_reaction.beat_avg_move_pct Avg actual move (%) following EPS beats (surprise > 1%); nullable.
surprise_reaction.miss_avg_move_pct Avg actual move (%) following EPS misses (surprise < −1%); nullable.
surprise_reaction.inline_avg_move_pct Avg actual move (%) following in-line results (−1%…+1% surprise); nullable.

Errors

Status When
403 Caller is below the Alpha tier.
404 no_data - no upcoming earnings for the symbol; or symbol_not_found - no live market data.

GET /v1/earnings/dealer-positioning/{symbol}

Dealer exposure analysis scoped to the earnings event: gamma flip / call & put walls on the event-week expiries, net GEX bucketed by pre-event / event-week / post-event, charm acceleration into the event, and the top strikes by absolute net GEX.

Requires Alpha plan or higher.

Parameters

Name In Required Default Description
symbol path yes - Underlying symbol (case-insensitive).

Example

curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/earnings/dealer-positioning/AAPL"

Response 200

{
  "symbol": "AAPL",
  "underlying_price": 212.34,
  "as_of": "2026-06-05T15:42:10Z",
  "earnings_date": "2026-06-09",
  "event_expiry": "2026-06-12",
  "levels": {
    "gamma_flip": 210.0,
    "call_wall": 220.0,
    "put_wall": 205.0,
    "highest_oi_strike": 215.0
  },
  "gex_by_dte_bucket": [
    { "bucket": "pre_event", "net_gex": 120000000, "contract_count": 48 },
    { "bucket": "event_week", "net_gex": -85000000, "contract_count": 36 },
    { "bucket": "post_event", "net_gex": 340000000, "contract_count": 90 }
  ],
  "top_strikes": [
    { "strike": 220.0, "net_gex": 65000000, "call_oi": 18500, "put_oi": 2100 }
  ],
  "charm_acceleration": 0.42,
  "regime": "negative_gamma"
}

Response Fields

Field Description
event_expiry Closest options expiry on or after the earnings date; null if none found.
levels.gamma_flip Underlying price where aggregate repriced GEX crosses from negative to positive (event-week scope); nullable.
levels.gamma_flip_status available when the level is certified. A numeric gamma_flip with any of sensitive_root, uncertain_root_path, quality_budget, uncertain_gamma_variance, insufficient_local_coverage or insufficient_quote_quality is an unverified level: the root exists but failed that publication gate, and regime is unknown. Any other status means no supportable root and gamma_flip is null.
levels.call_wall / put_wall Largest positive-GEX strike above / largest below spot; nullable.
levels.highest_oi_strike Strike with the most open interest (event-week scope); nullable.
gex_by_dte_bucket[] Net GEX and contract count for the pre_event, event_week, and post_event expiry buckets (buckets with no contracts are omitted).
top_strikes[] Up to 5 strikes ranked by absolute net GEX, with per-strike call/put OI.
charm_acceleration Ratio of event-expiry CHEX to full-chain CHEX - how concentrated charm flow is into the event; null when not computable.
regime positive_gamma when spot ≥ gamma flip, negative_gamma when below, unknown when no flip is published.

Errors

Status When
403 Caller is below the Alpha tier.
404 no_data - no upcoming earnings for the symbol; or symbol_not_found - no live market data.

GET /v1/earnings/strategies/{symbol}

Strategy-suitability scores (0-100) for the upcoming event across common earnings structures, blending implied move, VRP premium ratio, expected IV crush, ATM liquidity, and the gamma regime.

Requires Alpha plan or higher.

Parameters

Name In Required Default Description
symbol path yes - Underlying symbol (case-insensitive).

Example

curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/earnings/strategies/AAPL"

Response 200

{
  "symbol": "AAPL",
  "as_of": "2026-06-05T15:42:10Z",
  "earnings_date": "2026-06-09",
  "scores": {
    "long_straddle": 38,
    "short_strangle": 72,
    "iron_condor": 65,
    "calendar_spread": 58,
    "earnings_diagonal": 61
  },
  "context": {
    "premium_ratio": 1.48,
    "iv_crush_median": 38.5,
    "regime": "negative_gamma",
    "implied_move_pct": 4.6
  }
}

Response Fields

Field Description
scores.long_straddle Suitability (0-100) for a long straddle (favored when premium is cheap / negative gamma).
scores.short_strangle Suitability for a short strangle (favored when premium is rich, positive gamma, high crush).
scores.iron_condor Suitability for an iron condor (defined-risk premium sale).
scores.calendar_spread Suitability for a calendar spread.
scores.earnings_diagonal Suitability for an earnings diagonal.
context.premium_ratio VRP premium ratio (implied / realized-median) feeding the scores.
context.iv_crush_median Median historical IV crush (%) feeding the scores.
context.regime Gamma regime: positive_gamma, negative_gamma, or unknown. Unknown contributes no gamma adjustment to earnings strategy scores.
context.implied_move_pct Earnings-implied move (%) used as the scoring input.

Errors

Status When
403 Caller is below the Alpha tier.
404 no_data - no upcoming earnings for the symbol; or symbol_not_found - no live market data.

GET /v1/earnings/screener

Cross-sectional screener over upcoming earnings in a forward window, ranked by VRP richness, cheapest implied move, highest historical crush, or importance.

Requires Alpha plan or higher.

Parameters

Name In Required Default Description
sort query no vrp_richest Ranking: vrp_richest (highest premium ratio), cheapest_move (lowest implied move), highest_crush (highest median IV crush), or importance. Unrecognized values fall back to vrp_richest.
limit query no 20 Max rows returned. Clamped to 1-300.
days query no 14 Forward window in days. Clamped to 1-60.
min_importance query no - Only include events with importance >= this value.

Example

curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/earnings/screener?sort=vrp_richest&limit=20&days=14&min_importance=3"

Response 200

{
  "events": [
    {
      "symbol": "AAPL",
      "company_name": "Apple Inc",
      "earnings_date": "2026-06-09",
      "days_to_event": 4,
      "timing": "amc",
      "importance": 5,
      "implied_move_pct": 4.6,
      "premium_ratio": 1.48,
      "iv_crush_median": 38.5,
      "expected_crush_pct": 37.2,
      "assessment": "slightly_rich"
    }
  ],
  "count": 1
}

Response Fields

Field Description
events[].premium_ratio VRP premium ratio (implied / realized-median); null when no historical actual moves exist or when the stored implied move isn't populated for the event (see note below). Never 0.
events[].iv_crush_median Median historical IV crush (%); null when no crush history. Requires per-event historical ATM IV, which exists only where the options archive reaches, so it is null for most listed reports - prefer expected_crush_pct.
events[].expected_crush_pct Forward-looking IV crush (%) from the current term structure: how far the earnings-loaded front IV sits above the settled neighbour. Derived from the same live SVI legs as the implied move, so it is populated wherever implied_move_pct is.
events[].assessment VRP richness classification (rich, slightly_rich, fair, slightly_cheap, cheap, insufficient_data, or implied_unavailable); null when no history.

Screener coverage caveat: unlike /earnings/vrp, the screener does not compute the implied move live from the term structure - it reads the stored implied_move_pct, which is only populated for a subset of events. When it's absent, premium_ratio / assessment are null even for large, well-covered names, and the vrp_richest / cheapest_move sorts degrade to date order. For a reliable per-symbol richness read use GET /v1/earnings/vrp/{symbol}. | count | Total matched events before limit is applied (the events array is capped at limit). |

Errors

Status When
400 Invalid query parameter.
403 Caller is below the Alpha tier.
404 no_data - no upcoming earnings in the requested window.

Structures

Pure-math multi-leg structure utilities - at-expiry P&L diagrams, breakevens, and aggregate position Greeks. Every result here is a deterministic function of the legs you supply: there is no market-data lookup, no symbol resolution, and no live IV. Pass the legs, premiums, and (for Greeks) the spot/IV you want priced against, and the response is computed analytically.


POST /v1/structures/pnl

At-expiry profit-and-loss curve, breakevens, and max profit/loss for an arbitrary multi-leg option structure.

Requires Basic plan or higher. Starter (Free) accounts receive 403 tier_restricted.

Request body

{
  "legs": [
    { "action": "buy",  "type": "call", "strike": 100, "premium": 3.20, "quantity": 1 },
    { "action": "sell", "type": "call", "strike": 110, "premium": 1.10, "quantity": 1 }
  ],
  "minUnderlying": 80,
  "maxUnderlying": 130,
  "points": 81
}
Field Type Required Default Description
legs array yes - One or more legs. Must be non-empty.
legs[].action string yes "buy" buy (alias long) or sell (alias short).
legs[].type string yes "call" call (alias c) or put (alias p).
legs[].strike number yes - Strike price. Must be > 0.
legs[].premium number yes - Per-contract premium paid/received. Must be >= 0.
legs[].quantity integer no 1 Number of contracts. Must be > 0.
minUnderlying number no derived Lower bound of the underlying-price curve. If omitted (or not strictly below maxUnderlying), the range is derived from the leg strikes ±30%.
maxUnderlying number no derived Upper bound of the curve. See minUnderlying.
points integer no 81 Number of equally-spaced curve sample points (endpoints inclusive). Clamped to a minimum of 2.

The payoff is piecewise-linear in the underlying, so breakevens and the bounded max/min are solved exactly from the kinks at the strikes. max_profit / max_loss are null on any side that is unbounded (e.g. a naked long call has unbounded max_profit; a naked short call has unbounded max_loss).

Example

curl -X POST "https://lab.flashalpha.com/v1/structures/pnl" \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
        "legs": [
          { "action": "buy",  "type": "call", "strike": 100, "premium": 3.20, "quantity": 1 },
          { "action": "sell", "type": "call", "strike": 110, "premium": 1.10, "quantity": 1 }
        ],
        "minUnderlying": 80,
        "maxUnderlying": 130,
        "points": 81
      }'

Response 200

{
  "legs": [
    { "action": "buy",  "type": "call", "strike": 100, "premium": 3.2, "quantity": 1 },
    { "action": "sell", "type": "call", "strike": 110, "premium": 1.1, "quantity": 1 }
  ],
  "pnl_curve": [
    { "underlying": 80,    "pnl": -2.1 },
    { "underlying": 80.625, "pnl": -2.1 },
    { "underlying": 100,   "pnl": -2.1 },
    { "underlying": 110,   "pnl": 7.9 },
    { "underlying": 130,   "pnl": 7.9 }
  ],
  "breakevens": [102.1],
  "max_profit": 7.9,
  "max_loss": -2.1
}

legs echoes the request body verbatim. Each pnl_curve entry is { underlying, pnl }. breakevens is an array of underlying prices where P&L crosses zero (may be empty). max_profit and max_loss are numbers, or null when unbounded on that side.

Errors

Status error When
400 empty_legs legs is missing or empty.
400 invalid_action leg[i].action is not buy/sell (or long/short).
400 invalid_type leg[i].type is not call/put (or c/p).
400 invalid_strike leg[i].strike <= 0.
400 invalid_premium leg[i].premium < 0.
400 invalid_quantity leg[i].quantity <= 0.
403 tier_restricted Starter (Free) plan. Requires Basic or higher.

POST /v1/structures/greeks

Aggregate Black-Scholes Greeks across a multi-leg position. Each leg carries its own expiry and implied vol, so calendars and diagonals aggregate correctly.

Requires Basic plan or higher. Starter (Free) accounts receive 403 tier_restricted.

Request body

{
  "legs": [
    { "action": "buy",  "type": "call", "strike": 100, "expiry": "2026-07-17", "impliedVol": 0.28, "quantity": 1 },
    { "action": "sell", "type": "call", "strike": 110, "expiry": "2026-07-17", "impliedVol": 0.25, "quantity": 1 }
  ],
  "spot": 102.5,
  "today": "2026-06-05",
  "rate": 0.045,
  "dividendYield": 0.013
}
Field Type Required Default Description
legs array yes - One or more legs. Must be non-empty.
legs[].action string yes "buy" buy (alias long) or sell (alias short).
legs[].type string yes "call" call (alias c) or put (alias p).
legs[].strike number yes - Strike price. Must be > 0.
legs[].expiry string yes - Leg expiry, YYYY-MM-DD.
legs[].impliedVol number yes - Implied volatility as a decimal (e.g. 0.28). Must be > 0.
legs[].quantity integer no 1 Number of contracts. Must be > 0.
spot number yes - Underlying spot price priced against. Must be > 0.
today string no today (UTC) Valuation date, YYYY-MM-DD.
rate number no 0.045 Risk-free rate (decimal).
dividendYield number no 0.013 Continuous dividend yield (decimal).

Each Greek is signed for direction (long +, short −) and scaled by quantity, then summed across legs. Legs that have already expired relative to today contribute zero.

Example

curl -X POST "https://lab.flashalpha.com/v1/structures/greeks" \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
        "legs": [
          { "action": "buy",  "type": "call", "strike": 100, "expiry": "2026-07-17", "impliedVol": 0.28, "quantity": 1 },
          { "action": "sell", "type": "call", "strike": 110, "expiry": "2026-07-17", "impliedVol": 0.25, "quantity": 1 }
        ],
        "spot": 102.5,
        "today": "2026-06-05",
        "rate": 0.045,
        "dividendYield": 0.013
      }'

Response 200

{
  "spot": 102.5,
  "as_of": "2026-06-05T14:32:10.123Z",
  "valuation_date": "2026-06-05",
  "rate": 0.045,
  "dividend_yield": 0.013,
  "legs": [
    { "action": "buy",  "type": "call", "strike": 100, "expiry": "2026-07-17", "impliedVol": 0.28, "quantity": 1 },
    { "action": "sell", "type": "call", "strike": 110, "expiry": "2026-07-17", "impliedVol": 0.25, "quantity": 1 }
  ],
  "position_greeks": {
    "delta": 0.3142,
    "gamma": 0.0184,
    "theta": -0.0421,
    "vega": 0.1163,
    "rho": 0.0457,
    "vanna": -0.0039,
    "charm": 0.0008
  }
}

legs echoes the request body. valuation_date is the resolved today. position_greeks carries the aggregated delta, gamma, theta, vega, rho, vanna, and charm. (Top-level response keys are snake_case; the echoed legs use the same camelCase field names as the request body.)

Errors

Status error When
400 empty_legs legs is missing or empty.
400 invalid_spot spot <= 0.
400 invalid_action leg[i].action is not buy/sell (or long/short).
400 invalid_type leg[i].type is not call/put (or c/p).
400 invalid_strike leg[i].strike <= 0.
400 invalid_iv leg[i].impliedVol <= 0.
400 invalid_expiry leg[i].expiry is not a valid YYYY-MM-DD date.
400 invalid_quantity leg[i].quantity <= 0.
403 tier_restricted Starter (Free) plan. Requires Basic or higher.

GET /v1/surface/svi/{symbol}

Live SVI-fitted volatility surface for a symbol - the calibrated (a, b, ρ, m, σ) parameters per expiry slice, with ATM total variance and ATM IV. A lightweight subset of the full advanced-volatility payload for clients that reconstruct the surface themselves.

Requires Alpha plan or higher. Lower tiers receive 403 tier_restricted.

Parameters

Name In Required Default Description
symbol path yes - Underlying ticker (case-insensitive; normalized to uppercase).

Example

curl "https://lab.flashalpha.com/v1/surface/svi/SPY" \
  -H "X-Api-Key: YOUR_API_KEY"

Response 200

{
  "symbol": "SPY",
  "underlying_price": 542.18,
  "as_of": "2026-06-05T14:32:10.123Z",
  "market_open": true,
  "svi_parameters": [
    {
      "expiry": "2026-06-20",
      "days_to_expiry": 15,
      "forward": 542.91,
      "a": 0.001204,
      "b": 0.142318,
      "rho": -0.612045,
      "m": 0.018221,
      "sigma": 0.094117,
      "atm_total_variance": 0.002918,
      "atm_iv": 16.42
    },
    {
      "expiry": "2026-07-18",
      "days_to_expiry": 43,
      "forward": 543.77,
      "a": 0.002981,
      "b": 0.158902,
      "rho": -0.584311,
      "m": 0.021004,
      "sigma": 0.112744,
      "atm_total_variance": 0.006611,
      "atm_iv": 17.05
    }
  ]
}

underlying_price is the mid of the underlying. svi_parameters is ordered by days_to_expiry; each slice gives the raw SVI parameters a, b, rho, m, sigma, the per-expiry forward, the ATM total variance (atm_total_variance), and the ATM implied vol as a percentage (atm_iv).

Errors

Status error When
403 tier_restricted Tier below Alpha. Requires Alpha or higher.
404 symbol_not_found No data for the symbol. Message notes when the market is closed (9:30 AM - 4:00 PM ET).

GET /v1/expected-move/{symbol}

Straddle-implied expected move per expiry, derived from ATM implied volatility. Generalizes the earnings-only expected move to any upcoming expiry.

Requires Basic plan or higher. Starter (Free) accounts receive 403 tier_restricted.

Parameters

Name In Required Default Description
symbol path yes - Underlying ticker (case-insensitive; normalized to uppercase).
expiry query no all expiries Restrict the result to a single expiry, YYYY-MM-DD. Omit to receive every upcoming expiry.

Example

curl "https://lab.flashalpha.com/v1/expected-move/SPY?expiry=2026-06-20" \
  -H "X-Api-Key: YOUR_API_KEY"

Response 200

{
  "symbol": "SPY",
  "underlying_price": 542.18,
  "as_of": "2026-06-05T14:32:10.123Z",
  "expected_moves": [
    {
      "expiry": "2026-06-20",
      "daysToExpiry": 15,
      "atmIv": 0.1642,
      "expectedMove": 17.6234,
      "expectedMovePct": 3.2504,
      "lowerBound": 524.5566,
      "upperBound": 559.8034
    }
  ]
}

The top-level keys are snake_case (underlying_price, as_of, expected_moves); the items inside expected_moves use camelCase. expected_moves is ordered by expiry. For each expiry: daysToExpiry (calendar days), atmIv (ATM implied vol as a decimal, or null when no ATM IV can be derived), expectedMove (1-σ move in price terms), expectedMovePct (as a percentage of spot), and the lowerBound / upperBound of the implied range (spot ∓ expectedMove).

Errors

Status error When
400 invalid_expiry expiry query parameter is not a valid YYYY-MM-DD date.
403 tier_restricted Starter (Free) plan. Requires Basic or higher.
404 symbol_not_found No data for the symbol. Message notes when the market is closed (9:30 AM - 4:00 PM ET).

Screener

Cross-sectional screening over the live in-memory universe. POST /v1/screener runs a structured query - preset/custom universe, a recursive filter tree, computed-formula fields, multi-key sort, field projection, and pagination - and returns the matching rows. GET /v1/screener/fields lists the queryable field names and types. A deeper guide to the filter grammar and field taxonomy lives in screener.md.


POST /v1/screener

Query the live screener. The request body is a structured query object; the response is { meta, data } where data is the projected, sorted, paginated result rows.

Requires Growth plan or higher. Some fields/formulas are Alpha-gated; the response meta.tier reflects the tier the query ran under.

Request body

{
  "universe": { "type": "preset", "value": "sp500", "symbols": null },
  "filters": {
    "op": "and",
    "conditions": [
      { "field": "atm_iv", "operator": "gte", "value": 30 },
      { "field": "pc_ratio_oi", "operator": "lt", "value": 0.8 }
    ]
  },
  "formulas": [
    { "alias": "vrp", "expression": "atm_iv - rv_20d" }
  ],
  "sort": [
    { "field": "atm_iv", "direction": "desc" }
  ],
  "select": ["symbol", "price", "atm_iv", "pc_ratio_oi"],
  "limit": 50,
  "offset": 0
}
Field Type Required Default Description
universe object no all loaded symbols { type, value, symbols }. type = preset (with value, e.g. an index name) or an explicit list via symbols (array of tickers).
filters object no none Recursive FilterNode tree. A branch node has op (and/or) + conditions[]; a leaf has field or formula, plus operator and value. Numeric operators: eq, neq, gt, gte, lt, lte, between, in, is_null, is_not_null. Symbols such as >= are rejected.
sort array no none Ordered list of { field or formula, direction }; direction = asc/desc (default desc).
select array no all fields Field names to project into each row. Omit for the full flat row.
formulas array no none Computed columns: { alias, expression }. Expression operates over field names; the alias becomes a usable field for select/sort/filters.
limit integer no 50 Page size.
offset integer no 0 Page offset.

Example

curl -X POST "https://lab.flashalpha.com/v1/screener" \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
        "filters": { "op": "and", "conditions": [
          { "field": "atm_iv", "operator": "gte", "value": 30 }
        ]},
        "sort": [{ "field": "atm_iv", "direction": "desc" }],
        "select": ["symbol", "price", "atm_iv"],
        "limit": 25
      }'

Response 200

{
  "meta": {
    "total_count": 142,
    "returned_count": 25,
    "universe_size": 503,
    "offset": 0,
    "limit": 25,
    "tier": "growth",
    "as_of": "2026-06-05T15:42:10Z"
  },
  "data": [
    { "symbol": "TSLA", "price": 212.34, "atm_iv": 58.2 },
    { "symbol": "NVDA", "price": 118.91, "atm_iv": 51.5 }
  ]
}
Field Description
meta.total_count Rows matching the filter before pagination.
meta.returned_count Rows in this page (data.length).
meta.universe_size Total symbols in the live store.
meta.tier Tier the query executed under (growth / alpha).
data[] Projected rows. Shape depends on select (or the full flat row) plus any formulas aliases.

Errors

Status error When
400 validation_error The query failed validation (bad field, operator, or structure).
400 formula_error A formulas[].expression failed to parse.
403 tier_restricted Caller is below the Growth plan.

GET /v1/screener/fields

Lists every field that can be referenced in a screener query's filters, sort, select, or formula expressions, with its value type.

Requires an API key on the Growth plan or higher (lower plans get 403 tier_restricted).

Example

curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/screener/fields"

Response 200

{
  "fields": [
    { "name": "atm_iv", "type": "number" },
    { "name": "pc_ratio_oi", "type": "number" },
    { "name": "price", "type": "number" },
    { "name": "rv_20d", "type": "number" }
  ],
  "count": 4
}

Fields are returned sorted by name. type is the value type used by the filter/sort engine (e.g. number, string).


Errors

Error bodies come in four shapes. Check the status code before parsing JSON.

Shape When Content-Type
Empty (0 bytes) 401 (missing or invalid key, except the Risk Engine), 405, 404 for a route that does not exist none
RFC 7807 problem details A query value of the wrong type, a required value the framework checks (historical at, exposure/range from/to), a POST body that cannot be parsed, the 404 of an unknown Alerts id application/problem+json
{error, message} Most endpoint errors (400, 404, 422, 503) application/json
{status: "ERROR", ...} 403, 429, Risk Engine, Screener / Alerts validation_error application/json

On a few routes error is a sentence rather than a code, returned as is: No option quote found., Invalid expiry date format. Use yyyy-MM-dd., Invalid type. Use C, P, Call, or Put. (all on /optionquote) and Quota exceeded.

401 Unauthorized

Empty body: no JSON, no Content-Type, no WWW-Authenticate. The Risk Engine returns {"status":"ERROR","error":"missing_api_key",...} or invalid_api_key instead.

400 Bad Request

Business validation uses {error, message} with a code such as invalid_expiry. A value of the wrong type gets an RFC 7807 body:

{
  "type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "errors": { "spot": ["The value 'abc' is not valid."] },
  "traceId": "00-...",
  "endpoint_version": "2026.09.30"
}

403 Tier Restricted

{
  "status": "ERROR",
  "endpoint_version": "2026.09.30",
  "error": "tier_restricted",
  "message": "This endpoint requires the Growth plan or higher.",
  "current_plan": "Basic",
  "required_plan": "Growth"
}

On the Free plan, a stock outside the ~250-symbol tracked universe (GET /v1/universe) gets 403 with symbol_not_in_free_universe on /v1/exposure/gex, /v1/exposure/levels and /stockquote. Basic and higher can query any US-listed symbol. The request still counts toward the daily quota.

{
  "status": "ERROR",
  "endpoint_version": "2026.09.30",
  "error": "symbol_not_in_free_universe",
  "message": "The Free plan only covers ~250 tracked symbols. KSS isn't one of them. Upgrade to Basic or higher for on-demand access to any US-listed symbol.",
  "current_plan": "Free",
  "required_plan": "Basic"
}

404 Not Found

{
  "error": "symbol_not_found",
  "message": "No data for ZZZZQ.",
  "endpoint_version": "2026.09.30"
}

Most 404s use no_data or symbol_not_found. Route-specific codes: no_option_data, no_coverage, no_snapshot, index_not_found, insufficient_history, expiry_not_found, research_pack_not_available, research_artifact_not_found, and No option quote found. on /optionquote.

429 Too Many Requests

{
  "status": "ERROR",
  "endpoint_version": "2026.09.30",
  "error": "Quota exceeded",
  "message": "You have exceeded your daily API quota of 5 requests on the Free plan. Please upgrade to Basic for a higher limit. Your quota resets at 2026-10-31 00:00:00 UTC.",
  "current_plan": "Free",
  "limit": 5,
  "upgrade_to": "Basic",
  "reset_at": "2026-10-31T00:00:00.0000000Z"
}

Headers: Retry-After (seconds until the 00:00 UTC reset), X-RateLimit-Limit, X-RateLimit-Remaining: 0, X-RateLimit-Reset (Unix seconds). limit is the plan's daily quota. Requests rejected with 403 still count toward the quota.

Other status codes

Status Error When
405 - Method not allowed. Empty body
422 iv_not_found, unsupported_symbol Value cannot be computed, or the symbol is not supported by that operation
503 historical_source_unhealthy, ... Data still loading, a disabled source (body names a reason), or /health failing
504 - Gateway timeout. HTML body, Retry-After: 120; retry later

Conventions

  • All timestamps are UTC in ISO 8601 format
  • All exposure values are in USD notional
  • Dates use yyyy-MM-dd format
  • Days to expiry (days_to_expiry, dte) is whole calendar days from the US Eastern valuation date: today for live requests, or the requested date for historical requests. A same-day expiry has dte 0. Displayed days to expiry is separate from the fractional time used in pricing calculations.
  • GEX formula: gamma * OI * 100 * spot^2 * 0.01 (per 1% move, SpotGamma convention)
  • Greeks are calculated via Black-Scholes-Merton (not sourced from vendor)
  • Implied volatility is smoothed via SVI (Stochastic Volatility Inspired) parameterization
  • Dealer position is the opposite of net exposure (dealers are counterparty)
  • OI changes are day-over-day deltas (when prior-day snapshot is available)
  • Any US equity or ETF symbol is supported. Paid plans serve live data; access, fields and quotas remain tiered. The public surface, anonymous summaries and background data have separate policies.

Ready to build with FlashAlpha?

Get your free API key and start pulling live options data in 60 seconds.

No credit card required Setup in 60 seconds Secure & reliable