- SDKs & MCP
- Specifications & Samples
- 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
GET /stockquote/{ticker}- Live stock quoteFree+GET /optionquote/{ticker}- Option quotes with greeksGrowth+GET /v1/surface/{symbol}- Vol surface gridPublicGET /v1/surface/svi/{symbol}- Live SVI parameters per expiryAlpha+GET /v1/stock/{symbol}/summary- Comprehensive stock summaryPublic(delayed) /Free+(live; futures delayed below Growth)
Exposure Analytics
GET /v1/exposure/gex/{symbol}- Gamma exposure by strikeFree+(single expiry) /Growth+(full chain)GET /v1/exposure/dex/{symbol}- Delta exposure by strikeBasic+GET /v1/exposure/vex/{symbol}- Vanna exposure by strikeBasic+GET /v1/exposure/chex/{symbol}- Charm exposure by strikeBasic+GET /v1/exposure/summary/{symbol}- Full exposure summaryGrowth+GET /v1/exposure/levels/{symbol}- Key support/resistance levelsFree+GET /v1/exposure/narrative/{symbol}- Verbal narrative analysisGrowth+GET /v1/exposure/zero-dte/{symbol}- Real-time 0DTE analyticsGrowth+GET /v1/exposure/sheet/{symbol}- Unified per-strike sheet (GEX/DEX/VEX/CHEX/DAG + LIS + peaks)Growth+GET /v1/exposure/term-structure/{symbol}- Per-greek term structure (DTE buckets + per-expiry), plusgamma_concentrationfor SPY/QQQGrowth+GET /v1/exposure/expiry-impact/{symbol}- Gamma concentration with one expiry excluded (SPY/QQQ)Growth+GET /v1/exposure/strike-dependency/{symbol}- One exact strike's gamma attributed to its expiries (SPY/QQQ)Growth+GET /v1/exposure/basket- Cross-symbol basket aggregateGrowth+GET /v1/exposure/oi-diff/{symbol}- Day-over-day OI deltas (top changes)Growth+GET /v1/maxpain/{symbol}- Max pain analysis with dealer alignmentBasic+
Volatility (additional)
GET /v1/liquidity/{symbol}- Per-expiry + chain liquidity scoresGrowth+GET /v1/volatility/skew-term/{symbol}- Skew term structure with RR / butterfly conventionsGrowth+GET /v1/volatility/skew-term/{symbol}/history- Daily constant-tenor 25-delta skew history (SPY/QQQ)Alpha+- release pendingGET /v1/volatility/term-structure/{symbol}/history- Daily 30/60/90-day volatility curve (SPY/QQQ)Alpha+- release pendingGET /v1/volatility/spot-vol-correlation/{symbol}- 20d / 60d spot-vol correlationGrowth+GET /v1/volatility/realized/{symbol}- Range-based realized estimators (Parkinson, Garman-Klass, Rogers-Satchell, Yang-Zhang)Alpha+GET /v1/volatility/forecast/{symbol}- Conditional vol forecasts (EWMA, HAR-RV, GARCH, HAR-IV, HARQ, Realized GARCH, HEAVY)Alpha+GET /v1/dispersion- Implied + realized correlation across an index basketAlpha+
Macro
GET /v1/macro/vix-state- VIX vs SPX-realized regime classifierGrowth+
Universe
GET /v1/universe- Curated tier-1 / tier-2 symbol directoryPublic
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.
GET /v1/flow/levels/{symbol}- Live gamma flip + call/put walls + max painGrowth+GET /v1/flow/pin-risk/{symbol}- Live pin-risk score with full breakdownGrowth+GET /v1/flow/summary/{symbol}- At-a-glance flow direction + headline live GEXGrowth+GET /v1/flow/gex/{symbol}- Live GEX with per-strike profile (simulation-aware)Growth+GET /v1/flow/dex/{symbol}- Live DEX with per-strike profile (simulation-aware)Growth+GET /v1/flow/dealer-risk/{symbol}- Settled vs live shift with direction classifierGrowth+GET /v1/flow/oi/{symbol}- Raw OI simulator state (model input)Alpha+GET /v1/flow/live/{symbol}- Headline flow bundle in one callAlpha+GET /v1/flow/signals/{symbol}- Scored, classified unusual-flow feed (sweep/block, opening bias, intent)Alpha+GET /v1/flow/signals/{symbol}/summary- Net bullish/bearish + opening/closing premium roll-upAlpha+GET /v1/flow/options/{symbol}/option-minute-history- Per-contract (strike+right+expiry) minute buy/sell/mid classified volume; aggregateΣ(buys − sells)per strike for net customer flow. The inferred dealer inventory change is its negation,sells − buys, accumulating from zero at the 09:30 ET open - a change over the session, never an absolute dealer positionAlpha+
Trade Flow Data
Alpha-only trade-flow proxy endpoints. These return pass-through JSON from the ingest service and use camelCase field names.
GET /v1/flow/options/{symbol}/recent- Recent option trades by underlyingAlpha+GET /v1/flow/options/{symbol}/summary- Option trade-flow totals by underlyingAlpha+GET /v1/flow/options/{symbol}/blocks- Large option trades by underlyingAlpha+GET /v1/flow/options/{symbol}/history- Minute option-flow buckets by underlyingAlpha+GET /v1/flow/options/{symbol}/cumulative- Cumulative option net-flow pointsAlpha+GET /v1/flow/stocks/{symbol}/recent- Recent stock tradesAlpha+GET /v1/flow/stocks/{symbol}/summary- Stock trade-flow totalsAlpha+GET /v1/flow/stocks/{symbol}/blocks- Large stock tradesAlpha+GET /v1/flow/stocks/{symbol}/history- Minute stock-flow bucketsAlpha+GET /v1/flow/stocks/{symbol}/cumulative- Cumulative stock net-flow pointsAlpha+GET /v1/flow/stocks/{symbol}/bars- Multi-resolution OHLCV+flow barsAlpha+GET /v1/flow/options/leaderboard- Option-flow buyer/seller leaderboardAlpha+GET /v1/flow/options/outliers- Option-flow outlier scanAlpha+GET /v1/flow/stocks/leaderboard- Stock-flow buyer/seller leaderboardAlpha+GET /v1/flow/stocks/outliers- Stock-flow outlier scanAlpha+
Pricing & Sizing
GET /v1/pricing/greeks- Full BSM greeks (first, second, third order)Free+GET /v1/pricing/iv- Implied volatility from market priceFree+GET /v1/pricing/kelly- Kelly criterion sizing for optionsGrowth+
Volatility Analytics
GET /v1/volatility/{symbol}- Comprehensive volatility analysisGrowth+GET /v1/adv_volatility/{symbol}- Advanced volatility analytics (SVI, variance surface, arb detection, greeks surfaces, var swap)Alpha+GET /v1/expected-move/{symbol}- ATM-IV-derived expected move per expiryBasic+GET /v1/expected-move/{symbol}/history- Reconstructed sigma bands vs matured price outcomes (SPY/QQQ)Alpha+- release pending
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 backtestingAlpha+(series=normalizedis release pending)
Strategy Signals
Decision-support endpoints that score one trading idea and return a unified strategy decision envelope.
GET /v1/strategies/flow-anomaly/{symbol}- Directional options-flow imbalanceGrowth+GET /v1/strategies/expiry-positioning/{symbol}- OPEX pin-risk / iron-fly setupBasic+GET /v1/strategies/zero-dte/{symbol}- Same-day range-compression readGrowth+(+0DTE)GET /v1/strategies/dealer-regime/{symbol}- Dealer gamma regime classifierGrowth+GET /v1/strategies/vol-carry/{symbol}- VRP carry credit-spread selectionAlpha+GET /v1/strategies/yield-enhancement/{symbol}- Covered-call / cash-secured-put income overlayGrowth+GET /v1/strategies/surface-anomaly/{symbol}- SVI residual rich/cheap wing detectionAlpha+GET /v1/strategies/skew/{symbol}- 25-delta skew / risk-reversal signalGrowth+GET /v1/strategies/term-structure/{symbol}- ATM-IV term-structure signalGrowth+GET /v1/strategies/tail-pricing/{symbol}- Downside-tail richness signalGrowth+GET /v1/strategies/sell-window/{symbol}- Daily sell or skip per expiry, plus suggested positions (SPY, QQQ, IWM)Basic+ through November 2026, then Growth+
Earnings
GET /v1/earnings/calendar- Upcoming earnings calendarGrowth+GET /v1/earnings/expected-move/{symbol}- Earnings-implied move decompositionGrowth+GET /v1/earnings/history/{symbol}- Past events: surprises, moves, IV crushGrowth+GET /v1/earnings/iv-crush/{symbol}- Expected + historical IV-crush distributionGrowth+GET /v1/earnings/vrp/{symbol}- Earnings vol-risk-premium (implied vs realized)Alpha+GET /v1/earnings/dealer-positioning/{symbol}- Event-scoped dealer exposureAlpha+GET /v1/earnings/strategies/{symbol}- Earnings strategy-suitability scoresAlpha+GET /v1/earnings/screener- Cross-sectional earnings screenerAlpha+
Structures
Pure-math multi-leg utilities - no market lookup.
POST /v1/structures/pnl- At-expiry P&L curve + breakevensBasic+POST /v1/structures/greeks- Aggregate position greeksBasic+
Screener
POST /v1/screener- Cross-sectional structured screen over the live universeGrowth+GET /v1/screener/fields- List queryable screener fields + typesGrowth+
Reference Data
GET /v1/tickers- All available stock tickersFree+GET /v1/options/{ticker}- Option chain metadata (expirations + strikes)Free+GET /v1/symbols- Currently queried symbols with live dataFree+
Account & System
GET /v1/account- Account info and quotaFree+GET /health- Health checkPublic
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_feedandoi_feed.futures_feedbeingnullin that same response says nothing about the answer. - Compare against the cadence, not against the clock.
oi_feedat yesterday's close is correct - open interest is settled data.equity_options_feedan hour behind during market hours is not. nullmeans 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_ofis notas_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_ofdescribes 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 carriesdata_as_ofin 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
midis computed as(bid + ask) / 2on the equity/ETF quote path.lastPriceis 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.
lastUpdateis 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
200response 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_restrictedbelow 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 benullif the external data source is unavailable exposureisnullif 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_25duses SVI-parameterized vol surface; k_25d ≈ ±σ√t × 0.675 (inverse normal of 0.25)vix_futures.basisis approximated from VIX3M vs VIX spot (not actual futures prices)top_strikesreturns up to 5 strikes sorted by absolute net GEXmax_painis 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_dexdwarfingnet_gexin the same response is expected. A chain withnet_gexof -3.6e8 can legitimately carrynet_dexof +2.0e12.net_vexis per 1.00 vol, not per vol point. This differs fromvega, which the greeks endpoints report per 1 vol point. The two conventions are inconsistent with each other;net_vexis 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_remainingisnullwhentime_to_close_hoursis 0 (market closed)- Near close (<5 minutes), a
warningsarray is included noting potential greek instability strike_rangeonly filters thestrikesarray - 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_bucketrather than emitted with zeros. pct_of_chain_gexis0for every row when the chain's total|net_gex|is zero (safe-divide guard).- For QQQ,
gamma_concentrationuses the raw-quote common-anchor repricer that QQQ GEX uses. When no captured Greeks are available the legacyby_dte_bucket/by_expiryarrays can be empty whilegamma_concentrationis 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.comwithat=(Alpha+). Date-onlyatuses 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 inmissing_symbolswhile 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_expiryis 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. Readchain_execution_scorealongside 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 therisk_reversal_25d/butterfly_25daliases. - 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) arenull(e.g. 12 bars ⇒rv10populated,rv20/rv30null).
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_rvuses squared daily returns as the realized-variance proxy;har_rv_5muses 5-minute data.realized_modelsis 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
200withconverged: falseand anullforecast. An unavailable archive symbol returns404, not500.
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(notNaN) when the underlying math is undefined. Treatnullas "not computable from this basket / window." - Index IV comes from the same
StockSummaryBuilder.ComputeAtmIvFromSnapshotspath 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
ExposureStrikeSnapshotswriter pipeline ships, the flag flips totrueand deltas populate without any client change. - For point-in-time historical OI diffs, use
historical.flashalpha.comwith 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
AtmIvand a positiveUnderlyingPriceon 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}/historyforSPX.
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/universeonce and filterhas_0dte == trueto get the full daily-expiry basket up front, instead of probing/v1/exposure/zero-dte/{symbol}per symbol and inferring exclusion from theno_zero_dteflag. 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_strikeis 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
expiryis 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_shiftisnullwhen settled GEX is zero and live GEX is non-zero (mathematically undefined - regime created from no baseline). When both are zero, the value is0flow_directionenum:no_flow/neutral/amplifying/dampening/regime_flipexpiryechoes the filter param back (ornullwhen the request omitted it);contracts_totalreflects 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_shiftandflow_dex_pct_shiftcan benullwhen 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 is0flow_directionenum:no_flow/neutral/amplifying/dampening/regime_fliptotal_abs_delta_contractssums|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_oiis the morning-broadcast OPRA settled value - won't change until next morning's broadcastsimulated_oiis 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 visibilityeffective_oiclamps each contract's value to≥ 0before summing - this is what/v1/flow/gex/,/dex/, etc. consumeoi_delta_confidenceis the per-trade weight applied to side-classified volume (currently0.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_deltais the DEX value (delta exposure), named for consistency with thelive_gexprefixflow_adjusted_dealer_risk.flow_directionenum: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-riskwhen 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_breakdowncomponents sum toscore; weights are server-tunable, so absolute contributions may shift over time while ordering stays stableopen_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_deltais that raw signed estimate;open_close_confidenceis the simulator's confidence weightintentisneutralwheneveropen_close_biasisclosing_bias(direction can't be attributed on unwinds) or the tradesideismid(note:side- the upstream buy/sell/mid aggressor classification - is distinct from the NBBOaggressorlabel)structureis alwaysblockorsweep(a lone block-sized print is ablock);singleis reserved and not currently emitted, so filteringstructure=singlereturns nothingtagsmay includesweep,block,opening,closing,0dte,whale(premium ≥ $1M), andgolden(score in the top decile within this response set and ≥ 70 absolute - so a weak set may yield nogoldenat all)enrichment.*fields arenullandmoneynessis"unknown"when the signal's contract isn't in the settled chain snapshot (illiquid / just-listed)hypothetical_gex_impact_if_openingis 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/gexchainlevels 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_priceis0, allchainlevels arenull, and everyenrichmentblock 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 classifiedintent/open_close_biassignal_countis the full count in the window;top_signalsis 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-barwas not one of30s/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 includesstatus,error,message,current_plan, andrequired_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-sidewas notall/calls/puts. Body:{ "error": "invalid_side", "message": "side must be all|calls|puts" }. - 400
invalid_bar-barwas not one of30s/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 includesstatus,error,message,current_plan, andrequired_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-baris anything other than1m. - 400
invalid_metric-metricnot ingex|dex|vex|chex|oi|signed_flow. - 400
invalid_mode-modeis notraw/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-baris anything other than1m. - 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).
dataStartUtcsurfaces 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;/barsfor 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 includesmax_pain_by_expirationfor all available expiries - With
?expiration=, the response is scoped to that single expiry andmax_pain_by_expirationisnull - 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_probabilityis 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 edgemu < r→ you're more bearish → puts have positive edgemu = 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 (withr). 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
- 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.
- 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.
- Tail convexity measures the second difference of the skew. Positive values indicate steep left-wing curvature (crash protection being bid).
- GEX by DTE uses the SpotGamma convention: gamma × OI × 100 × spot² × 0.01. Calls positive, puts negated.
- Herfindahl index ranges from 1/N (perfectly even) to 1.0 (all OI at one strike). Values above 0.10 suggest high concentration.
- 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=normalizedis release pending: it returns503 historical_source_unhealthy(reason: daily_series_disabled) until the daily-series release is enabled. It has a different response shape; see daily history series. Mixingdayswithnormalized, orstart/end/tenor_dayswithlegacy, returns400 invalid_history_request; any otherseriesvalue returns400 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-ddformat - 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 hasdte0. 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.
Unlock DEX, VEX and CHEX with Basic
Everything in Free, plus:
- DEX, VEX and CHEX exposure
- Full max pain
- ETF & index symbols SPY, QQQ, SPX, VIX
Go full-chain with Growth
Everything in Basic, plus:
- Full-chain GEX Every expiration
- 0DTE analytics
- Exposure summary & AI narrative
- CME futures & live screener
Add history and vol surfaces with Alpha
Everything in Growth, plus:
- Historical API replay Since 2017
- SVI vol surfaces
- VRP analytics
- Greeks surfaces Vanna, charm, volga and speed grids