0DTE went from a curiosity to the dominant SPY flow in about three years. Half of SPY options volume now expires the same day. The intraday behavior of every other expiry is shaped by what's happening at 0DTE - gamma walls, pin levels, hedging acceleration into the close. Anyone trading SPY intraday needs the 0DTE picture, and anyone researching SPY intraday flow needs the historical 0DTE picture, which until now barely existed as a buyable dataset.
FlashAlpha's Historical API ships the live 0DTE endpoint at every minute since 2018-04-16. The same response shape, the same regime labels, the same pin-risk score. This article walks through the endpoint, the analytics block, and the research workflows that become possible when you can replay any 0DTE session at minute resolution.
The Endpoint
GET https://historical.flashalpha.com/v1/exposure/zero-dte/{symbol}?at=&strike_range=
Same shape as live /v1/exposure/zero-dte. The at parameter is the as-of timestamp; strike_range optionally narrows the strikes block. Default strike range covers a wide window around spot.
| Param | Description |
symbol | Underlying (SPY today) |
at | yyyy-MM-ddTHH:mm:ss ET wall-clock - the as-of |
strike_range | Optional strike window around spot |
What the Response Contains
Nine analytics blocks, all aligned to the same at:
regime - gamma flip, spot vs flip, regime label (positive/negative/unknown)
exposures - net GEX/DEX/VEX/CHEX at the 0DTE expiry, plus % of total chain GEX
expected_move - straddle price, implied 1σ move, remaining 1σ from at to close, upper/lower bounds, ATM IV
pin_risk - magnet strike, max pain, pin score (0-100), distance-to-magnet, OI concentration
decay - theta per remaining hour, gamma acceleration, charm regime label
flow - call OI, put OI, P/C ratios, total OI
hedging - dealer-shares-to-trade at ±0.5% and ±1% spot moves
levels - highest-OI strike, call wall, put wall, max positive/negative gamma strikes
strikes - per-strike chain (OI, GEX, DEX, IV) inside strike_range
And time_to_close_hours, computed from at against 16:00 ET on the same day - so theta and gamma-acceleration numbers are accurate to the minute.
Example: 0DTE at 9:45 ET on the August 5, 2024 Carry-Trade Unwind
curl -H "X-Api-Key: YOUR_API_KEY" \
"https://historical.flashalpha.com/v1/exposure/zero-dte/SPY?at=2024-08-05T09:45:00"
This pulls the 0DTE picture 15 minutes after the open on the day SPY gapped down hard on the Japan-carry unwind. Read the response top to bottom: what was the regime? Where was the magnet strike? What was the implied 1σ remaining move at 9:45? What was net 0DTE GEX as a fraction of total chain GEX? The data describes a known-extreme session at the resolution traders actually decided.
For comparison, run the same call against a quiet day:
curl -H "X-Api-Key: YOUR_API_KEY" \
"https://historical.flashalpha.com/v1/exposure/zero-dte/SPY?at=2025-09-23T09:45:00"
Diff the responses. The structural difference between a panic open and a quiet open is exactly what regime classifiers and ML models need labelled examples of.
Pattern: Pin-Rate Accuracy Across N Expiries
One of the most-asked questions about 0DTE: does the magnet strike actually predict the close? Easy to test now.
import httpx, pandas as pd
from tqdm import tqdm
API_KEY = "..."
BASE = "https://historical.flashalpha.com"
dates = pd.bdate_range("2024-01-02", "2025-12-31") # daily-expiry era
rows = []
with httpx.Client(headers={"X-Api-Key": API_KEY}, timeout=30) as c:
for d in tqdm(dates):
# 0DTE picture at 15:30, when pin risk is most reliable
r = c.get(f"{BASE}/v1/exposure/zero-dte/SPY",
params={"at": d.strftime("%Y-%m-%dT15:30:00")})
if r.status_code != 200: continue
z = r.json()
if z.get("no_zero_dte"): continue # no daily expiry that day
# Close spot
c2 = c.get(f"{BASE}/v1/stockquote/SPY",
params={"at": d.strftime("%Y-%m-%dT16:00:00")})
if c2.status_code != 200: continue
rows.append({
"date": d,
"spot_1530": z["underlying_price"],
"magnet": z["pin_risk"]["magnet_strike"],
"max_pain": z["pin_risk"]["max_pain"],
"pin_score": z["pin_risk"]["pin_score"],
"spot_close": c2.json()["mid"],
})
df = pd.DataFrame(rows)
df["close_dist_to_magnet"] = (df["spot_close"] - df["magnet"]).abs()
df["close_dist_to_random"] = (df["spot_close"] - df["spot_1530"]).abs()
print(df[["close_dist_to_magnet", "close_dist_to_random"]].describe())
Compare median close_dist_to_magnet against close_dist_to_random (treating 15:30 spot as a no-information baseline). If the magnet hypothesis is real, magnet-distance should be smaller. Stratify by pin_score and you'll see the relationship is monotone - high pin scores predict tighter closes.
This study is one for-loop. Without the historical 0DTE endpoint, it's a quarter of pipeline work.
Pattern: Intraday Decay Dynamics
Theta and gamma acceleration on 0DTE are non-linear in time-to-close. The decay block exposes this directly:
from datetime import datetime, timedelta
t = datetime(2025, 6, 13, 9, 30)
end = datetime(2025, 6, 13, 16, 0)
rows = []
with httpx.Client(headers={"X-Api-Key": API_KEY}, timeout=30) as c:
while t <= end:
r = c.get(f"{BASE}/v1/exposure/zero-dte/SPY",
params={"at": t.strftime("%Y-%m-%dT%H:%M:%S")})
if r.status_code == 200:
z = r.json()
if z.get("decay"):
rows.append({
"t": t,
"theta_per_hour": z["decay"].get("theta_per_hour_remaining", 0),
"gamma_accel": z["decay"].get("gamma_acceleration", 0),
"charm_regime": z["decay"].get("charm_regime"),
})
t += timedelta(minutes=15)
decay = pd.DataFrame(rows).set_index("t")
decay[["theta_per_hour", "gamma_accel"]].plot(secondary_y="gamma_accel",
title="0DTE decay dynamics - Jun 13, 2025")
The shape of the gamma-acceleration curve is what pinning behavior looks like in raw signal - flat from open through midday, ramping after 14:00 ET, peaking just before close. Replay this across a quarter of trading days and you have a clean empirical curve for any pinning model.
Pattern: Hedging Cliff at the Close
The hedging block returns dealer-shares-to-trade at ±0.5% and ±1% spot moves. As 0DTE approaches expiry, these numbers explode - small spot moves trigger huge dealer rebalancing because the gamma is concentrated in a vanishing time slice. Plotting hedging.spot_up_half_pct.dealer_shares_to_trade across an intraday session is a visual of the mechanical pressure that builds into the close.
Combined with the flow block (call/put OI by strike), you have everything you need to simulate the close-period hedging sweep in research code - without ever pulling raw chains or running your own gamma aggregation.
Honest Gap: 0DTE Greeks at Very-Near Expiry
One known limitation: at very-near expiry - typically the last few hours of the session - per-contract greeks like delta, gamma, theta, and IV often arrive as 0 or null because the historical pipeline hasn't yet computed BSM for those minute-resolution contracts. The chain itself is still listed for OI and strike-distribution analysis, and the aggregated regime/pin-risk/expected-move blocks are populated normally - but the per-strike greek values can collapse to zero in those late windows.
This is queued as part of the 0DTE-greeks backfill. For research that depends on per-contract late-session greeks, sample at 14:00 ET or earlier; the higher-level 0DTE analytics block (regime, pin score, hedging estimates) is reliable across the full session.
What If There's No 0DTE Expiry?
If a date genuinely has no same-day-expiring options (early SPY history, pre-2022 daily-expiry rollout, or other underlyings on a Tuesday/Thursday cycle), the response collapses to a small flag set:
{
"symbol": "SPY",
"as_of": "2019-03-12T15:30:00",
"expiration": null,
"no_zero_dte": true,
"next_zero_dte_expiry": "2019-03-15",
"message": "No 0DTE expiry on this date"
}
SPY post-2022 lists Mon-Fri expiries so this branch is rare; pre-2022 history hits it more often. Always check no_zero_dte before reading the analytics blocks.
Coverage
| Symbols | SPY (more on demand) |
| Dates | 2018-04-16 → 2026-04-02; daily-expiry coverage from SPY rollout (2022) |
| Resolution | Per-minute regime/pin-risk/decay/hedging; per-strike chain |
| Tier | Alpha ($1,499/mo) |
| Endpoint | /v1/exposure/zero-dte/{symbol}?at=&strike_range= |
What This Replaces
- Reconstructing 0DTE GEX from raw chains and EOD OI → use the endpoint; aggregation is done.
- Computing pin-risk scores or expected-move bounds yourself → both ship pre-computed in the response.
- Storing your own minute-level 0DTE history → the API is faster than your DB for ad-hoc research.
Where you'd still build it yourself: bespoke pin-risk models that disagree with the FlashAlpha methodology, or research that requires per-contract late-session greeks (queued for backfill).
Related Articles
Historical API · Alpha tier · from $1,199/mo
Replay any analytics endpoint at any minute since 2018
Same response shape as live, leak-free percentiles, 6.7B option rows for SPY, more symbols on demand.
View pricing →
Data freshness: intraday data through the previous trading day's close, refreshed by the daily pipeline run. Live coverage status at
/v1/tickers.
Upgrade to Alpha
API Spec