Yahoo Options Scraper [💰$1.2/1K] | Greeks | IV | Live Chains
Pricing
from $1.17 / 1,000 results
Yahoo Options Scraper [💰$1.2/1K] | Greeks | IV | Live Chains
Extract Yahoo Finance options chains with full Greeks, volume and implied volatility data for any ticker. Build options screeners, volatility research and pricing models.
Pricing
from $1.17 / 1,000 results
Rating
5.0
(1)
Developer
Ahmed Jasarevic
Maintained by CommunityActor stats
2
Bookmarked
198
Total users
30
Monthly active users
9 days ago
Last modified
Categories
Share
Yahoo Finance Options Scraper
Scrapes Yahoo Finance option chains over plain HTTP — no API key, no browser, no headless Chrome.
It returns both calls and puts across multiple expirations, in three output modes, and computes Greeks locally with Black-Scholes-Merton because Yahoo does not provide them.
What it does
| Transport | got-scraping over HTTP, sticky Apify proxy session, cookie + crumb handshake |
| Data source | Yahoo's public /v7/finance/options/{ticker} endpoint, plus /v8/finance/chart/^IRX for the risk-free rate |
| Modes | chain (one row per contract), summary (one row per ticker), unusual (unusual-activity scanner) |
| Expirations | Up to 12 per ticker in a single run, not just the nearest one |
| Greeks | Computed locally: delta, gamma, theta, vega, rho, theoretical price |
| Risk-free rate | Fetched from ^IRX automatically; falls back to 4.5%, or set your own via riskFreeRate |
| Free tier | Capped at 50 output rows. Every row is billed identically |
Runs that fail produce a non-zero exit and a clear error. It never reports success on empty or partial results.
Quick start
{"tickers": ["AAPL", "SPY", "NVDA"],"optionsExpirations": 4,"maxItems": 2000}
From the CLI:
$apify run -i '{"tickers":["AAPL","SPY"],"mode":"summary"}'
Input
Unknown fields are rejected with an error, not silently ignored. A misspelled key is the fastest way to quietly get wrong data back, so the run fails instead and lists the valid fields.
Tickers
tickers accepts an array or a string. Strings may be comma- or space-separated. Symbols are uppercased and
de-duplicated. Supports stocks, ETFs, indices (^GSPC), futures (ES=F), crypto pairs (BTC-USD) and FX
pairs (EURUSD=X). Dash style is converted to Yahoo's dot style, so BRK-B becomes BRK-B on the wire.
- Default:
["AAPL"] - Max: 50 per run
What to scrape
| Field | Default | Notes |
|---|---|---|
mode | chain | chain, summary or unusual |
contractTypes | ["calls","puts"] | calls, puts, or both. both / all also accepted |
optionsExpirations | 1 | How many nearest expirations to fetch (max 12) |
expirationDates | — | Exact YYYY-MM-DD dates. Takes priority over the range inputs |
expirationFrom / expirationTo | — | Inclusive date range |
includeGreeks | true | Computes Greeks locally |
riskFreeRate | auto | Decimal, e.g. 0.043. Overrides the ^IRX fetch |
expirationDates wins over expirationFrom/expirationTo when both are set.
The default of
optionsExpirations: 1fetches the nearest expiry, which is often 0–2 days out. Greeks on a contract with almost no time left are close to meaningless. If you want usable Greeks or a 30-day IV term structure, raise this —"optionsExpirations": 12reaches roughly one month out. The nearest expiry costs no extra request; each additional one is one more HTTP call.
Filtering
| Field | Notes |
|---|---|
moneyness | all, inTheMoney, outOfTheMoney, atTheMoney |
minStrike / maxStrike | Absolute strike bounds |
strikeWindowPercent | Keep strikes within N% of spot |
minVolume, minOpenInterest | Liquidity floors |
minImpliedVolatility, maxImpliedVolatility | Decimal, e.g. 0.3 for 30% |
minDaysToExpiration, maxDaysToExpiration | DTE window |
atmEpsilonPercent | How close to spot counts as ATM. Default 1.5 |
atTheMoney is resolved against atmEpsilonPercent, not a fixed price distance. A tolerance smaller than the
ticker's strike spacing matches nothing, so the minimum accepted value is 0.1.
Output shape
| Field | Default | Notes |
|---|---|---|
maxItems | 1000 | Caps rows across the whole run, not per ticker. Max 25000 |
sortBy | yahooOrder | See below |
concurrency | 4 | Tickers fetched in parallel over one sticky session (max 8) |
maxRetries | 3 | Retries per request before the run fails (max 5) |
requestDelayMs | 0 | Delay between requests |
proxyConfiguration | — | Passed through to Apify proxy settings |
sortBy accepts yahooOrder, strikeAsc, strikeDesc, volumeDesc, openInterestDesc,
impliedVolatilityDesc, impliedVolatilityAsc, premiumDesc, lastTradeNewest, nearestStrike,
moneynessAsc.
With several tickers and a maxItems cap, rows are allocated round-robin across tickers so no single ticker
eats the whole budget. Note that unusual mode ranks by score within each ticker, then interleaves.
Unusual mode thresholds
| Field | Default |
|---|---|
unusualVolumeOiRatio | 5 — flag when volume is at least 5x open interest |
unusualPremiumUsd | 50000 — flag when estimated traded premium reaches this |
Contracts with no open interest yet qualify on volume alone, since a fresh position has no OI to compare against.
Modes
chain
One row per contract. Both calls and puts, across every selected expiration. This is the default.
summary
One row per ticker with aggregated metrics: total and per-side volume, open interest, put/call ratios for both,
average IV per side, the 30-day ATM IV block, net premium flow, premium traded, the most active contract, the
max pain strike, and a sentiment label.
maxPainStrike is the strike at which the total payout to option holders is lowest, so the price tends to settle
there. It is weighted by open interest, falls back to volume when a chain carries no open interest at all, and is
null when neither exists. maxPainWeighting tells you which of the two was used.
Max pain is calculated from the full unfiltered chain for the nearest expiration — not from the rows you
filtered. A minVolume or strike-window filter would otherwise leave too few contracts and produce a
mathematically valid but meaningless number. The nearest expiration is reported in maxPainExpiration.
sentiment is a three-signal vote, not a single ratio:
- net premium flow (call premium minus put premium)
- put/call volume ratio — above 1.2 is bearish, below 0.8 is bullish
- put/call open interest ratio — same thresholds
Majority wins, a tie is mixed, and no usable data is neutral. Premium flow on its own is a weak signal, so
it needs confirmation from at least one of the other two.
unusual
Contracts that pass both unusual thresholds, ranked by unusualScore and then by traded premium. The score is
0.4 × log(volume/OI ratio) + 0.6 × log(premium), both on log scales. A linear scale saturates here — real
premiums run from $100k to $10M, so a linear score collapses to two distinct values and cannot rank anything.
Output
rowType is contract or summary and tells you which shape you have. Fields that do not apply to a given
row are null, never zero or an empty string.
Numeric fields are real numbers. impliedVolatility is a decimal fraction (0.32 = 32%), not the string
"32.00". Prices, strikes, Greeks and volumes are all numbers, so spreadsheet tools and JSON consumers do not
have to parse text.
Contract rows
- Identity:
contractSymbol,type,strike,expiration,expirationTimestamp,daysToExpiration - Quote:
lastPrice,bid,ask,mid,spread,spreadPercent,change,percentChange,lastTradeDate - Volatility and Greeks:
impliedVolatility,delta,gamma,theta,vega,rho,theoreticalPrice - Activity:
volume,openInterest,volumeOpenInterestRatio,premiumTradedUsd - Moneyness:
inTheMoney,moneyness(ITM/OTM/ATM),moneynessPercent - Underlying:
underlyingPrice,underlyingChange,marketCap,trailingPE,forwardPE,fiftyTwoWeekLow,fiftyTwoWeekHigh,earningsDate,dividendYield
Conventions
thetais per calendar day.vegais per one volatility point (a move of 0.01 in decimal IV).rhois per one percentage point of interest rate.premiumTradedUsdislastPrice × volume × 100, using the standard US equity contract multiplier. It is an estimate built off the last trade price, so a contract with a stale print can look far more active than it is.volumeOpenInterestRatiois usually the more honest activity signal.moneynessis a computed label, not a copy of Yahoo's flag.inTheMoneyfollows Yahoo's own field.
null means Yahoo had no data
This is important. null is not zero and not "the contract has no value":
bidandaskare0when Yahoo has no two-sided quote, which is common outside regular trading hours. When there is no quote at all,midandspreadarenullrather than0, because a spread of zero is a real claim about a market and this is not one.impliedVolatilityand all Greeks arenullwhen Yahoo has no usable IV.volumeOpenInterestRatioisnullwhen a contract has no open interest yet.
Data quality, measured
This section is the part most option scrapers get wrong, so here are the actual observed numbers.
Yahoo quantizes implied volatility and clamps it at 50%
Sampling 2000 contracts across AAPL, SPY, NVDA, TSLA, AMD and KO:
| Percentile | IV |
|---|---|
| p1 | 0.39% |
| p10 | 3.13% |
| p50 | 25.00% |
| p75 | 50.00% |
| p90 | 50.00% |
| p99 | 50.00% |
The values land on a quantized ladder — steps of roughly 0.001, frequently exact powers of two times 0.0078 —
and 25% of all contracts came back at exactly the same value, 0.500005, which is the 50% ceiling rather than a
real quote. Treat high IV readings from this source as suspiciously capped.
A large share of contracts have no IV at all
Only 1055 of 2000 contracts in that sample carried an IV above the 1% floor. Availability depends on the ticker, the strike and the session; deep out-of-the-money and near-expiry contracts are the worst affected.
IVs below 1% are treated as missing. Yahoo sends 0.00001 as a "no IV here" sentinel, and the low end of the
ladder carries values like 0.0005 and 0.0039 that are artifacts rather than real volatilities. The floor is
what keeps those from producing confident-looking Greeks with a delta of exactly 0 or 1.
Outside trading hours, the smile degenerates
With the market closed, the 30-day AAPL chain returned this IV ladder:
| Strike | Call IV | Put IV |
|---|---|---|
| 325 | 0.00001 | 0.0313 |
| 330 | 0.00001 | 0.0156 |
| 335 | 0.00001 | 0.0078 |
| 340 | 0.0039 | 0.00001 |
| 345 | 0.0156 | 0.00001 |
| 350 | 0.0313 | 0.00001 |
Monotonic in strike, on the same power-of-two ladder, with essentially no volatility at the money. A real smile
is U-shaped with a minimum at the money. This is not a smile. If you need reliable IVs, run during US regular
market hours and check marketState on the underlying.
Greeks inherit all of the above
The Black-Scholes implementation is standard and unit-tested against known values, but it can only be as good as
the IV it is handed. Greeks are null whenever the IV is missing or below the floor. Greeks on a 0-DTE contract
are also near-meaningless by construction, since there is no time value left to differentiate.
The 30-day block is reported honestly or not at all
iv30d, ivSkew30d, atmCallIV30d, atmPutIV30d and expiration30d are only populated when an expiration
genuinely close to 30 days out was fetched — within ±10 days — and Yahoo returned a usable ATM IV for it.
Otherwise they are null. An earlier version reported whatever expiration happened to be nearest to 30 days, so
a run covering only 0–7 DTE would label 7-day values as iv30d. It also reports the actual DTE in
daysToExpiration30d so you can verify what you are looking at.
atmStrike is the strike nearest spot, not simply the lowest strike inside the ATM band.
Premium traded can be overstated
premiumTradedUsd multiplies today's volume by the last trade price. For a contract that has not traded
recently, that price is stale and the figure inflates accordingly.
Known limitations
- No historical option data. Current chains only.
- IV is Yahoo's, quantized and capped at 50%. See above.
- Greeks are model output, not exchange data. They are computed from Yahoo's IV, and are only as accurate as that IV.
- No intraday or real-time option quotes. Bid and ask are whatever Yahoo last published.
- Contract multiplier is assumed to be 100. Correct for US equity options, wrong for adjusted contracts,
indexes and some adjusted symbols.
premiumTradedUsdinherits this assumption. - DTE comes from Yahoo's own expiration list.
- Not a full trading backtest. Sentiment and unusual scores are descriptive aggregates over one snapshot, not signals with demonstrated predictive value.
Charges and pricing
Billing is the Apify default: every dataset row is a chargeable result. The Actor uses the synthetic
apify-default-dataset-item event, which Apify charges automatically for each item written to the run's
default dataset. No extra charging code is needed and none is used.
This is also the simplest pricing you can have: one event, one price, and the user sees exactly what they
pay for. Custom per-mode events (contract, summary, unusual) are deliberately not charged
separately. To charge them, each would have to be registered as a paid event in the Apify Console, and
because the rows also land in the default dataset, users would be billed twice unless
apify-default-dataset-item were removed. More events also make costs harder for users to predict.
Free-tier users are capped at 50 rows and are billed the same per-row rate; they simply cannot request more.
RUN_SUMMARY goes to the key-value store via setValue, which is not a dataset row and is not charged.
Development
npm installnpm test # 111 unit tests, no network requirednpm start # local run; reads storage/key_value_stores/default/INPUT.json
To emulate a paying user locally:
$APIFY_USER_IS_PAYING=1 npm start
The unit tests cover Greeks against known Black-Scholes values, input validation, expiration selection, the round-robin allocator, every filter and sort, the unusual-activity gate and score, IV sentinel handling, 30-day honesty, the sentiment vote, unknown-field rejection, and the ATM tolerance default.
| File | Purpose |
|---|---|
main.js | Orchestration, mode dispatch, row pushing, run summary |
src/yahoo.js | Sticky proxy session, cookie/crumb handshake, retries, session refresh |
src/rows.js | Row building, filters, sorting, summaries, unusual scoring |
src/greeks.js | Black-Scholes-Merton |
src/input.js | Validation, normalization, unknown-field rejection |
src/constants.js | Defaults, limits, tuning constants |
src/util.js | Numeric and date helpers, concurrency |
License
ISC