Historical GEX Data API
Query gamma exposure by strike at any minute since April 2018. Same response shape as the live /v1/exposure/gex endpoint — just add an at= timestamp to replay GEX, gamma flip, call/put walls, and dealer hedging from any historical moment.
Endpoint
historical.flashalpha.com
Auth: X-Api-Key
Tier: Alpha
Parameters
| Name | In | Required | Default | Description |
|---|---|---|---|---|
symbol | path | yes | — | Underlying symbol (e.g. SPY) |
at | query | yes | — | As-of timestamp. yyyy-MM-ddTHH:mm:ss for minute resolution, or yyyy-MM-dd (defaults to 16:00 ET close). Treated as ET wall-clock. |
expiration | query | no | all | Filter to a single expiry (yyyy-MM-dd) |
min_oi | query | no | 0 | Minimum open interest threshold |
The at Parameter
Every historical endpoint hangs off at — the as-of timestamp. Greeks and spot are stored at 1-minute resolution (9:30→16:00 ET) and resolved with a LATEST ON per-contract query, so any at within the trading day returns the contract state as of that minute. Open interest is EOD and applies to all intraday timestamps for that day.
curl -H "X-Api-Key: YOUR_API_KEY" \
"https://historical.flashalpha.com/v1/exposure/gex/SPY?at=2020-03-16T15:30:00"
import httpx, pandas as pd
from tqdm import tqdm
# Replay net GEX at 15:30 ET for every trading day in 2024
dates = pd.bdate_range("2024-01-02", "2024-12-31")
rows = []
for d in tqdm(dates):
at = d.strftime("%Y-%m-%dT15:30:00")
r = httpx.get(
f"https://historical.flashalpha.com/v1/exposure/gex/SPY?at={at}",
headers={"X-Api-Key": "YOUR_API_KEY"}, timeout=10
)
if r.status_code == 200:
j = r.json()
rows.append({"date": d, "net_gex": j["net_gex"], "flip": j["gamma_flip"]})
df = pd.DataFrame(rows)
const at = "2020-03-16T15:30:00"; // COVID crash, 15:30 ET
const r = await fetch(
`https://historical.flashalpha.com/v1/exposure/gex/SPY?at=${at}`,
{ headers: { "X-Api-Key": "YOUR_API_KEY" } }
);
const j = await r.json();
console.log(`Net GEX: $${j.net_gex.toLocaleString()}`);
console.log(`Regime: ${j.net_gex_label}`);
console.log(`Flip: ${j.gamma_flip}`);
Response
Identical shape to the live /v1/exposure/gex endpoint, with two extra fields (as_of, as_of_requested) that echo the resolved timestamp.
{
"symbol": "SPY",
"underlying_price": 246.01,
"as_of": "2020-03-16T15:30:00",
"as_of_requested": "2020-03-16T15:30:00",
"gamma_flip": 270.92,
"net_gex": -2633970601,
"net_gex_label": "negative",
"strikes": [
{
"strike": 240,
"call_gex": 145210000,
"put_gex": 88420000,
"net_gex": 233630000,
"call_oi": 14211,
"put_oi": 38820,
"call_volume": 0,
"put_volume": 0,
"call_oi_change": null,
"put_oi_change": null
}
]
}
Field gaps vs live: call_volume / put_volume are always 0 (minute-resolution table doesn't carry sizes); call_oi_change / put_oi_change are null (no prior-day OI join yet).
What This Unlocks
- Backtest GEX-regime strategies — pull regime labels at 15:30 ET for every day in your test window, join forward returns, measure edge. Walkthrough →
- Replay specific events — what did dealer positioning look like at 15:30 ET on the COVID crash (2020-03-16, -12%)? GameStop squeeze (2021-01-27)? SVB collapse (2023-03-13)?
- Train ML models on minute-level GEX features without lookahead bias — every
atresolves with point-in-time semantics. - Validate GEX research from blogs / papers against the actual historical sequence.
- Build dashboards that overlay historical gamma flip vs price for any day or window.
Coverage — Supported Tickers
Live coverage pulled from GET /v1/tickers. Additional symbols backfill on demand for Alpha customers (typically <48h).
| Symbol | First date | Last date | Healthy days | Total days |
|---|---|---|---|---|
| SPY | 2018-04-16 |
2026-04-02 |
1,972 | 2,909 |
(Coverage table fell back to last-known snapshot — live /v1/tickers was unreachable at render time.)
Errors
| Status | Code | When |
|---|---|---|
400 | invalid_at | at missing or wrong format |
400 | invalid_expiration | expiration not yyyy-MM-dd |
401 | — | Missing or invalid X-Api-Key |
403 | tier_restricted | Plan below Alpha |
404 | no_data | (symbol, at) outside coverage window or in a gap |
429 | rate_limited | Daily quota exhausted (shared with live API) |
Why Historical GEX Is Hard to Find
Most GEX dashboards show live exposure — useful for today, useless for backtests. The few vendors who store history typically keep EOD aggregates, which collapse the intraday flip and ruin any signal that depends on knowing where dealers stood at 14:00 ET vs 15:30 ET. FlashAlpha's historical service stores minute-level greeks per contract, so the GEX you query at 14:00 on 2020-03-16 is computed from the actual greeks and spot at that minute — not interpolated, not stamped with the close. More on why this is rare →
Related Historical Endpoints
- Historical DEX — delta exposure by strike
- Historical VEX — vanna exposure by strike
- Historical CHEX — charm exposure by strike
- Historical Exposure Summary — composite GEX/DEX/VEX/CHEX + regime + hedging estimates
- Historical Gamma Levels — gamma flip, call wall, put wall, max pain, magnet
- Historical Narrative — verbal regime + day-over-day GEX change
- Historical 0DTE — pin risk, expected move, decay for the front-day expiry
- Historical VRP — leak-free volatility risk premium percentiles
- Historical API overview →
Ready to build?
Get your free API key and start pulling live options data in 30 seconds.