Sitemap
Level Up Coding

Coding tutorials and news. The developer homepage gitconnected.com && skilled.dev && levelup.dev

Exploring the US Options Data API by EODHD APIs with Python

An introduction to the US Options Data API by EODHD APIs, understanding the scale, limitations, and how to utilize it effectively

9 min readOct 7, 2026

--

Press enter or click to view image in full size
Photo by Austin Distel on Unsplash

Disclaimer: I am an affiliate of EODHD APIs and may receive compensation if you purchase a product or subscription through links in this article. This content is for educational and informational purposes only and is not financial, investment, tax, or legal advice. Any stocks, options, strategies, or securities referenced are for illustrative purposes only and should not be considered recommendations. Investing and trading involve substantial risk, including loss of principal, and options can involve additional risks such as leverage, volatility, and time decay. Always conduct your own research and consider your financial situation, objectives, and risk tolerance before making investment decisions.

Introduction

Trading Options can involve both significant opportunities and risk. In addition, a stock could have hundreds, possibly thousands, of unique option contracts available to trade on any given trading day, making exploration of the underlying data a challenge in itself. What if you could give yourself an edge in this process? That is where the US Options Data API by EODHD APIs can come into play.

In this article, I will provide the reader with a gentle yet practical introduction to the US Options Data API by EODHD APIs using Python. More specifically, I will demonstrate:

  • How to craft a simple GET helper function
  • How to retrieve a list of the available underlying symbols
  • A practical view of the options contract endpoint with AAPL

Importing Libraries and the API Key

The following code chunks in this article come from a Google Colab notebook I built. To mitigate the risk of exposure, I added my API key as a secret. In addition, here are the libraries I utilized in this project.

Image provided by the author
# Imports
import requests
import pandas as pd
import numpy as np

from google.colab import userdata


# Load EODHD API key from Colab Secrets

API_KEY = userdata.get("EODHDAPIKEY")

if not API_KEY:
raise ValueError(
"EODHDAPIKEY was not found in Google Colab Secrets."
)

print("EODHD API key loaded successfully.")

Crafting a GET helper function

When working with APIs, especially programmatically, I find it very useful to build a GET helper function. This allows one to dynamically select parameters, check for errors in the response from the server (the server being “EODHD APIs”), and directly return the desired response from the API call.

Let’s break down the function below that I built for retrieving the available underlying symbols for this API:

def get_option_underlyings(api_key):
url = "https://eodhd.com/api/mp/unicornbay/options/underlying-symbols"

params = {
"api_token": api_key
}

response = requests.get(
url,
params=params,
timeout=30
)

response.raise_for_status()

return response.json()
  • url: the base URL to use in the function.
  • params: dictionary of parameters. In this function, we just need the EODHD API key.
  • response: stores the response returned by the EODHD APIs server after the GET request is sent.

Anatomy of an EODHD API response with the Underlying Symbols endpoint

Let’s take a look at the output of the previous function for retrieving available underlying symbols with option data.

underlyings_json = get_option_underlyings(API_KEY)
print(underlyings_json.keys())
Image provided by the author
  • meta: Metadata about the request, including the number of records, the fields, and whether the response is considered compact; if true, meaning the data is returned as a flat array of values.
  • data: The data we want; in this case, a list of the underlying symbols with option data.
  • links: If there are additional pages, this will show the full URL body to call the next page's worth of data. We will discuss this in more detail when looking at the core options contract endpoint.
print("Metadata: ",underlyings_json['meta'])
print("Available Symbols with options data: ", underlyings_json['data'][0:5])
print("Pagination links ",underlyings_json['links'])
Press enter or click to view image in full size
Image provided by the author

As of the day I am writing this article, EODHD APIs is offering nearly 7,000 underlying symbols with option data. Now, let’s take a look at one of them with the EODHD APIs Options Contract endpoint.

The Options Contract Endpoint

This endpoint allows one to dynamically request the latest end-of-day option contract data for a given stock, including but not limited to bid/ask prices, open interest, volume, and Greeks, across all available options by strike, days to expiration, and type (calls and puts).

An options chain can contain multiple expiration dates, both calls and puts, and many strike prices within each expiration; in other words, the number of contracts can grow very quickly. A single day’s worth of options data for a given stock can be extremely large; therefore, when working with or requesting this type of data, ensure you are as strategic as possible. I will show you some practical applications of this endpoint and its filters to achieve this. Let’s take a look at the basic GET helper function I constructed for this endpoint; you will notice it is very similar to the previous one.

def get_option_data(api_key, params):
url = "https://eodhd.com/api/mp/unicornbay/options/contracts"

request_params = params.copy()
request_params["api_token"] = api_key

response = requests.get(
url,
params=request_params,
timeout=30
)

response.raise_for_status()

return response.json()

Notice how params are passed to the get method of the requests object. I created a copy of the parameters and passed the copy to the get method. The main reason I did this is that it does not expose the API key in another variable, thus mitigating the risk of exposure. Within the notebook, the API key remains as a “secret” (pun intended). More specifically, a Google Colab secret. Now, let’s request the latest end-of-day options contracts for AAPL.

params = {
"filter[underlying_symbol]": "AAPL",
"page[limit]": 5
}

aapl_options_json = get_option_data(
API_KEY,
params
)
print(aapl_options_json['meta'])
print(aapl_options_json['data'])
print(aapl_options_json['links'])
Press enter or click to view image in full size
Image provided by the author

Let’s take a look at the parameters (indicated by the params variable) that I passed in this request. As you probably guessed, filter[underlying_symbol] is how one specifies which stock's options contract data to pull. On the other hand, page[limit] controls the number of observations (rows) the request retrieves. In this case, a row represents one option contract.

One key factor to consider when using the EODHD APIs options data endpoint is that it is a marketplace endpoint. For EODHD APIs marketplace endpoints, a single API request to this endpoint counts as 10 API calls against your limit.

Get Jacob Ingle’s stories in your inbox

Join Medium for free to get updates from this writer.

One might naturally ask how the filter parameter affects the 10-API-call rule. There is only one endpoint call, up to a limit of 1000. As you will see shortly, some stocks will have yields of many more than 1000 observations. Luckily, the API offers pagination, and as you can see in the “links” field, there is a URL. That is the URL that can be called to ingest more data at the same limit. Keep in mind that each additional page is another HTTP request, and each HTTP request currently costs 10 API call units against the Options Marketplace quota. One additional detail I should note about the EODHD APIs US Options Marketplace quota is that the limit operates on a 24 hour window beginning with your first marketplace request rather than GMT midnight as EODHD APIs core API quota behavior. Let’s take a look at how to execute this properly.

Pagination Example with AAPL

Let’s make another call for AAPL options, but this time, we won’t specify a limit.

params = {
"filter[underlying_symbol]": "AAPL"
}

aapl_options_json = get_option_data(
API_KEY,
params
)

Then take a glimpse at the metadata:

aapl_options_json['meta']
Image provided by the author

This isn’t the full output; however, take a look at the offset and limit fields. The offset field indicates where to start within the available data. When it is 0, we are starting at the top of the available dataset. On the other hand, limit only specifies the maximum number of rows to attempt to retrieve in the request (1000); it does not indicate how many rows were actually retrieved.

Let’s look at how many rows were actually retrieved:

len(aapl_options_json['data']) 
Image provided by the author

To retrieve the next set of option data, set the offset to 1000 to ensure the API returns data after the 1000th row.

params = {
"filter[underlying_symbol]": "AAPL",
"page[offset]": 1000
}

aapl_options_json = get_option_data(
API_KEY,
params
)

Lastly, with a maximum page limit of 1000 and a maximum offset of 10000, a single filtered query can return up to 11000 rows when paginated. Larger datasets need to be partitioned through filtering, which we will cover next. This may sound a bit complicated on the surface; however, let’s go through some practical ways to filter, sort, and retrieve results to get the most out of the API.

Filters, Sort, and Pages

Let’s do some basic sorting, starting with the expiration date. In the upcoming examples, I will limit the results to 5 observations for efficiency.

params = {
"filter[underlying_symbol]": "AAPL",
"page[limit]": 5,
"sort": "exp_date"
}

aapl_options_json = get_option_data(
API_KEY,
params
)
aapl_options_json['data']
Image provided by the author

As you can see, when sorting by expiration date, the default order is ascending. The expiration date of the first observation is 10–06–2023. This particular API pulls data referring to the last completed trading day. The first result is this contract, which expired in 2023. This shows that this endpoint can return expired contracts rather than limiting results to currently active options. You could circumvent this by changing the sorting order to descending, as below:

params = {
"filter[underlying_symbol]": "AAPL",
"page[limit]": 5,
"sort": "-exp_date"
}

That still would not remove the expired contracts from the query, though; they would just be the last observations in the dataset. Let’s add a filter to ensure we only retrieve options that expire on or after the current day:

from datetime import date

params = {
"filter[underlying_symbol]": "AAPL",
"page[limit]": 5,
"filter[exp_date_from]": date.today().isoformat(),
"sort": "exp_date"
}

aapl_options_json = get_option_data(
API_KEY,
params
)

aapl_options_json['data']
Image provided by the author

If you are just getting started with an API, I always find it useful to output the data in a more readable format as you get familiar with it. I wrote a basic function to convert JSON into a pandas DataFrame, which I find much more readable.

from datetime import date

params = {
"filter[underlying_symbol]": "AAPL",
"page[limit]": 5,
"filter[exp_date_from]": date.today().isoformat(),
"sort": "exp_date"
}

aapl_options_json = get_option_data(
API_KEY,
params
)

def options_json_to_df(data):
return pd.DataFrame([row["attributes"] for row in data])


aapl_options_df = options_json_to_df(
aapl_options_json["data"]
)

aapl_options_df.head()
Press enter or click to view image in full size
Image provided by the author

Now, let’s take a look at a robust set of filters. The following query will retrieve AAPL options with the following parameters:

  • calls
  • days to expiration between 7 and 30 calendar days from today.
  • strike prices from 300–400.
  • Sorted by strike prices in ascending order.
  • No offset, so the retrieval starts at the “top” of the dataset.
  • Return up to 100 results.
  • only retrieve the following fields: contract, underlying_symbol, type, exp_date, strike, moneyness, volume, open_interest, volatility, delta, theta, dte.
from datetime import date, timedelta

today = date.today()

params = {
# Which contracts qualify
"filter[underlying_symbol]": "AAPL",
"filter[type]": "call",
"filter[exp_date_from]": (today + timedelta(days=7)).isoformat(),
"filter[exp_date_to]": (today + timedelta(days=30)).isoformat(),
"filter[strike_from]": 300,
"filter[strike_to]": 400,

# How the results are returned
"sort": "strike",
"page[limit]": 100,
"page[offset]": 0,

# Only return fields we care about
"fields[options-contracts]": (
"contract,"
"underlying_symbol,"
"type,"
"exp_date,"
"strike,"
"moneyness,"
"volume,"
"open_interest,"
"volatility,"
"delta,"
"theta,"
"dte"
)
}

aapl_options_json = get_option_data(
API_KEY,
params
)

aapl_options_df = options_json_to_df(
aapl_options_json["data"]
)

aapl_options_df.head()
Press enter or click to view image in full size
Image provided by the author

Conclusion

Trading options can be daunting in itself, and retrieving the data you need to do so is no different. Luckily, US Options Data API by EODHD APIs proves to be an effective tool in this undertaking. My advice to any reader who wants to utilize it is to:

  • start small
  • utilize the examples in this article to get familiar with the endpoints
  • For trading, determine what type of options strategy suits you best and leverage the API strategically, and only pull the data you need

Be on the lookout for more articles on how to utilize this API and more!

If you enjoyed this story, please follow me here for more stories on AI, Data Science, Python, Cloud Computing, Machine Learning, Finance, & more

--

--

Jacob Ingle
Jacob Ingle

Written by Jacob Ingle

Lead Data Scientist | AWS Machine Learning Certified | Passion for Quantitative Finance