0DTE Options Scanner API — Screen Same-Day Contracts by Delta, OI, and Gamma Regime | FlashAlpha

0DTE Options Scanner API — Screen Same-Day Contracts by Delta, OI, and Gamma Regime

How to build a 0DTE options scanner using the FlashAlpha Live Screener API. Cascading filters isolate same-day expiry contracts by delta, OI, type, and gamma regime in one POST request. Includes Python examples for 0DTE call selling, put scanning, pin-risk detection, intraday options scanning, and 0DTE premium selling setups across 20-248 symbols.

T
Tomasz Dobrowolski Quant Engineer
Apr 6, 2026
14 min read
0DTE OptionsScanner OptionsScreener GammaExposure API FlashAlpha DealerPositioning IntradayTrading PremiumSelling IronCondor

If you’re already familiar with the Live Screener, skip to the recipes. If not, the short version: POST /v1/screener accepts a recursive filter tree, applies it across 20–248 symbols (depending on your plan), and returns a ranked, trimmed result. Filters at the expiries.*, strikes.*, and contracts.* levels cascade inside an AND group, meaning non-matching expiries, strikes, and contracts are pruned from each symbol before the response ships. That’s what makes it a scanner, not just a filter: the response is already shaped like the answer.

Why a 0DTE scanner is different from a 0DTE endpoint

FlashAlpha has a dedicated 0DTE analytics endpoint (GET /v1/exposure/zero-dte/{symbol}) that returns pin risk, expected move, gamma regime, dealer hedging estimates, and theta decay for one symbol at a time. That’s the right tool when you already know which name you’re trading.

A scanner answers a different question: which symbols have 0DTE setups worth looking at right now? Instead of calling the 0DTE endpoint for each of 20 symbols and merging client-side, one screener POST returns only the symbols (and only the contracts within those symbols) that match your criteria.

QuestionBest tool
“What’s the pin risk and expected move for SPY today?”GET /v1/exposure/zero-dte/SPY
“Which symbols have high-delta 0DTE calls with OI above 1,000?”POST /v1/screener
“Show me every 0DTE put with delta between -0.3 and -0.5 in positive-gamma names.”POST /v1/screener
“Build a chart of SPY’s 0DTE GEX profile by strike.”GET /v1/exposure/gex/SPY

How cascading filters work for 0DTE

The key concept is cascading AND. When you put stock-level, expiry-level, and contract-level conditions inside a single AND group, the server:

  1. Passes or fails each symbol on the stock-level conditions
  2. For surviving symbols, removes expiries that don’t match the expiry-level conditions
  3. For surviving expiries, removes contracts that don’t match the contract-level conditions
  4. Drops any symbol that has zero surviving contracts

This is exactly what you want for 0DTE scanning. You set expiries.days_to_expiry = 0 to isolate same-day, then layer contract-level conditions on top.

{
  "filters": {
    "op": "and",
    "conditions": [
      { "field": "expiries.days_to_expiry", "operator": "eq",  "value": 0 },
      { "field": "contracts.type",          "operator": "eq",  "value": "C" },
      { "field": "contracts.delta",         "operator": "gte", "value": 0.3 },
      { "field": "contracts.oi",            "operator": "gte", "value": 1000 }
    ]
  },
  "select": ["*"]
}

That returns, for every symbol in your universe: only the 0DTE expiry, only the call contracts, only the ones with delta ≥ 0.3 and OI ≥ 1,000. No other expiries, no puts, no low-delta or low-OI noise.

Five 0DTE scanner recipes

1. 0DTE call seller (high-delta, high-OI calls)

The classic: find 0DTE calls worth selling. Cascading trims to just the tradable contracts.

{
  "filters": {
    "op": "and",
    "conditions": [
      { "field": "regime",                   "operator": "eq",  "value": "positive_gamma" },
      { "field": "expiries.days_to_expiry",  "operator": "eq",  "value": 0 },
      { "field": "contracts.type",           "operator": "eq",  "value": "C" },
      { "field": "contracts.delta",          "operator": "gte", "value": 0.3 },
      { "field": "contracts.oi",             "operator": "gte", "value": 1000 }
    ]
  },
  "select": ["*"]
}

The regime = positive_gamma condition means you’re only selling calls where dealers are dampening moves, not amplifying them. This is the core 0DTE premium selling setup: rich theta, friendly regime, liquid contracts.

What comes back:

{
  "meta": {
    "total_count": 4,
    "returned_count": 4,
    "universe_size": 248,
    "tier": "alpha",
    "as_of": "2026-04-06T15:22:10Z"
  },
  "data": [
    {
      "symbol": "SPY",
      "price": 563.42,
      "regime": "positive_gamma",
      "zero_dte_net_gex": 1284000000,
      "contracts": [
        { "strike": 565, "type": "C", "delta": 0.42, "iv": 0.18, "bid": 1.85, "ask": 1.88, "oi": 14200, "volume": 8340 },
        { "strike": 570, "type": "C", "delta": 0.31, "iv": 0.19, "bid": 0.72, "ask": 0.75, "oi": 9800, "volume": 5120 }
      ]
    },
    {
      "symbol": "QQQ",
      "price": 478.15,
      "regime": "positive_gamma",
      "contracts": [
        { "strike": 480, "type": "C", "delta": 0.38, "iv": 0.21, "bid": 2.10, "ask": 2.14, "oi": 6300, "volume": 3200 }
      ]
    }
  ]
}

Notice: only 0DTE expiry survives, only calls, only the ones clearing the delta and OI floor. Every other expiry, put, and low-OI contract was pruned server-side. That’s the cascading at work.

Plan note: cascading 0DTE filters work on both Growth (20 symbols) and Alpha (~248 symbols). Growth returns up to 10 rows; Alpha returns up to 50 with offset pagination.

2. 0DTE put scanner (protective or speculative)

{
  "filters": {
    "op": "and",
    "conditions": [
      { "field": "expiries.days_to_expiry",  "operator": "eq",      "value": 0 },
      { "field": "contracts.type",           "operator": "eq",      "value": "P" },
      { "field": "contracts.delta",          "operator": "between", "value": [-0.5, -0.2] },
      { "field": "contracts.volume",         "operator": "gte",     "value": 500 }
    ]
  },
  "select": ["*"]
}

Useful for finding 0DTE puts that are actually trading (volume ≥ 500) in the 20–50 delta range. Use this to scan for protective put opportunities when you’re long stock, or for speculative directional bets on names showing weakness intraday. The volume floor filters out illiquid strikes where you’d get a bad fill.

3. Negative-gamma 0DTE scanner (amplified move risk)

{
  "filters": {
    "op": "and",
    "conditions": [
      { "field": "regime",                   "operator": "eq", "value": "negative_gamma" },
      { "field": "expiries.days_to_expiry",  "operator": "eq", "value": 0 }
    ]
  },
  "sort":   [{ "field": "net_gex", "direction": "asc" }],
  "select": ["symbol", "price", "regime", "net_gex", "gamma_flip", "zero_dte_net_gex", "zero_dte_pct_of_total"]
}

This surfaces the names where 0DTE dealer gamma is negative and sorted by most-negative GEX. These are the names where intraday moves will be amplified by dealer hedging today. What to do with it: avoid selling premium on these names (dealers will run against you), or use them as breakout candidates where you buy directional 0DTE options and ride the gamma amplification. Widen stops on any existing positions in these symbols.

Running these on live data? All five recipes work on Growth ($299/mo, 20 symbols) and Alpha ($1,499/mo, ~248 symbols + formulas). Get an API key | Read the full screener docs

4. ATM 0DTE straddle scanner

{
  "filters": {
    "op": "and",
    "conditions": [
      { "field": "expiries.days_to_expiry",  "operator": "eq",      "value": 0 },
      { "field": "contracts.delta",          "operator": "between", "value": [-0.55, 0.55] },
      { "field": "contracts.oi",             "operator": "gte",     "value": 2000 },
      { "field": "atm_spread_pct",           "operator": "lte",     "value": 0.03 }
    ]
  },
  "select": ["*"],
  "limit": 10
}

Finds symbols with liquid, tight-spread 0DTE ATM contracts. The atm_spread_pct ≤ 0.03 stock-level filter prunes names with bad microstructure before the contract-level cascade runs. This is your 0DTE iron condor scanner: once you have the ATM contracts, you know which names have the liquidity and spread width for a same-day iron condor or straddle.

5. 0DTE + high short-term GEX concentration

{
  "filters": {
    "op": "and",
    "conditions": [
      { "field": "zero_dte_pct_of_total", "operator": "gte", "value": 0.3 },
      { "field": "expiries.days_to_expiry", "operator": "eq",  "value": 0 },
      { "field": "contracts.oi",            "operator": "gte", "value": 500 }
    ]
  },
  "sort":   [{ "field": "zero_dte_pct_of_total", "direction": "desc" }],
  "select": ["symbol", "price", "zero_dte_pct_of_total", "zero_dte_net_gex", "regime"]
}

Surfaces names where 0DTE options account for 30%+ of total gamma. These are the most “0DTE-driven” symbols today, where short-dated flow dominates the exposure profile. High concentration means the name will behave differently from its usual regime: pin risk is elevated, gamma acceleration is concentrated, and theta decay is extreme. Use this as an intraday options scanner for the most mechanically-interesting names of the session.

Full Python workflow: scan then drill

The pattern that works best for 0DTE trading: use the screener to find candidates, then hit the per-symbol 0DTE endpoint for the ones you want to trade.

from flashalpha import FlashAlpha

fa = FlashAlpha("YOUR_API_KEY")

# Step 1: scan for 0DTE call-selling candidates
scan = fa.screener(
    filters={
        "op": "and",
        "conditions": [
            {"field": "regime",                  "operator": "eq",  "value": "positive_gamma"},
            {"field": "expiries.days_to_expiry",  "operator": "eq",  "value": 0},
            {"field": "contracts.type",           "operator": "eq",  "value": "C"},
            {"field": "contracts.delta",          "operator": "gte", "value": 0.3},
            {"field": "contracts.oi",             "operator": "gte", "value": 1000},
        ],
    },
    select=["symbol", "price", "regime", "zero_dte_net_gex"],
    limit=5,
)

# Step 2: drill into top candidates
for row in scan["data"]:
    sym = row["symbol"]
    dte = fa.zero_dte(sym)
    print(f"\n{sym}:")
    print(f"  Pin risk:      {dte['pin_risk']['score']}/100")
    print(f"  Expected move: {dte['expected_move']['range_1_sigma']}")
    print(f"  Regime:        {dte['gamma_regime']['regime']}")

Two requests instead of N. The screener finds the 5 best candidates; the 0DTE endpoint gives you pin risk, expected move, and dealer hedging detail for each one. This is also how you’d build a 0DTE options filter in production: scan on a timer, drill into the survivors, and alert when conditions align.

Plan note: fa.screener() requires Growth+. fa.zero_dte() also requires Growth+. Both draw from the same daily quota (2,500/day on Growth, unlimited on Alpha).

SDK support

The screener method is available in all five FlashAlpha SDKs (v0.3.0+):

  • Python: fa.screener(filters=..., select=...)
  • JavaScript: fa.screener({ filters, select })
  • .NET: client.ScreenerAsync(new ScreenerRequest { ... })
  • Java: client.screener(Map.of(...))
  • Go: client.Screener(ctx, flashalpha.ScreenerRequest{...})

No need to build the HTTP POST yourself. Install the SDK, call screener(), and the filter tree goes over the wire as typed JSON.

Further reading

Scan 0DTE setups today

The Live Screener is part of the FlashAlpha Real-Time Options Analytics API. Growth gives you 20 symbols with cascading 0DTE filters. Alpha unlocks ~248 symbols, formulas, and strategy scores.

Get an API key → Compare plans →

Live Market Pulse

Get tick-by-tick visibility into market shifts with full-chain analytics streaming in real time.

Intelligent Screening

Screen millions of option pairs per second using your custom EV rules, filters, and setups.

Execution-Ready

Instantly send structured orders to Interactive Brokers right from your scan results.

Join the Community

Discord

Engage in real time conversations with us!

Twitter / X

Follow us for real-time updates and insights!

GitHub

Explore our open-source SDK, examples, and analytics resources!