The live options chain is solved. Polygon, Tradier, ThetaData, and a dozen others ship raw option quotes for "now." The historical chain at minute resolution - every strike, every expiry, greeks attached - is much harder to find. You can patch it together from EOD snapshots if you don't mind losing the intraday signal, or you can pull raw tick data and run your own BSM pipeline if you have a quarter to spare.
The other path: one HTTP call against FlashAlpha's Historical API. This article walks through the chain endpoint specifically, with the patterns you'll actually use.
The Endpoint in One Line
GET https://historical.flashalpha.com/v1/optionquote/{ticker}?at=&expiry=&strike=&type=
Same shape as live lab.flashalpha.com/v1/optionquote. Same fields. The only difference is the required at parameter - the as-of timestamp.
| Param | In | Required | Description |
ticker | path | yes | Underlying symbol - 200+ in the archive across index, ETF and single-name options |
at | query | yes | yyyy-MM-ddTHH:mm:ss ET wall-clock, or yyyy-MM-dd (defaults to 16:00 ET) |
expiry | query | no | yyyy-MM-dd filter |
strike | query | no | Numeric strike filter |
type | query | no | C / Call / P / Put |
With all three of expiry, strike, type → returns a single object. Otherwise → array.
What Comes Back
{
"type": "C",
"expiry": "2026-03-06",
"strike": 680,
"bid": 3.42,
"ask": 3.44,
"mid": 3.43,
"lastUpdate": "2026-03-05T15:30:00",
"implied_vol": 0.2579,
"delta": 0.4824,
"gamma": 0.0435,
"theta": -1.8617,
"vega": 0.1417,
"rho": 0.0089,
"vanna": 0.0891,
"charm": -0.0147,
"open_interest": 6284
}
Every field that matters for analytics: prices, sizes, full BSM greeks (including vanna and charm), open interest. The greeks are recomputed from minute-level spot and IV - they're real intraday values, not EOD snapshots smeared across the day. Volume is on the tape route /v1/flow/options/{symbol}/history and SVI parameters on /v1/adv_volatility; implied_vol on each contract is the BSM solve at that minute.
Pattern 1: Single Contract at a Moment
Pass all three filters - get one row.
import httpx
API_KEY = "..."
r = httpx.get(
"https://historical.flashalpha.com/v1/optionquote/SPY",
params={
"at": "2024-08-05T10:00:00",
"expiry": "2024-08-05",
"strike": 530,
"type": "P",
},
headers={"X-Api-Key": API_KEY},
).json()
print(f"SPY 530P 0DTE at 10:00 on Aug 5: mid={r['mid']:.2f}, IV={r['implied_vol']:.2%}")
Use this when you have a specific contract you want to track - backtesting a known straddle, replaying a single trade, or sanity-checking that a historical price is reasonable.
Pattern 2: Full Expiry at a Moment
Drop strike and type - get every contract for one expiry.
chain = httpx.get(
"https://historical.flashalpha.com/v1/optionquote/SPY",
params={"at": "2024-08-05T10:00:00", "expiry": "2024-08-05"},
headers={"X-Api-Key": API_KEY},
).json()
import pandas as pd
df = pd.DataFrame(chain)
print(df[["type", "strike", "mid", "implied_vol", "delta", "open_interest"]].head())
This is your standard "give me the chain" call. About 200-400 contracts per SPY expiry depending on tail extension. Useful for any single-expiry analysis: skew profile at a moment, OI distribution, dealer-positioning per strike.
Pattern 3: Full Chain at a Moment (Every Expiry)
Drop all filters - get every contract on every expiry.
full_chain = httpx.get(
"https://historical.flashalpha.com/v1/optionquote/SPY",
params={"at": "2024-08-05T10:00:00"},
headers={"X-Api-Key": API_KEY},
timeout=60,
).json()
print(f"{len(full_chain)} contracts in SPY chain at 10:00 on Aug 5, 2024")
Typically 6,000-10,000 contracts for SPY. This is the "rebuild the universe" call. Use it for full-surface fitting, total-OI calculations, or anything that needs the entire chain in one snapshot. One call, one timestamp, the whole chain.
Pattern 4: Time Series of a Single Contract
Loop over at values to build the price/greek history of one contract:
from datetime import datetime, timedelta
start = datetime(2024, 8, 5, 9, 30)
end = datetime(2024, 8, 5, 16, 0)
rows = []
t = start
with httpx.Client(headers={"X-Api-Key": API_KEY}, timeout=30) as c:
while t <= end:
r = c.get(
"https://historical.flashalpha.com/v1/optionquote/SPY",
params={
"at": t.strftime("%Y-%m-%dT%H:%M:%S"),
"expiry": "2024-08-05",
"strike": 530,
"type": "P",
},
)
if r.status_code == 200:
j = r.json()
rows.append({"t": t, "mid": j["mid"], "iv": j["implied_vol"], "delta": j["delta"]})
t += timedelta(minutes=15)
ts = pd.DataFrame(rows).set_index("t")
ts["mid"].plot(title="SPY 530P 0DTE - intraday on Aug 5, 2024 (carry-trade unwind)")
15-minute resolution gives you 27 samples across a session - usually enough for charts and intraday analysis. Drop to 1-minute for trade-level reconstructions.
Pattern 5: Multi-Expiry Sampling at One Moment
For surface-shape analysis or term-structure plots, fetch each expiry separately rather than the full chain - usually faster and easier to align:
expiries = ["2024-08-05", "2024-08-09", "2024-08-16",
"2024-08-30", "2024-09-20", "2024-12-20"]
slices = {}
with httpx.Client(headers={"X-Api-Key": API_KEY}, timeout=30) as c:
for exp in expiries:
slices[exp] = c.get(
"https://historical.flashalpha.com/v1/optionquote/SPY",
params={"at": "2024-08-05T10:00:00", "expiry": exp},
).json()
Six calls, six DataFrames, ready for term-structure plotting. For higher-level surface work, the dedicated IV-surface endpoint ships a 50×50 grid in one call - use it when you don't need contract-level detail.
Coverage
200+ symbols in the archive, 54 with 8+ years of minute-level history and 14 running from 2017-01-03 to the present. /v1/tickers returns the loaded symbols and date ranges if you're scripting against a range:
curl -H "X-Api-Key: YOUR_API_KEY" \
"https://historical.flashalpha.com/v1/tickers?symbol=SPY"
Tier: Alpha ($1,199/mo billed annually, $1,499/mo monthly).
What This Replaces
If you've been doing any of these, the chain endpoint replaces them:
- Pulling raw historical options parquets from ThetaData and running your own BSM hydration → use
/v1/optionquote; greeks are already attached.
- Reconstructing chains from EOD snapshots and interpolating intraday → no longer necessary; minute resolution is real.
- Storing your own historical chain in a custom database → you can, but the API is faster than your DB for most ad-hoc research.
For signed volume, block detection and print-level work, the raw options tape replays at /v1/flow/options/{symbol}/history and /recent off 4.66 billion real prints since 2017. For everything else - analytics, research, ML features, dashboards - the chain endpoint collapses the pipeline.
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
The historical options chain endpoint replaces the entire raw-data-plus-BSM-pipeline with one HTTP call. Five filter patterns cover almost every research workflow - single contract, full expiry, full chain, time series, multi-expiry slice. Greeks, OI, quotes and sizes ship attached at minute resolution; signed volume replays from the raw tape. Coverage runs across 200+ symbols, 14 of them from 2017-01-03 to today, on the Alpha tier.