Option Scanner API
Scan live and delayed option chains for trade candidates that match a strategy definition. Every candidate comes back with pricing, greeks, probability of profit, and risk metrics. The Option Scanner in the ORATS dashboard runs on this same service.
Send the raw token in an Authorization header, with no Bearer prefix. Requests without a valid token (or with a Bearer prefix) return a 403. The token query parameter is optional, and when you send both, the query parameter is the one validated, so keep them the same.
Responses are JSON: an array of candidate objects, one per trade candidate.
The Scan endpoint is rate limited to 10 requests per minute per token. Beyond that, requests return a 429 with a message saying when the minute window resets.
Overview and pricing for this and the other Tools APIs are in the Tools APIs section of the Data API page.
https://api.orats.io/scannerScan
Scans the current option chain for trade candidates matching a strategy definition and returns them with pricing, greeks, and distribution analytics. Candidates are ordered by closeness to the delta and DTE targets (each leg's distance to its targets, normalized by the band widths, summed), not by edge; use distPct, fcstPct, and smoothPct to judge value.
The scan always uses the most recent chain. Symbols with no options data return an empty array rather than an error.
Required attributes
- Name
v- Type
- number
- Description
The API version, passed as a query parameter. Always 2.
- Name
backtest- Type
- object
- Description
The strategy definition to scan with, passed in the JSON body.
Set the symbols to scan in general.symbols, one entry per symbol with weight and signals set to null. Response time scales with the number of symbols.
general.expirationType filters which listed expirations each leg may use, and applies per leg on both single- and multi-leg scans. ALL (also the behavior when the field is omitted or null, and the value in every request example here) keeps every listed expiration. MONTHLY keeps expirations on the third Friday of the month, or the Thursday immediately before it when that Friday is not listed for the ticker (the holiday-shifted monthly). WEEKLY keeps every listed expiration that is not a monthly by that rule. Values are case-insensitive and normalized to uppercase; anything else returns a 400 naming the field and the three choices.
Define one leg per entry.options entry: ratio (negative for short legs), optionType (call or put), leg (1-based), an opening.dte band of days to expiration (target, min, max), and an opening.strikeSelection with a band (target, min, max). The strikeSelection type is absDelta (the band is absolute delta), stockOTMPct (the band is strike divided by stock price, so 1.03 to 1.10 selects calls 3 to 10 percent out of the money and values below 1 select out-of-the-money puts), or both, which keeps a contract inside either band and takes a value whose min and max are objects holding an absDelta and a stockOTMPct number, with a single numeric target that is scored against both the absolute delta and the strike-to-stock ratio (the flat value shape, a nested target, or a missing target returns a 400). The type is case-sensitive and any other spelling returns a 400. optionType must be call or put, ratio a nonzero number, and legs numbered 1 to N in order; each of those returns a 400 naming the leg otherwise. Symbols are trimmed and upper-cased, and share classes may be written BRK_B, BRK.B, or BRK/B.
Multi-leg definitions also require entry.legRelation, keyed by leg pair: for example entry.legRelation.strikeWidth.leg1Leg2 with min and max in strike points, and entry.legRelation.dteDiff.leg1Leg2 with min and max in days (0,0 forces the same expiration). The relation properties are strikeWidth (strike points), dteDiff (days), and deltaTotal (the ratio-weighted net delta of the pair); any other key under legRelation, or a leg pair that does not exist for the leg count, returns a 400 when it has non-null values (all-null placeholders are accepted). Spread-level price, delta, and yield bands go in entry.spread as min and max objects, with an optional target that is scored against the assembled spread. A body that is not valid JSON returns a 400 with the message "payload format error."; a multi-leg definition without entry.legRelation, a leg without opening or opening.dte, a missing general or entry block, or a symbols entry without a string symbol returns a 400 with a message naming the problem; so does a leg without a strikeSelection.value, a strike, DTE, or spread band whose min exceeds its max or whose target lies outside it, and a body without a backtest object (backtest input is required.). legRelation bands are validated the same way: an inverted or non-numeric strikeWidth, dteDiff, or deltaTotal band, or a band written as an array instead of a min and max object, returns a 400 naming the pair; a band that is valid but that no contract in the leg pools can satisfy still returns an empty array.
See the request example for a complete single-leg definition.
Optional attributes
- Name
totalTrades- Type
- number
- Description
The maximum number of candidates across all symbols. Applied only when tradesPerSymbol is 0 or null; otherwise tradesPerSymbol alone sets the response size. Must be a non-negative integer. Defaults to 10.
- Name
tradesPerSymbol- Type
- number
- Description
The maximum number of candidates to return per symbol (not capped). For multi-leg definitions it also sets how many candidate contracts each leg keeps before the legs are paired: the closest tradesPerSymbol divided by the leg count contracts to that leg's targets, whatever the strategyName. Because pairing happens inside those pools, a low tradesPerSymbol can return an empty array even though matching spreads exist; use 20 to 100 for two legs and at least 40 for four. Must be a non-negative integer (a numeric string is accepted); a fraction, a negative, or a non-numeric value returns a 400. Defaults to 1.
- Name
dataSource- Type
- string
- Description
The quote feed to scan: real-time or delayed (the 15-minute feed). real-time, realtime, and delayed are accepted in any letter case; any other value returns a 400. Real-time quotes are live OPRA data, so they need the same live data entitlement (signed OPRA agreements) as the live endpoints; without it a real-time scan returns a 403. Each leg's updatedAt is the quote time. Ex: delayed
- Name
distribution- Type
- boolean
- Description
Whether to include distribution analytics. When true, each candidate includes risk, maxGain, maxLoss, pop, and breakEvens, distPct is populated, and each leg includes distValue. Recommended: true.
- Name
drift- Type
- number
- Description
The assumed stock drift in percent, for probability of profit. 0 is neutral; positive is a bullish outlook, negative is bearish. Drift changes pop, distPct, and each leg's distValue; values beyond roughly plus or minus 20 saturate pop at 100 or 0, and a value outside -100 to 100 or a non-numeric value returns a 400. Ex: 10
- Name
id- Type
- string
- Description
A label for the scan. Not validated and not echoed in the response (which is an array of candidates with no wrapper object). Ex: LongCall
Response fields
- Name
date, ticker, stockPrice- Type
- candidate
- Description
The scan's business date, the underlying, and the underlying price at the first leg's quote.
- Name
bid, mid, ask- Type
- candidate
- Description
The structure's net price from the legs' quotes; mid is the true midpoint. Credit structures are negative (mid is minus the credit received), so bid is greater than ask with that sign convention.
- Name
iv- Type
- candidate
- Description
Ratio-weighted average of the legs' tradeVol, in percent.
- Name
distPct, fcstPct, smoothPct- Type
- candidate
- Description
Percent edge of the distribution value, the forecast-volatility price, and the smoothed theoretical price over mid. distPct is null without distribution.
- Name
delta, gamma, theta, vega, deltaCost- Type
- candidate
- Description
Ratio-weighted sums of the per-leg greeks (per share) and of deltaCost.
- Name
risk, maxGain, maxLoss, pop, breakEvens- Type
- candidate
- Description
Present with distribution true. maxGain and maxLoss are dollars per one-lot at mid, evaluated across the plus or minus three sigma price range, so unbounded payoffs are bounded there; risk is maxGain divided by the absolute maxLoss; pop is the percent probability of profit at expiration on a 21-bucket terminal distribution (profit zones narrower than about 0.3 sigma, such as butterflies, produce a pop near zero), null for legs with a ratio other than plus or minus one; breakEvens is an array of underlying prices, empty for calendars.
- Name
earningsDate, lastEarningsDate, earningsTimeOfDay, earningsConfirmed, divDate, divAmt- Type
- candidate
- Description
Next and last earnings dates, the earnings time as HHMM, whether the date is confirmed (0 or 1), and the next ex-dividend date and amount. Null for ETFs and indexes.
- Name
legs[].leg, ratio, optionType, expirDate, expiryTod, strike, dte- Type
- leg
- Description
The contract. expiryTod is am or pm settlement; dte counts today's expiration as 1.
- Name
legs[].optionBid, optionAsk, tradeOptPx, theoOptPx, fcstOptPx, fcstRatio- Type
- leg
- Description
The raw quote, the assumed fill (long legs at bid plus a fraction of the spread, short legs at ask minus it; the fraction is 0.75 for one leg, 0.66 for two, 0.56 for three, 0.53 for four or more), the ORATS smoothed theoretical value, the forecast-volatility value, and fcstOptPx divided by tradeOptPx.
- Name
legs[].iVolBid, iVolAsk, tradeVol, theoVol, calVol, unadjVol- Type
- leg
- Description
Implied volatilities: iVolBid and iVolAsk are decimals; tradeVol (at the fill), theoVol (smoothed), calVol (including the earnings effect), and unadjVol (ex-earnings) are percent.
- Name
legs[].delta, gamma, vega, theta, volume, openInterest, deltaCost, updatedAt, distValue- Type
- leg
- Description
Per-share greeks (put deltas negative), the side's volume and open interest, premium per unit of delta, volatility, and time (tradeOptPx divided by the absolute delta times stock price times tradeVol times the square root of dte, times ratio times 100), the quote time in UTC, and the leg's expected value under the drift-adjusted distribution (with distribution true).
{
"backtest": {
"general": {
"strategyName": "LongCall",
"expirationType": "ALL",
"stockPosition": {
"type": "none",
"ratio": 0
},
"symbols": [
{
"weight": null,
"signals": null,
"symbol": "AAPL"
}
],
"returnType": {
"perTrade": "notional",
"daily": "average"
},
"commission": {
"option": 1,
"stock": 0.01
}
},
"entry": {
"options": [
{
"ratio": 1,
"optionType": "call",
"leg": 1,
"opening": {
"dte": {
"target": 30,
"min": 20,
"max": 45
},
"strikeSelection": {
"type": "absDelta",
"value": {
"target": 0.3,
"min": 0.2,
"max": 0.5
}
}
}
}
]
},
"exit": {
"dteDays": "expire"
}
},
"totalTrades": 20,
"tradesPerSymbol": 10,
"dataSource": "delayed",
"distribution": true,
"drift": 0,
"id": "LongCall"
}
Response
[
{
"date": "2026-07-15",
"ticker": "AAPL",
"stockPrice": 327.33,
"bid": 4,
"mid": 4.2,
"ask": 4.4,
"iv": 27.6,
"distPct": -31.65,
"fcstPct": 4.02,
"smoothPct": 0.24,
"delta": 0.27,
"gamma": 0.01326,
"theta": -0.15,
"vega": 0.3189,
"deltaCost": 0.03166,
"risk": 10.8985132,
"maxGain": 4577.38,
"maxLoss": -420,
"pop": 17.82,
"breakEvens": [
349.2
],
"earningsDate": "2026-07-30",
"lastEarningsDate": "2026-04-30",
"earningsTimeOfDay": "1630",
"earningsConfirmed": 1,
"divDate": "2026-08-11",
"divAmt": 0.27,
"legs": [
{
"leg": 1,
"ratio": 1,
"optionType": "call",
"expirDate": "2026-08-14",
"strike": 345,
"expiryTod": "pm",
"dte": 31,
"optionBid": 4,
"optionAsk": 4.4,
"tradeOptPx": 4.3,
"theoOptPx": 4.21,
"fcstOptPx": 4.3688,
"iVolBid": 0.2667,
"iVolAsk": 0.2797,
"tradeVol": 27.6,
"theoVol": 27.4,
"fcstRatio": 1.02,
"delta": 0.27,
"gamma": 0.01326,
"vega": 0.3189,
"theta": -0.15,
"volume": 598,
"openInterest": 998,
"calVol": 28.42,
"unadjVol": 24.11,
"deltaCost": 0.03166,
"updatedAt": "2026-07-15T19:59:49Z",
"distValue": 2.8708215979740945
}
]
}
]