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.
Endpoint
X-Api-Key)
Rate Limited: Yes
Refresh: 5-10s
Request Body (JSON)
| Field | Type | Required | Description |
|---|---|---|---|
filters | object | no | Filter tree (recursive AND/OR groups with leaf conditions) |
sort | array | no | Sort specs, applied in order (primary sort first) |
select | array | no | Field names to return, or ["*"] for full flat object |
formulas | array | no | Computed fields (Alpha only) |
limit | number | no | 1-10 (Growth), 1-50 (Alpha). Default: 50 |
offset | number | no | Pagination offset (Alpha only). Default: 0 |
{} 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"},
})
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.
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.
SPY
QQQ
SPX
IWM
VIX
AAPL
TSLA
NVDA
AMZN
META
MSFT
AMD
GOOGL
AVGO
NFLX
COIN
PLTR
MSTR
JPM
SMH
VIX
RUT
DJX
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
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
JPM
BAC
WFC
GS
MS
C
SCHW
BLK
AXP
V
MA
PYPL
COF
USB
PNC
BK
ICE
CME
SPGI
ADP
SOFI
HOOD
AFRM
UNH
JNJ
PFE
MRK
ABBV
LLY
TMO
ABT
DHR
BMY
AMGN
GILD
ISRG
MDT
SYK
BSX
ZTS
VRTX
REGN
MRNA
BIIB
DXCM
HIMS
WMT
COST
HD
LOW
TGT
TJX
ROST
DG
NKE
SBUX
MCD
YUM
CMG
LULU
DECK
PG
KO
PEP
PM
MO
MNST
BA
CAT
DE
HON
GE
RTX
LMT
NOC
GD
UPS
FDX
CSX
UNP
DAL
UAL
AAL
XOM
CVX
COP
SLB
EOG
MPC
VLO
PSX
OXY
DVN
HAL
FANG
WMB
KMI
LNG
F
GM
TM
RIVN
LCID
NIO
XPEV
LI
AMT
PLD
CCI
EQIX
SPG
O
DLR
PSA
MARA
RIOT
CLSK
HUT
T
VZ
TMUS
CMCSA
DIS
WBD
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 fieldsstrikes.X— filters strike-level fields within each expirycontracts.X— filters contract-level fields (individual calls/puts)
Example: { "field": "expiries.days_to_expiry", "operator": "eq", "value": 7 }.
Operators
Numeric Operators
| Operator | Meaning | Reads as | Value type | Example |
|---|---|---|---|---|
eq | Equals | field = value | number | "value": 18.5 |
neq | Not equal | field ≠ value | number | "value": 0 |
gt | Greater than | field > value | number | "value": 20 |
gte | Greater than or equal | field ≥ value | number | "value": 15 |
lt | Less than | field < value | number | "value": 5 |
lte | Less than or equal | field ≤ value | number | "value": 100 |
between | Within range (inclusive) | min ≤ field ≤ max | array [min, max] | "value": [15, 25] |
in | Matches any value in list | field ∈ [list] | array of numbers | "value": [1, 2, 3] |
String Operators
| Operator | Meaning | Value type | Example |
|---|---|---|---|
eq | Equals (case-insensitive) | string | "value": "positive_gamma" |
neq | Not equal | string | "value": "toxic_short_vol" |
in | Matches any string in list | array of strings | "value": ["contango", "mixed"] |
Null Operators (any type)
| Operator | Meaning | Example |
|---|---|---|
is_null | Field exists but has no value (missing data) | { "field": "atm_iv", "operator": "is_null" } |
is_not_null | Field 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)
| Category | Fields | Tier |
|---|---|---|
| Identity & Price | symbol, price, bid, ask, mid | Growth |
| Exposure | regime, net_gex, net_dex, net_vex, net_chex | Growth |
| Key Levels | gamma_flip, call_wall, put_wall, max_positive_gamma, max_negative_gamma, highest_oi_strike, max_pain, zero_dte_magnet | Growth |
| Zero-DTE | zero_dte_net_gex, zero_dte_pct_of_total | Growth |
| Volatility | atm_iv, fair_vol, rv_5d, rv_10d, rv_20d, rv_30d, rv_60d | Growth |
| 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 VRP | put_wing_iv_25d, call_wing_iv_25d, downside_rv_20d, upside_rv_20d, downside_vrp, upside_vrp | Alpha |
| Skew | skew_25d, skew_25d_put, skew_25d_call | Growth |
| Term Structure | term_near_slope_pct, term_far_slope_pct, term_state (contango, backwardation, mixed, unknown) | Growth |
| Liquidity | atm_spread_pct, wing_spread_pct, atm_contracts, wing_contracts | Growth |
| OI Concentration | top_3_pct, top_5_pct, herfindahl | Growth |
| Flow | total_call_oi, total_put_oi, total_call_volume, total_put_volume, pc_ratio_oi, pc_ratio_volume | Growth |
| IV Dispersion | iv_dispersion_cross_expiry, iv_dispersion_cross_strike | Growth |
| GEX by DTE | gex_0to7_dte, gex_8to30_dte, gex_31to60_dte, gex_61plus_dte | Growth |
| Strategy Scores | harvest_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.Xmatches if any expiry matchesstrikes.Xmatches if any strike in any expiry matchescontracts.Xmatches 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
| Field | Description |
|---|---|
total_count | Symbols matching filters (before pagination) |
returned_count | Rows in this page |
universe_size | Total symbols in store |
offset / limit | Effective pagination (clamped to tier limits) |
tier | "growth" or "alpha" |
as_of | Server UTC timestamp |
Select behavior
"select": ["symbol", "price"]— flat row with only these fields"select": ["*"]— full flat object includingexpiry_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
| Error | HTTP | When |
|---|---|---|
tier_restricted | 403 | Plan below Growth |
validation_error | 400 | Unknown field, invalid operator, depth exceeded, 20+ conditions, tier violation |
formula_error | 400 | Invalid formula expression, unknown field in formula, bad syntax |
Common errors
| Trigger | Message |
|---|---|
| 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
| Limit | Value |
|---|---|
| Max filter nesting depth | 3 |
| Max total leaf conditions | 20 |
| Max formula expression length | 200 chars |
| Max formula nesting depth | 10 |
| Growth tier max limit | 10 |
| Alpha tier max limit | 50 |
| Growth tier offset | Disabled (forced to 0) |
| Growth daily requests | 2,500 (shared across all API endpoints) |
| Alpha daily requests | Unlimited |
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.
| Score | Range | Inputs | What “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.
Related
- Screener Field Taxonomy — complete glossary of every filterable field
- Screener Cookbook — 10+ realistic trader recipes
- Pillar: Real-Time Options Screener Guide
- Article: Screener Recipes for GEX, VRP and 0DTE
- Article: How to Scan GEX With an API
Ready to build?
Get your free API key and start pulling live options data in 30 seconds.