"Historical implied volatility data" is a request that means different things to different people. A discretionary trader wants the IV term structure across DTE buckets. A quant building a vol model wants the smoothed surface. An ML engineer wants per-contract IV they can join to OI and greeks. A vol researcher wants the calibrated SVI parameters so they can refit themselves. The right historical IV API ships all four shapes, time-aligned, in the same response shape as the live equivalents.
FlashAlpha's Historical API does this. Four endpoints cover the IV stack, every one of them takes the same at parameter, back to 2017-01-03. This guide walks through each of them with example calls and the typical use case for each.
The Four Endpoints
| Endpoint | Returns | Best for |
/v1/optionquote/{ticker} | Per-contract BSM IV (with greeks & OI) | Per-strike IV at a moment, building features per contract |
/v1/volatility/{symbol} | RV ladder, IV-RV spreads, skew profiles, term-structure slopes & state | Skew analysis, IV-RV spread, full vol dashboard at a moment |
/v1/surface/{symbol} | 50×50 IV grid over (tenor, log-moneyness) | Surface plots, surface-shape features for ML, smooth IV at any moneyness |
/v1/adv_volatility/{symbol} | SVI params, forwards, total variance surface, arbitrage flags, variance-swap fair values | Vol-model research, SVI refit, variance-swap pricing |
Same at parameter. Same response shape as live. Pick the one that matches your job.
1. Per-Contract IV - /v1/optionquote
For per-strike IV at a moment, the contract endpoint is the one to use. Returns BSM-derived implied_vol for every contract, computed from the minute-level mid-quote and spot.
import httpx, pandas as pd
API_KEY = "..."
chain = httpx.get(
"https://historical.flashalpha.com/v1/optionquote/SPY",
params={"at": "2024-08-05T10:00:00", "expiry": "2024-08-09"},
headers={"X-Api-Key": API_KEY},
).json()
df = pd.DataFrame(chain)
df = df[(df["type"] == "C") & (df["mid"] > 0)]
df.set_index("strike")["implied_vol"].plot(
title="SPY 2024-08-09 calls - IV smile at 10:00 ET on the carry-trade unwind day"
)
One call, one expiry, full smile. implied_vol is BSM-derived from the mid - the market's own IV; for the SVI-smoothed view, the calibrated parameters ship on /v1/adv_volatility for the same date.
2. Vol Dashboard - /v1/volatility
For the full IV picture at a moment - ATM IV, realized vol ladder, IV-RV spread, skew profiles per expiry, term-structure slopes and regime, IV dispersion - the volatility dashboard returns it all in one response. Same shape as the live /v1/volatility endpoint. Its term_structure is a summary object (near_slope_pct, far_slope_pct, state); the per-tenor IV array lives on /v1/stock/{symbol}/summary, used in the term-structure section below.
vol = httpx.get(
"https://historical.flashalpha.com/v1/volatility/SPY",
params={"at": "2024-08-05T10:00:00"},
headers={"X-Api-Key": API_KEY},
).json()
# ATM IV, IV-RV spread, and term-structure regime in one response
print("ATM IV: ", vol["atm_iv"])
print("VRP (20d): ", vol["iv_rv_spreads"]["vrp_20d"])
ts = vol["term_structure"] # {near_slope_pct, far_slope_pct, state}
print(f"Term structure: {ts['state']} "
f"(near {ts['near_slope_pct']:+.1f}%, far {ts['far_slope_pct']:+.1f}%)")
This is the call to make when you want the full vol dashboard at a single moment. Realized vols are computed from the QuestDB stocks table via SAMPLE BY 1d last-tick - so HV20, HV60, etc. are real, computed against actual historical closes available at at.
3. Smooth Surface - /v1/surface
For ML features, surface plots, or interpolated IV at any (tenor, moneyness), the surface endpoint returns a 50×50 grid:
surf = httpx.get(
"https://historical.flashalpha.com/v1/surface/SPY",
params={"at": "2024-08-05T10:00:00"},
headers={"X-Api-Key": API_KEY},
).json()
import numpy as np
iv_grid = np.array(surf["iv"]) # 50 x 50 grid
tenors = np.array(surf["tenors"]) # years to expiry
mny = np.array(surf["moneyness"]) # log-moneyness, -0.25 to +0.25
print(f"Grid shape: {iv_grid.shape}, slices used: {surf['slices_used']}")
The grid is built by the same calculator as live - bilinear interpolation from OTM contract IVs across tenor and log-moneyness. For sparse-history dates with too few liquid OTM contracts to fill the grid, the endpoint returns 404 insufficient_data rather than serving a degenerate surface.
This is your "ML feature store" call - flatten the grid into 2,500 features per timestamp and you have a dense surface representation the model can learn against. Combined with the dealer-positioning features from /v1/exposure/summary, that's enough surface area to train serious volatility models. Backtesting article shows the dealer-positioning side →
4. SVI Parameters - /v1/adv_volatility
For research that needs the calibrated parameters themselves - re-fitting your own model, computing variance swaps, generating validated vol surfaces - the advanced volatility endpoint returns the SVI fit per expiry, plus forward prices, total-variance surface, arbitrage flags, and greek surfaces (vanna, charm, volga, speed):
adv = httpx.get(
"https://historical.flashalpha.com/v1/adv_volatility/SPY",
params={"at": "2024-08-05"}, # SVI is daily, defaults to 16:00 ET
headers={"X-Api-Key": API_KEY},
).json()
svi = pd.DataFrame(adv["svi_parameters"])
print(svi[["expiry", "a", "b", "rho", "m", "sigma", "atm_iv"]])
SVI parameters are end-of-day - one fit per trading day, applied across the day. For intraday vol changes use the surface or per-contract endpoints; for calibrated parameters to re-fit or price from, use this one.
The arbitrage_flags array tells you, per expiry, whether the fit had any butterfly or calendar arbitrage flagged during calibration. For research that depends on surfaces with no flagged arbitrage, filter on this. SVI methodology background →
Building an IV Term-Structure History (4 Years)
The most common "historical IV" research question: how did IV term structure evolve over time? Pull the stock summary daily across your window and extract its volatility.iv_term_structure array (ATM IV per expiry).
from tqdm import tqdm
dates = pd.bdate_range("2022-01-03", "2025-12-31")
rows = []
with httpx.Client(headers={"X-Api-Key": API_KEY}, timeout=30) as c:
for d in tqdm(dates):
r = c.get(
"https://historical.flashalpha.com/v1/stock/SPY/summary",
params={"at": d.strftime("%Y-%m-%d")},
)
if r.status_code != 200: continue
ts = r.json().get("volatility", {}).get("iv_term_structure", [])
# Each entry has days_to_expiry + iv (ATM IV at that tenor)
for slot in ts:
rows.append({"date": d,
"dte": slot["days_to_expiry"],
"iv": slot["iv"]})
iv = pd.DataFrame(rows)
# Bucket DTEs to standard tenors so plots align day to day
iv["bucket"] = pd.cut(iv["dte"], bins=[0, 14, 45, 120, 365],
labels=["~7d", "~30d", "~90d", "long"])
pivot = iv.groupby(["date", "bucket"])["iv"].mean().unstack("bucket")
pivot[["~7d", "~30d", "~90d"]].plot(
title="SPY ATM IV by tenor bucket - 2022 to 2025", figsize=(12, 5))
About 1,000 trading days, one call per day. The resulting DataFrame is the canonical "IV term structure over time" dataset - feed it into vol-targeting strategies, regime classifiers, or just use it to plot when contango broke into backwardation across the test window.
Coverage and Access
- Granularity: minute-level for per-contract IV, the vol dashboard and the surface grid.
- Coverage: 200+ symbols in the archive, 54 with 8+ years of minute-level history, 14 from 2017-01-03 to present.
- Tier: Alpha ($1,199/mo billed annually, $1,499 monthly). One API key works against both the live and historical hosts.
What This Replaces
If you've been doing any of these for historical IV research, the four endpoints replace them:
- Pulling raw historical chains and inverting BSM yourself for IV →
/v1/optionquote ships implied_vol attached.
- Computing your own term-structure aggregations from EOD chains →
/v1/stock/{symbol}/summary ships the per-tenor IV term-structure array pre-computed (and /v1/volatility ships the term-structure slopes and regime).
- Fitting your own SVI parameters daily →
/v1/adv_volatility ships them, with arbitrage flags.
- Storing your own surface grids per minute →
/v1/surface ships a fresh 50×50 per call.
Related Articles
Historical API · Alpha tier · from $1,199/mo billed annually
Replay 52 analytics routes at any minute since 2017
Same response shape as live, leak-free percentiles, 80+ billion minute-level option rows and a 4.6-billion-print trade tape, back to 2017.
View pricing →
Upgrade to Alpha
API Spec
Historical implied volatility data ships in four shapes - per-contract BSM IV, vol dashboard with term structure, 50×50 surface grid, and SVI parameters with arbitrage flags. All four take the same at parameter and all four match the live response shape. Pick the endpoint that matches your job: per-contract for features, dashboard for term-structure history, surface for ML, SVI for vol-model research. Coverage SPY 2017 → today, Alpha tier.