Most developers assume options data means a Bloomberg terminal, a six-figure institutional feed, or scraping broker websites that break every earnings season. With a proper options data API, none of that is necessary.

If you’re building:

  • an options screener,
  • a volatility backtest,
  • or options analytics inside a fintech product,

a REST API call gets you there in under ten minutes.

Register & Get Data

The free sources fall apart the moment you build something real

Free options data looks fine in a demo. Yahoo Finance’s options endpoint isn’t official and stops returning data without warning. Scraping a broker’s site means parsing HTML that changes with every UI redesign, and the IP block usually shows up right after you’ve wired the pipeline into something that matters.

Unfortunately, developers tend to discover this the hard way, after a pipeline built on one of these sources has already been running for weeks.

In the end, the failure mode is always the same: no Greeks, no historical depth, and a source that goes quiet mid-project.

Options data doesn’t need more free sources. It needs one that ships chains, Greeks, implied volatility, and open interest history together, instead of three unofficial feeds stitched into something fragile.

EODHD’s Options Data API in three endpoints

EODHD’s Options Data API covers 6,600+ US stocks and ETFs on NYSE, NASDAQ, and AMEX, with historical data back to Q4 2023. Every contract record ships with 42+ fields:

  • the full option chain, calls and puts, every strike and expiration
  • all five Greeks: delta, gamma, theta, vega, rho
  • implied volatility, with day-over-day change
  • open interest, with day-over-day change and vol/OI ratio
  • bid/ask with sizes, OHLC, midpoint, and theoretical price

No separate Greeks add-on. No Black-Scholes implementation to maintain. The Greeks arrive pre-calculated in the response.

Three endpoints cover the whole surface:

GET /api/mp/unicornbay/options/contracts         # option chain, filterable
GET /api/mp/unicornbay/options/eod # historical EOD data, compact mode for bulk pulls
GET /api/mp/unicornbay/options/underlying-symbols # ticker coverage check

After testing a handful of options data providers for client projects, this is the one I keep going back to. It’s the only one where Greeks, IV, and historical data starting in Q4 2023 come in the same subscription instead of three separate ones.

What these fields actually tell you about a trade

Each Greek answers a different question a trader asks before pulling the trigger, not just an academic label:

  • Delta approximates the probability a contract expires in the money, and doubles as a hedge ratio: a 0.30 delta call moves roughly like holding 30 shares.
  • Gamma shows how fast that delta will change as the stock moves, which is why near-the-money contracts get jumpy close to expiration.
  • Theta is the daily cost of holding the option, what gets eaten by time decay if the stock does nothing.
  • Vega measures exposure to a change in implied volatility itself, separate from the stock’s price.
  • Rho, the sensitivity to interest rates, mostly matters for contracts with a year or more left.

Implied volatility is the market’s forecast of how much the stock will move, priced into the option rather than the stock. For example, high IV means expensive options and richer premium for anyone selling them. By contrast, low IV means cheap options, better value for buying protection or making a directional bet. Comparing a contract’s IV against its own history, not against a round number, is what tells you if 32% is actually high for that specific stock.

Similarly, open interest is how many contracts are still outstanding, a rough proxy for how liquid that strike and expiration are. However, volume alone can be one large trade closing a position. Comparing volume against open interest, the vol/OI ratio used in the screener further down, is what separates routine turnover from something that looks like fresh positioning worth a closer look.

How to get your EODHD API key

1. Use the “demo” API key to test our data from a limited set of tickers without registering:
AAPL.US | TSLA.US | VTI.US | AMZN.US | BTC-USD | EUR-USD
Real-Time Data and all of the APIs (except Bulk) are included without API calls limitations with these tickers and the demo key.
2. Register to get your free API key (limited to 20 API calls per day) with access to:
End-Of-Day Historical Data with only the past year for any ticker, and the List of tickers per Exchange.
3. To unlock your API key, we recommend to choose the subscription plan which covers your needs.

Register & Get Data

For the Options Data API, the demo key works with AAPL and AMZN, and the free plan doesn’t include options data, so a paid plan is needed beyond those two tickers.

Pulling a live option chain with the Options Data API

There’s a public demo token, so you can test the response shape before signing up for anything:

curl -g "https://eodhd.com/api/mp/unicornbay/options/contracts?filter[underlying_symbol]=AAPL&filter[type]=call&page[limit]=5&api_token=demo"

Also, the -g flag matters here: curl treats [ ] in a URL as its own range/glob syntax, even inside quotes, so filter[type]=call fails with “bad range in URL” without it.

Every example in this guide uses Python’s requests library directly against the endpoint:

import requests
from datetime import date, timedelta

API_TOKEN = "YOUR_API_TOKEN"
BASE_URL = "https://eodhd.com/api/mp/unicornbay/options/contracts"

today = date.today()
window_end = today + timedelta(days=60)

params = {
"filter[underlying_symbol]": "AAPL",
"filter[type]": "call",
"filter[exp_date_from]": today.isoformat(),
"filter[exp_date_to]": window_end.isoformat(),
"page[limit]": 100,
"api_token": API_TOKEN,
"fmt": "json",
}

resp = requests.get(BASE_URL, params=params)
resp.raise_for_status()
chain = resp.json()

for contract in chain["data"][:5]:
attrs = contract["attributes"]
print(
f"{attrs['contract']} | strike {attrs['strike']} | "
f"exp {attrs['exp_date']} | delta {attrs['delta']} | IV {attrs['volatility']}"
)

The response is JSON:API formatted. Greeks and IV arrive already attached to each contract, no post-processing required.

Register & Get Data

Backtesting with historical EOD data

The /options/eod endpoint pulls the same contract-level fields across a date range instead of a single snapshot, which is what you want for backtesting or building a volatility surface. In addition, compact mode returns arrays instead of objects, which keeps bulk downloads lighter:

resp = requests.get(
"https://eodhd.com/api/mp/unicornbay/options/eod",
params={
"filter[underlying_symbol]": "AAPL",
"filter[tradetime_from]": "2024-01-01",
"filter[tradetime_to]": "2024-01-31",
"compact": 1,
"api_token": "YOUR_API_TOKEN",
"fmt": "json",
},
)
history = resp.json()

Furthermore, pagination supports offsets up to 10,000 and up to 1,000 records per page. The number of requests needed depends on the symbol, date range, and number of contracts. For larger datasets, narrow the query with filters and retrieve smaller subsets.

Building something with the Options Data API

For instance, a common first build is an IV-rank screener: pull chains for a watchlist, flag contracts where implied volatility sits in the top decile for that symbol, and rank by vol/OI ratio to surface unusual activity.

BASE_URL = "https://eodhd.com/api/mp/unicornbay/options/contracts"
watchlist = ["AAPL", "TSLA", "NVDA"]
flagged = []

for symbol in watchlist:
params = {
"filter[underlying_symbol]": symbol,
"filter[type]": "call",
"page[limit]": 1000,
"api_token": API_TOKEN,
"fmt": "json",
}
resp = requests.get(BASE_URL, params=params)
resp.raise_for_status()
for contract in resp.json()["data"]:
attrs = contract["attributes"]
if attrs["vol_oi_ratio"] and attrs["vol_oi_ratio"] > 3:
flagged.append((symbol, attrs["contract"], attrs["vol_oi_ratio"], attrs["volatility"]))

flagged.sort(key=lambda row: row[2], reverse=True)

From here:

  • an unusual options activity screener
  • a volatility surface built across strikes and expirations
  • an earnings-play backtest using the historical EOD endpoint

One more example: covered-call candidates

A covered call means selling a call option against stock you already own, collecting the premium as income in exchange for capping the upside at the strike. If the stock stays below that strike, the option expires worthless and the premium is pure income. If it runs past the strike, the shares get called away at that price, which is the trade-off.

In practice, the 0.20-0.35 delta band is the common sweet spot for this. Low enough that assignment stays a minority outcome, high enough that the premium is worth collecting instead of picking a strike so far out that the payout barely covers the transaction cost. Filtering the chain for that band is where the code below comes in:

MIN_DELTA, MAX_DELTA = 0.20, 0.35
target_exp = today + timedelta(days=45)

params = {
"filter[underlying_symbol]": "AAPL",
"filter[type]": "call",
"filter[exp_date_from]": today.isoformat(),
"filter[exp_date_to]": target_exp.isoformat(),
"page[limit]": 500,
"api_token": API_TOKEN,
"fmt": "json",
}
resp = requests.get(BASE_URL, params=params)
resp.raise_for_status()
chain = resp.json()

candidates = [
c["attributes"] for c in chain["data"]
if c["attributes"].get("delta") and MIN_DELTA <= c["attributes"]["delta"] <= MAX_DELTA
]
candidates.sort(key=lambda c: c["exp_date"])

Things to keep in mind with the Options Data API

Implementation considerations for request quotas, ticker formats, pagination, and error handling:

  • Options endpoints cost 10 API calls per request, not 1, so budget a paid plan’s daily quota accordingly. The Marketplace’s 24-hour quota window starts with your first request, rather than resetting at 00:00 GMT. Plan bulk downloads around that window; it is separate from the per-minute rate limit.
  • This is end-of-day data. There’s no real-time or intraday options feed, so day-trading use cases need a different data source.
  • The rate limit is 1,000 requests/minute across EODHD endpoints. When a 429 response includes a Retry-After header, use its value to schedule the next attempt.
  • The options endpoints use a plain ticker (AAPL), while other EODHD endpoints use the SYMBOL.EXCHANGE format (AAPL.US). Apply the appropriate format when combining these APIs in a project.
  • Pagination caps at 10,000 offset and 1,000 records per page. For tickers with hundreds of strikes and expirations, narrow with the date or strike filters instead of paging through everything.
  • Error responses follow a {status, error, message} envelope. Check the status code before assuming resp.json()[“data”] exists.
  • The demo token works for AAPL and AMZN. Anything else needs a real token from a paid plan.
  • Finally, testing with curl needs the -g flag. curl parses [ ] in a URL as its own range/glob syntax, even inside quotes, so filter[type]=call otherwise fails with “bad range in URL”.

A minimal retry wrapper that handles the rate limit and surfaces the error envelope:

import time

def fetch_chain(symbol, max_retries=3, **filters):
params = {"filter[underlying_symbol]": symbol, "api_token": API_TOKEN, "fmt": "json"}
for key, value in filters.items():
params[f"filter[{key}]"] = value

for attempt in range(max_retries):
resp = requests.get(BASE_URL, params=params)
if resp.status_code == 429:
wait = int(resp.headers.get("Retry-After", 5))
time.sleep(wait)
continue
if resp.status_code >= 400:
body = resp.json()
raise RuntimeError(f"{resp.status_code} {body['error']}: {body['message']}")
return resp.json()
raise RuntimeError("Gave up after retries")

Three things worth remembering

  • The bottleneck was never access. It’s getting chains, Greeks, IV, and OI history from one source instead of three.
  • Filtering server-side, by strike, expiration, or type, keeps payloads small and pipelines fast.
  • Pre-calculated Greeks save real engineering time. Nobody wants to maintain a Black-Scholes implementation for a side project.

FAQs

❓ Is there a free options data API?

✅ Not with Greeks and real historical depth. Free sources like Yahoo Finance’s unofficial endpoint return raw chains with no Greeks and no uptime guarantee. EODHD’s plan starts at $29.99/mo and includes all three endpoints, with no separate Greeks charge.

❓ Does the API cover real-time options data?

✅ No, it’s end-of-day, updated daily after market close. Fields like volume_change and vol_oi_ratio approximate flow-style analysis on an EOD basis, which is enough for most research and backtesting work.

❓ How far back does the historical data go?

✅ Dataset coverage starts in Q4 2023. The available date range for an individual contract depends on its trading history and the records available for that contract.

❓ Can I filter by strike price and expiration date?

✅ Yes. The contracts endpoint takes filter[strike_from], filter[strike_to], filter[exp_date_from], and filter[exp_date_to], so you pull the slice of the chain you need instead of the whole thing.

❓ Is there a Python SDK?

✅ The examples in this guide use requests directly against the REST endpoint, so there’s nothing else to install.

Final Thoughts

Options data used to mean a six-figure feed or a scraper held together with duct tape.

Now it’s a REST call away.

Register & Get Data

Do you enjoy our articles?

We can send new ones right to your email box