Real-Time Options Screener & Scanner API — Filter IV, GEX, VRP, 0DTE | FlashAlpha
Lab API Live Screener
GROWTH+ ALPHA

Live Options Screener API

Real-time options screener and scanner for filtering and ranking across ~250 symbols by stock, expiry, strike, and contract-level metrics. Define custom fields with arithmetic formulas and filter on server-computed strategy scores (harvest_score, dealer_flow_risk, iron_condor_score, …). Part of the FlashAlpha Real-Time Options Analytics API, powered by an in-memory store that refreshes every 5-10 seconds — no database query runs at request time.

GROWTH ALPHA Growth gets 20 symbols · Alpha unlocks ~250 + formulas + strategy scores

Get an API key · Compare plans →

New to screening? Live Screener: Pillar Guide walks through the data model, filter tree, formulas, and cascading semantics. For recipes: Screener Cookbook.

Endpoint

POST /v1/screener
Auth required (X-Api-Key) Rate Limited: Yes Refresh: 5-10s

Request Body (JSON)

Field Type Required Description
filtersobjectnoFilter tree (recursive AND/OR groups with leaf conditions)
sortarraynoSort specs, applied in order (primary sort first)
selectarraynoField names to return, or ["*"] for full flat object
formulasarraynoComputed fields (Alpha only)
limitnumberno1-10 (Growth), 1-50 (Alpha). Default: 50
offsetnumbernoPagination offset (Alpha only). Default: 0
Tip: An empty body {} returns all symbols with default fields.

Quick Start

curl -X POST "https://lab.flashalpha.com/v1/screener" \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "filters": { "field": "regime", "operator": "eq", "value": "negative_gamma" },
    "sort": [{ "field": "atm_iv", "direction": "desc" }],
    "select": ["symbol", "price", "regime", "atm_iv", "net_gex"],
    "limit": 10
  }'
import requests

body = {
    "filters": {"field": "regime", "operator": "eq", "value": "negative_gamma"},
    "sort": [{"field": "atm_iv", "direction": "desc"}],
    "select": ["symbol", "price", "regime", "atm_iv", "net_gex"],
    "limit": 10
}

resp = requests.post(
    "https://lab.flashalpha.com/v1/screener",
    headers={"X-Api-Key": "YOUR_API_KEY"},
    json=body
)
data = resp.json()
for row in data["data"]:
    print(f"{row['symbol']:6} iv={row['atm_iv']:5.1f}  gex={row['net_gex']:,.0f}")
const body = {
  filters: { field: "regime", operator: "eq", value: "negative_gamma" },
  sort: [{ field: "atm_iv", direction: "desc" }],
  select: ["symbol", "price", "regime", "atm_iv", "net_gex"],
  limit: 10
};

const resp = await fetch("https://lab.flashalpha.com/v1/screener", {
  method: "POST",
  headers: {
    "X-Api-Key": "YOUR_API_KEY",
    "Content-Type": "application/json"
  },
  body: JSON.stringify(body)
});
const { data } = await resp.json();
data.forEach(r => console.log(`${r.symbol} iv=${r.atm_iv} gex=${r.net_gex}`));

Official SDKs (v0.3.0+)

Every FlashAlpha SDK ships with a typed screener() method — no need to hand-build the JSON body. Full SDK reference: github.com/FlashAlpha-lab.

pip install flashalpha npm i flashalpha dotnet add package FlashAlpha com.flashalpha:flashalpha:0.3.0 go get github.com/FlashAlpha-lab/flashalpha-go
from flashalpha import FlashAlpha
fa = FlashAlpha("YOUR_API_KEY")

result = fa.screener(
    filters={
        "op": "and",
        "conditions": [
            {"field": "regime",        "operator": "eq",  "value": "positive_gamma"},
            {"field": "harvest_score", "operator": "gte", "value": 65},
        ],
    },
    sort=[{"field": "harvest_score", "direction": "desc"}],
    select=["symbol", "price", "harvest_score", "dealer_flow_risk"],
)
for row in result["data"]:
    print(row["symbol"], row["harvest_score"])
import { FlashAlpha } from 'flashalpha';
const fa = new FlashAlpha('YOUR_API_KEY');

const { data } = await fa.screener({
  filters: {
    op: 'and',
    conditions: [
      { field: 'regime',        operator: 'eq',  value: 'positive_gamma' },
      { field: 'harvest_score', operator: 'gte', value: 65 },
    ],
  },
  sort:   [{ field: 'harvest_score', direction: 'desc' }],
  select: ['symbol', 'price', 'harvest_score', 'dealer_flow_risk'],
});
data.forEach(r => console.log(r.symbol, r.harvest_score));
using FlashAlpha;
using var client = new FlashAlphaClient("YOUR_API_KEY");

var result = await client.ScreenerAsync(new ScreenerRequest
{
    Filters = new ScreenerGroup
    {
        Op = "and",
        Conditions = new List<object>
        {
            new ScreenerLeaf { Field = "regime",        Operator = "eq",  Value = "positive_gamma" },
            new ScreenerLeaf { Field = "harvest_score", Operator = "gte", Value = 65 },
        },
    },
    Sort   = new List<ScreenerSort> { new() { Field = "harvest_score", Direction = "desc" } },
    Select = new List<string> { "symbol", "price", "harvest_score", "dealer_flow_risk" },
});
import com.flashalpha.FlashAlphaClient;
import java.util.*;

FlashAlphaClient client = new FlashAlphaClient("YOUR_API_KEY");

Map<String, Object> body = new LinkedHashMap<>();
body.put("filters", Map.of(
    "op", "and",
    "conditions", List.of(
        Map.of("field", "regime",        "operator", "eq",  "value", "positive_gamma"),
        Map.of("field", "harvest_score", "operator", "gte", "value", 65)
    )));
body.put("sort",   List.of(Map.of("field", "harvest_score", "direction", "desc")));
body.put("select", List.of("symbol", "price", "harvest_score", "dealer_flow_risk"));

var result = client.screener(body);
import (
    "context"
    flashalpha "github.com/FlashAlpha-lab/flashalpha-go"
)

client := flashalpha.NewClient("YOUR_API_KEY")
ctx := context.Background()

result, _ := client.Screener(ctx, flashalpha.ScreenerRequest{
    Filters: flashalpha.ScreenerGroup{
        Op: "and",
        Conditions: []interface{}{
            flashalpha.ScreenerLeaf{Field: "regime",        Operator: "eq",  Value: "positive_gamma"},
            flashalpha.ScreenerLeaf{Field: "harvest_score", Operator: "gte", Value: 65},
        },
    },
    Sort:   []flashalpha.ScreenerSort{{Field: "harvest_score", Direction: "desc"}},
    Select: []string{"symbol", "price", "harvest_score", "dealer_flow_risk"},
})
Load preset:
POST https://lab.flashalpha.com/v1/screener

Plans & Universe

Your plan determines the universe of symbols you see and which fields you can use. No symbol list is required in the request body — the server returns every symbol that matches your filters within your plan’s universe.

Growth $299 / mo
Universe: 20 symbols (Tier 1)
Rows per query: up to 10
Daily quota: 2,500 requests
Cascading stock / expiry / strike / contract filters
All Growth-tier fields (regime, GEX, IV, VRP, skew, term structure, flow)
No formulas, no strategy scores, no pagination offset
Alpha $1,499 / mo
Universe: ~250 symbols (Tier 1 + Tier 2)
Rows per query: up to 50 + offset pagination
Daily quota: Unlimited
Everything in Growth
Custom formulas (atm_iv / rv_20d, arithmetic on any numeric field)
Strategy scores: harvest_score, dealer_flow_risk, iron_condor_score, calendar_spread_score
Directional VRP: downside_vrp, upside_vrp, vrp_regime
Shared daily budget: the screener draws from the same daily quota as every other FlashAlpha endpoint (market data, MCP, VRP, etc.). Every authenticated request decrements one counter that resets at 00:00 UTC. Responses carry X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers. Exceeding the budget returns HTTP 429 with a JSON body indicating the reset time.
TIER 1 20 symbols · Growth + Alpha
SPY QQQ SPX IWM VIX AAPL TSLA NVDA AMZN META MSFT AMD GOOGL AVGO NFLX COIN PLTR MSTR JPM SMH
TIER 2 ~228 additional symbols · Alpha only
Indexes 3
VIX RUT DJX
Major ETFs 42
IWM DIA GLD TLT XLF XLE XLK XLV XLI XLP XLU XLB XLRE XLC XBI XOP XHB XME SMH ARKK ARKW ARKG EEM EFA HYG LQD TIP SLV USO UNG UVXY SQQQ TQQQ SPXU UPRO KWEB FXI EWZ VNQ JETS SOXX IGV
Tech 50
GOOGL GOOG AVGO ORCL CRM ADBE INTC QCOM TXN MU AMAT LRCX KLAC NFLX CSCO IBM NOW INTU SNOW PANW CRWD DDOG ZS NET SHOP COIN MSTR PLTR UBER ABNB DASH RBLX SNAP PINS TTD ROKU SPOT DKNG SE BABA JD PDD ARM SMCI DELL ANET MRVL ON NXPI MPWR
Finance 23
JPM BAC WFC GS MS C SCHW BLK AXP V MA PYPL COF USB PNC BK ICE CME SPGI ADP SOFI HOOD AFRM
Healthcare 23
UNH JNJ PFE MRK ABBV LLY TMO ABT DHR BMY AMGN GILD ISRG MDT SYK BSX ZTS VRTX REGN MRNA BIIB DXCM HIMS
Consumer 21
WMT COST HD LOW TGT TJX ROST DG NKE SBUX MCD YUM CMG LULU DECK PG KO PEP PM MO MNST
Industrial 16
BA CAT DE HON GE RTX LMT NOC GD UPS FDX CSX UNP DAL UAL AAL
Energy 15
XOM CVX COP SLB EOG MPC VLO PSX OXY DVN HAL FANG WMB KMI LNG
Auto & EV 8
F GM TM RIVN LCID NIO XPEV LI
Real Estate 8
AMT PLD CCI EQIX SPG O DLR PSA
Crypto-adjacent 4
MARA RIOT CLSK HUT
Telecom & Media 6
T VZ TMUS CMCSA DIS WBD
Popular / meme / growth 19
IONQ RGTI OKLO VST CEG FSLR ENPH GME AMC ZM DOCU HUBS TWLO OKTA MDB AI BNTX PATH BILL

Alpha-only fields are silently omitted from Growth responses, but throw validation_error if referenced in a filter or sort:

harvest_score, dealer_flow_risk, vrp_regime, variance_risk_premium, convexity_premium, vrp_z_score, vrp_percentile, put_wing_iv_25d, call_wing_iv_25d, downside_rv_20d, upside_rv_20d, downside_vrp, upside_vrp, short_put_spread_score, short_strangle_score, iron_condor_score, calendar_spread_score.


Filter Syntax

Filters are a recursive tree. Each node is either a group (and/or with nested conditions) or a leaf (field + operator + value).

Leaf Node

{ "field": "atm_iv", "operator": "gte", "value": 20 }

Group Node

{
  "op": "and",
  "conditions": [
    { "field": "regime", "operator": "eq", "value": "positive_gamma" },
    { "field": "atm_iv", "operator": "gte", "value": 15 }
  ]
}

Nesting

Groups can nest groups up to 3 levels deep. Maximum 20 leaf conditions per query.

{
  "op": "and",
  "conditions": [
    { "field": "price", "operator": "gt", "value": 200 },
    {
      "op": "or",
      "conditions": [
        { "field": "regime", "operator": "eq", "value": "positive_gamma" },
        { "field": "vix", "operator": "gte", "value": 20 }
      ]
    }
  ]
}

Reads as: price > 200 AND (regime = "positive_gamma" OR vix >= 20).

Dotted Field Prefixes

Filter at deeper levels using dotted prefixes:

  • expiries.X — filters expiry-level fields
  • strikes.X — filters strike-level fields within each expiry
  • contracts.X — filters contract-level fields (individual calls/puts)

Example: { "field": "expiries.days_to_expiry", "operator": "eq", "value": 7 }.


Operators

Numeric Operators

OperatorMeaningReads asValue typeExample
eqEqualsfield = valuenumber"value": 18.5
neqNot equalfield valuenumber"value": 0
gtGreater thanfield > valuenumber"value": 20
gteGreater than or equalfield valuenumber"value": 15
ltLess thanfield < valuenumber"value": 5
lteLess than or equalfield valuenumber"value": 100
betweenWithin range (inclusive)min field maxarray [min, max]"value": [15, 25]
inMatches any value in listfield [list]array of numbers"value": [1, 2, 3]

String Operators

OperatorMeaningValue typeExample
eqEquals (case-insensitive)string"value": "positive_gamma"
neqNot equalstring"value": "toxic_short_vol"
inMatches any string in listarray of strings"value": ["contango", "mixed"]

Null Operators (any type)

OperatorMeaningExample
is_nullField exists but has no value (missing data){ "field": "atm_iv", "operator": "is_null" }
is_not_nullField has a value (exclude missing data){ "field": "vrp_regime", "operator": "is_not_null" }

Field Reference

See Screener Field Taxonomy for the complete field glossary grouped by level and category. The tables below highlight the most common fields.

Stock Level (one per symbol)

CategoryFieldsTier
Identity & Pricesymbol, price, bid, ask, midGrowth
Exposureregime, net_gex, net_dex, net_vex, net_chexGrowth
Key Levelsgamma_flip, call_wall, put_wall, max_positive_gamma, max_negative_gamma, highest_oi_strike, max_pain, zero_dte_magnetGrowth
Zero-DTEzero_dte_net_gex, zero_dte_pct_of_totalGrowth
Volatilityatm_iv, fair_vol, rv_5d, rv_10d, rv_20d, rv_30d, rv_60dGrowth
VRP (basic)vrp_5d, vrp_10d, vrp_20d, vrp_30d, vrp_assessment (very_high_premium, healthy_premium, moderate_premium, thin_premium, negative_spread, danger_zone, unknown)Growth
VRP (extended)variance_risk_premium, convexity_premium, vrp_z_score, vrp_percentile, vrp_regime (harvestable, toxic_short_vol, cheap_convexity, event_only, surface_distorted)Alpha
Directional VRPput_wing_iv_25d, call_wing_iv_25d, downside_rv_20d, upside_rv_20d, downside_vrp, upside_vrpAlpha
Skewskew_25d, skew_25d_put, skew_25d_callGrowth
Term Structureterm_near_slope_pct, term_far_slope_pct, term_state (contango, backwardation, mixed, unknown)Growth
Liquidityatm_spread_pct, wing_spread_pct, atm_contracts, wing_contractsGrowth
OI Concentrationtop_3_pct, top_5_pct, herfindahlGrowth
Flowtotal_call_oi, total_put_oi, total_call_volume, total_put_volume, pc_ratio_oi, pc_ratio_volumeGrowth
IV Dispersioniv_dispersion_cross_expiry, iv_dispersion_cross_strikeGrowth
GEX by DTEgex_0to7_dte, gex_8to30_dte, gex_31to60_dte, gex_61plus_dteGrowth
Strategy Scoresharvest_score, net_harvest_score, dealer_flow_risk, short_put_spread_score, short_strangle_score, iron_condor_score, calendar_spread_score (0-100)Alpha

Macro Fields (global, same value for all symbols)

Useful as context filters: “show me stocks only when VIX > 20”.

vix, vvix, skew, spx, move, vix_3m, vix_term_slope, dgs10, fed_funds, hy_spread

Expiry / Strike / Contract Levels

Access nested data via dotted prefixes. See Field Taxonomy for the complete list.

  • Expiry: expiries.days_to_expiry, expiries.atm_iv, expiries.put_25d_iv, expiries.call_25d_iv, expiries.skew_25d, expiries.smile_ratio, expiries.forward, expiries.basis_pct, expiries.fair_vol, expiries.call_oi, expiries.put_oi, expiries.call_volume, expiries.put_volume, expiries.net_gex, expiries.net_dex, expiries.net_vex, expiries.net_chex, expiries.iv, expiries.rv, expiries.vrp
  • Strike: strikes.strike, strikes.call_gex, strikes.put_gex, strikes.net_gex, strikes.call_dex, strikes.put_dex, strikes.net_dex, strikes.call_vex, strikes.put_vex, strikes.net_vex, strikes.call_chex, strikes.put_chex, strikes.net_chex, strikes.call_oi, strikes.put_oi, strikes.call_volume, strikes.put_volume
  • Contract: contracts.type ("C" or "P"), contracts.expiry, contracts.strike, contracts.dte, contracts.bid, contracts.ask, contracts.mid, contracts.iv, contracts.delta, contracts.gamma, contracts.theta, contracts.vega, contracts.oi, contracts.volume

Formulas ALPHA

Define computed fields using arithmetic expressions over numeric fields. Reference them in filters, sort, and select.

Define formulas

{
  "formulas": [
    { "alias": "vrp_ratio", "expression": "atm_iv / rv_20d" },
    { "alias": "risk_adj",  "expression": "harvest_score / (dealer_flow_risk + 1)" }
  ]
}

Supported syntax

  • Operators: +, -, *, /, parentheses
  • Unary negation: -atm_iv
  • Numeric literals: 100, 0.5, -3.14
  • Field names: any numeric screener field (snake_case)
  • Max expression length: 200 characters
  • Division by zero returns null
  • Null fields propagate null through the expression

Use formulas in filter

By alias:

{ "formula": "vrp_ratio", "operator": "gte", "value": 1.2 }

Or inline (no formulas array needed):

{ "formula": "atm_iv / rv_20d", "operator": "gte", "value": 1.2 }

Use formulas in sort / select

"sort":   [{ "formula": "vrp_ratio", "direction": "desc" }]
"select": ["symbol", "price", "vrp_ratio", "risk_adj"]
// or: "select": ["*", "vrp_ratio"]

Alias rules

  • Non-empty, unique per request
  • Cannot conflict with built-in field names
  • Expression fields must be numeric screener fields

Cascading Filters

When you filter on nested fields (expiries.*, strikes.*, contracts.*) inside a top-level AND group, the filter cascades — trims non-matching children at each level and excludes symbols with zero matches.

Stock filter    → pass/fail entire symbol
Expiry filter   → symbol passes, non-matching expiries removed
Strike filter   → surviving expiries keep only matching strikes
Contract filter → matching strikes keep only matching call/put contracts
Empty at any level → symbol excluded

Example

{
  "filters": {
    "op": "and",
    "conditions": [
      { "field": "regime",                   "operator": "eq",  "value": "positive_gamma" },
      { "field": "expiries.days_to_expiry",  "operator": "lte", "value": 30 },
      { "field": "strikes.call_oi",          "operator": "gte", "value": 1000 },
      { "field": "contracts.type",           "operator": "eq",  "value": "C" }
    ]
  },
  "select": ["*"]
}

Returns only:

  • Symbols where regime = positive_gamma
  • Only their expiries where DTE ≤ 30
  • Only strikes within those expiries with call_oi ≥ 1000
  • Only call contracts (puts removed)

OR with nested fields

Inside OR groups, nested filters use Any() semantics:

  • expiries.X matches if any expiry matches
  • strikes.X matches if any strike in any expiry matches
  • contracts.X matches if any contract in any strike matches

OR does not cascade — the full data is returned for matching symbols.


Response Format

{
  "meta": {
    "total_count": 7,
    "returned_count": 7,
    "universe_size": 250,
    "offset": 0,
    "limit": 50,
    "tier": "alpha",
    "as_of": "2026-04-05T10:30:00Z"
  },
  "data": [
    {
      "symbol": "SPY",
      "price": 656.01,
      "regime": "positive_gamma",
      "atm_iv": 20.7,
      "vrp_20d": 1.72
    }
  ]
}

Meta fields

FieldDescription
total_countSymbols matching filters (before pagination)
returned_countRows in this page
universe_sizeTotal symbols in store
offset / limitEffective pagination (clamped to tier limits)
tier"growth" or "alpha"
as_ofServer UTC timestamp

Select behavior

  • "select": ["symbol", "price"] — flat row with only these fields
  • "select": ["*"] — full flat object including expiry_aggregates, contracts, macro, and formula values
  • Omitted → default fields per tier

Full flat structure (select: ["*"])

{
  "symbol": "SPY",
  "as_of": "...",
  "market_open": true,
  "price": 656.01, "bid": 655.61, "ask": 656.41,
  "atm_iv": 20.7, "rv_20d": 18.98, "vrp_20d": 1.72,
  "regime": "positive_gamma",
  "net_gex": -7542138590,
  "gamma_flip": 655.9, "call_wall": 660, "put_wall": 630,
  "total_call_oi": 5116823, "total_put_oi": 9789307, "pc_ratio_oi": 1.913,
  "skew_25d": 5.31, "vix": 23.87, "vvix": 115.33,
  "expiry_aggregates": [
    {
      "expiry": "2026-04-06", "dte": 2, "is_zero_dte": false,
      "atm_iv": 20, "call_oi": 31427, "put_oi": 19809,
      "strikes": [
        { "strike": 651, "call_oi": 2161, "put_oi": 1810, "net_gex": 0 }
      ]
    }
  ],
  "contracts": [
    {
      "expiry": "2026-04-06", "strike": 651, "type": "C",
      "bid": 8.5, "ask": 9.0, "iv": 0.214,
      "delta": 0.678, "gamma": 0.01, "oi": 2161, "volume": 8334
    }
  ]
}

Example Queries

17 worked examples covering every feature of the screener. See also the Screener Cookbook for realistic trader workflows.

1. Simple stock filter

{
  "filters": { "field": "regime", "operator": "eq", "value": "positive_gamma" },
  "sort":    [{ "field": "net_gex", "direction": "desc" }],
  "select":  ["symbol", "price", "regime", "net_gex"]
}

2. AND of multiple conditions

{
  "filters": {
    "op": "and",
    "conditions": [
      { "field": "regime",         "operator": "eq",  "value": "positive_gamma" },
      { "field": "vrp_20d",        "operator": "gte", "value": 2.5 },
      { "field": "harvest_score",  "operator": "gte", "value": 65 }
    ]
  },
  "sort":   [{ "field": "harvest_score", "direction": "desc" }],
  "select": ["symbol", "price", "vrp_20d", "harvest_score", "dealer_flow_risk"]
}

3. OR of multiple conditions

{
  "filters": {
    "op": "or",
    "conditions": [
      { "field": "vrp_regime", "operator": "eq", "value": "toxic_short_vol" },
      { "field": "vrp_regime", "operator": "eq", "value": "event_only" }
    ]
  },
  "select": ["symbol", "vrp_regime", "atm_iv", "dealer_flow_risk"]
}

4. Nested AND inside OR

{
  "filters": {
    "op": "or",
    "conditions": [
      { "op": "and", "conditions": [
        { "field": "regime",        "operator": "eq",  "value": "positive_gamma" },
        { "field": "harvest_score", "operator": "gte", "value": 70 }
      ]},
      { "op": "and", "conditions": [
        { "field": "regime", "operator": "eq",  "value": "negative_gamma" },
        { "field": "atm_iv", "operator": "gte", "value": 50 }
      ]}
    ]
  }
}

Reads: “(positive_gamma AND score≥70) OR (negative_gamma AND iv≥50)”.

5. Macro context filter

{
  "filters": {
    "op": "and",
    "conditions": [
      { "field": "regime", "operator": "eq",  "value": "negative_gamma" },
      { "field": "vix",    "operator": "gte", "value": 20 }
    ]
  },
  "select": ["symbol", "regime", "atm_iv", "vix"]
}

6. between operator

{
  "filters": { "field": "atm_iv", "operator": "between", "value": [15, 25] },
  "sort":    [{ "field": "atm_iv", "direction": "asc" }],
  "select":  ["symbol", "atm_iv", "rv_20d"]
}

7. in operator

{
  "filters": { "field": "term_state", "operator": "in", "value": ["contango", "mixed"] },
  "select":  ["symbol", "term_state", "atm_iv"]
}

8. Cascading filter (stock + expiry + strike + contract)

{
  "filters": {
    "op": "and",
    "conditions": [
      { "field": "regime",                   "operator": "eq",  "value": "positive_gamma" },
      { "field": "expiries.days_to_expiry",  "operator": "lte", "value": 14 },
      { "field": "strikes.call_oi",          "operator": "gte", "value": 2000 },
      { "field": "contracts.type",           "operator": "eq",  "value": "C" },
      { "field": "contracts.delta",          "operator": "gte", "value": 0.3 }
    ]
  },
  "select": ["*"],
  "limit": 20
}

Returns symbols with positive gamma, only expiries within 14 days, only high-OI strikes, and only high-delta calls.

9. Formula in select

{
  "formulas": [
    { "alias": "vrp_ratio", "expression": "atm_iv / rv_20d" }
  ],
  "filters": { "field": "regime", "operator": "eq", "value": "negative_gamma" },
  "sort":    [{ "formula": "vrp_ratio", "direction": "desc" }],
  "select":  ["symbol", "price", "atm_iv", "rv_20d", "vrp_ratio"]
}

10. Formula in filter (by alias)

{
  "formulas": [{ "alias": "iv_premium", "expression": "atm_iv - rv_20d" }],
  "filters": { "formula": "iv_premium", "operator": "gte", "value": 5 },
  "sort":    [{ "formula": "iv_premium", "direction": "desc" }],
  "select":  ["symbol", "atm_iv", "rv_20d", "iv_premium"]
}

11. Formula inline (no alias)

{
  "filters": {
    "op": "and",
    "conditions": [
      { "field": "regime", "operator": "eq", "value": "positive_gamma" },
      { "formula": "atm_iv - rv_20d", "operator": "gt", "value": 6 }
    ]
  }
}

12. Formula with select: ["*"]

{
  "formulas": [
    { "alias": "call_wall_put_wall_ratio", "expression": "call_wall / (put_wall + 30)" }
  ],
  "filters": {
    "op": "and",
    "conditions": [
      { "field": "contracts.bid",      "operator": "eq", "value": 14.5 },
      { "field": "expiries.call_oi",   "operator": "eq", "value": 18252 },
      { "formula": "call_wall_put_wall_ratio", "operator": "eq", "value": 1 }
    ]
  },
  "sort":   [{ "field": "price", "direction": "desc" }],
  "select": ["*"],
  "limit": 20
}

13. Root OR with nested AND

{
  "formulas": [
    { "alias": "call_wall_put_wall_ratio", "expression": "call_wall / (put_wall + 30)" }
  ],
  "filters": {
    "op": "or",
    "conditions": [
      { "op": "and", "conditions": [
        { "field": "contracts.bid",    "operator": "eq", "value": 14.5 },
        { "field": "expiries.call_oi", "operator": "eq", "value": 18252 },
        { "formula": "call_wall_put_wall_ratio", "operator": "eq", "value": 1 }
      ]},
      { "field": "regime", "operator": "eq", "value": "negative_gamma" }
    ]
  },
  "sort":   [{ "field": "price", "direction": "desc" }],
  "select": ["*", "call_wall_put_wall_ratio"]
}

14. Multi-sort

{
  "sort": [
    { "field": "dealer_flow_risk", "direction": "asc" },
    { "field": "harvest_score",    "direction": "desc" }
  ],
  "select": ["symbol", "dealer_flow_risk", "harvest_score"]
}

Primary: lowest risk first. Secondary (ties broken by): highest harvest score.

15. Negative numbers

{
  "filters": { "field": "net_gex", "operator": "lt", "value": -500000 },
  "sort":    [{ "field": "net_gex", "direction": "asc" }],
  "select":  ["symbol", "net_gex", "regime"]
}

16. Null handling

{
  "filters": { "field": "vrp_regime", "operator": "is_not_null" },
  "select":  ["symbol", "vrp_regime"]
}

17. Pagination

{
  "sort":   [{ "field": "price", "direction": "desc" }],
  "select": ["symbol", "price"],
  "limit":  10,
  "offset": 10
}

Alpha tier: page 2. Growth tier: offset is ignored; returns first 10.


Realistic Workflows

Harvestable VRP screen

{
  "filters": {
    "op": "and",
    "conditions": [
      { "field": "regime",            "operator": "eq",  "value": "positive_gamma" },
      { "field": "vrp_regime",        "operator": "eq",  "value": "harvestable" },
      { "field": "dealer_flow_risk",  "operator": "lte", "value": 40 },
      { "field": "harvest_score",     "operator": "gte", "value": 65 }
    ]
  },
  "sort":   [{ "field": "harvest_score", "direction": "desc" }],
  "select": ["symbol", "price", "harvest_score", "dealer_flow_risk", "vrp_regime"]
}

Negative-gamma alert

{
  "filters": {
    "op": "and",
    "conditions": [
      { "field": "regime",           "operator": "eq",  "value": "negative_gamma" },
      { "field": "dealer_flow_risk", "operator": "gte", "value": 50 }
    ]
  },
  "sort":   [{ "field": "dealer_flow_risk", "direction": "desc" }],
  "select": ["symbol", "regime", "dealer_flow_risk", "gamma_flip", "net_gex"]
}

Vol scanner (high IV vs RV)

{
  "filters": {
    "op": "and",
    "conditions": [
      { "field": "vrp_20d", "operator": "gte", "value": 5 },
      { "field": "atm_iv",  "operator": "gte", "value": 25 }
    ]
  },
  "sort":   [{ "field": "vrp_20d", "direction": "desc" }],
  "select": ["symbol", "atm_iv", "rv_20d", "vrp_20d", "skew_25d"]
}

0DTE call seller

{
  "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": ["*"]
}

Risk-adjusted score (formula)

{
  "formulas": [
    { "alias": "risk_adj", "expression": "harvest_score / (dealer_flow_risk + 1)" }
  ],
  "filters": { "field": "harvest_score", "operator": "gte", "value": 50 },
  "sort":    [{ "formula": "risk_adj", "direction": "desc" }],
  "select":  ["symbol", "price", "harvest_score", "dealer_flow_risk", "risk_adj"],
  "limit":   20
}

Error Handling

All errors return HTTP 400 with a structured JSON body:

{
  "status": "ERROR",
  "error": "validation_error",
  "message": "Field 'harvest_score' requires the Alpha plan or higher."
}

Error types

ErrorHTTPWhen
tier_restricted403Plan below Growth
validation_error400Unknown field, invalid operator, depth exceeded, 20+ conditions, tier violation
formula_error400Invalid formula expression, unknown field in formula, bad syntax

Common errors

TriggerMessage
Unknown field{ "error": "validation_error", "message": "Unknown field 'atm'" }
Field requires Alpha{ "error": "validation_error", "message": "Field 'harvest_score' requires the Alpha plan or higher." }
Invalid formula syntax{ "error": "formula_error", "message": "Unexpected token '+' at position 5" }
Formulas on Growth{ "error": "validation_error", "message": "Formula expressions require the Alpha plan." }

Limits & Validation

LimitValue
Max filter nesting depth3
Max total leaf conditions20
Max formula expression length200 chars
Max formula nesting depth10
Growth tier max limit10
Alpha tier max limit50
Growth tier offsetDisabled (forced to 0)
Growth daily requests2,500 (shared across all API endpoints)
Alpha daily requestsUnlimited

Strategy Scores — Methodology

Alpha-tier responses include a family of 0–100 composite scores that collapse the screening signals into a single number per strategy. Each score is rebuilt every refresh cycle; the full inputs are documented below so you can audit, reproduce, or weight them differently in your own formulas.

ScoreRangeInputsWhat “high” means
harvest_score 0–100 VRP z-score, vrp_regime, realized-vol stability, gamma regime, skew slope, term state VRP is rich, RV is behaving, dealers are long gamma → premium-selling is attractive
net_harvest_score 0–100 harvest_score penalized by dealer_flow_risk Harvest score that already nets out the hedging risk dealers will run into you
dealer_flow_risk 0–100 Regime, |net_gex|, gamma concentration, OI skew (top_3/top_5), term state, 0DTE concentration Dealer hedging flow is likely to amplify, not dampen, price moves — short-vol unfriendly
short_put_spread_score 0–100 Put-wing VRP, skew_25d_put, positive-gamma regime, downside RV stability Bull put spreads look cheap relative to downside realization
short_strangle_score 0–100 Two-sided VRP, symmetric skew, IV-RV spread, positive gamma Both wings are rich, both sides of RV look stable
iron_condor_score 0–100 Strangle score + term state (contango/mixed) + liquidity (atm_spread_pct) Strangle conditions plus clean term structure and tight spreads for the wings
calendar_spread_score 0–100 term_near_slope_pct, term_far_slope_pct, term_state, IV dispersion cross-expiry Front IV is elevated vs back — sell front, buy back

Scores are directional signals, not recommendations. Treat a high harvest_score the way you’d treat a high Sharpe ratio on a backtest: a necessary condition, not a sufficient one. Always combine with your own position-sizing, stop, and execution framework. If you want to re-weight the inputs, use formulas to build your own composite on top of the primitive fields.


Why 250 symbols, not 6,000?

The screener is backed by an always-hot in-memory store that refreshes every 5–10 seconds. Keeping ~250 symbols warm (full expiry chains, per-strike greeks, full OI/volume snapshots, freshly computed VRP/skew/scores) fits inside the compute budget that makes a sub-second POST response possible. Scaling to 6,000+ symbols means either dropping refresh to minutes (at which point the endpoint stops being a “live” screener) or standing up a larger compute fleet.

The 250 is curated, not arbitrary: it covers Tier 1 (the ten most-traded index and mega-cap options) on a 5s cycle, plus Tier 2 (sector ETFs, liquid single-names across every major GICS sector, popular meme/growth names) on a 10s cycle. If you need screening across a wider universe, combine the per-symbol endpoints in your own loop — the screener handles the “which names are interesting right now” question; the per-symbol endpoints answer “tell me everything about this one.”

Universe expansion is on the roadmap; enterprise customers who need specific tickers added to the Tier 2 refresh loop can reach out.

Ready to screen live data?

The Live Screener is available on Growth (20-symbol universe) and Alpha (~250 symbols, formulas, and strategy scores). Start with an API key on the free tier and upgrade when you’re ready to scan.

Ready to build?

Get your free API key and start pulling live options data in 30 seconds.