MCP Server For Financial Data by EODHD Learn more

Bulk Fundamentals API (via Extended Fundamentals Plan)

The Bulk Fundamentals API downloads fundamental data for many companies in a single request, instead of calling the single-ticker Fundamentals API symbol by symbol. It is built for keeping a local fundamentals database in sync. Two endpoints cover different needs: the bulk endpoint lets you select specific symbols, paginate, and choose CSV or JSON, while the full-exchange snapshot endpoint returns an entire exchange in one JSON response served from an hourly cache.

Access to the Bulk Fundamentals API requires the Extended Fundamentals subscription plan. Details of this plan are provided upon request — please contact support@eodhistoricaldata.com. The All-in-One package on its own does not include Extended Fundamentals.

Two Endpoints, Two Jobs

Both endpoints return the same fundamentals record format for the same universe of stocks. They differ in how you select and receive the data — pick the one that matches your workflow.

 Bulk endpointFull-exchange snapshot
Path/api/bulk-fundamentals/{EXCHANGE_CODE}/api/v2/bulk-fundamentals/{EXCHANGE_CODE}
ScopeWhole exchange, a paginated slice, or a chosen list of symbolsWhole exchange only
Pagination (offset, limit)Yes, up to 500 symbols per requestNo — the full exchange is returned at once
Symbol selection (symbols)YesNo
FormatJSON or CSVJSON only
Data sourceLiveHourly cache snapshot
When data is not readyAlways availableReturns 503 with a Retry-After header while the snapshot rebuilds
Best forSelective pulls, pagination, CSV exportPulling an entire exchange in one fast call
Cost100 API calls (or 100 + number of symbols)5,000 API calls

API Call Cost

  • A regular single-ticker Fundamentals request costs 10 API calls (for reference).
  • Bulk endpoint — 100 API calls per request when no symbols are specified.
  • Bulk endpoint with the symbols parameter — 100 + the number of symbols. For example, a request for 3 symbols costs 103 API calls.
  • Full-exchange snapshot endpoint — 5,000 API calls per request. A single call delivers the entire exchange, replacing the many paginated requests the bulk endpoint would need.

Coverage and Limits

  • Stocks only. Common stocks are covered; ETFs and mutual funds are not supported.
  • Exchange code in the path. Use the general US code or address these US exchanges separately: NASDAQ, NYSE (or NYSE MKT), BATS, and AMEX. All non-US exchanges are supported as usual — see the full list of supported exchanges and codes here.
  • Maximum 500 symbols per request on the bulk endpoint. Page through larger exchanges with offset and limit. The snapshot endpoint has no such limit — it returns the whole exchange in one response.
  • Reduced field set. Each bulk record carries fewer fields than a single-ticker Fundamentals response, and historical data is limited to the last 4 quarters and last 4 years.

Bulk Endpoint

GET /api/bulk-fundamentals/{EXCHANGE_CODE} returns fundamentals for the exchange named in the path. Use offset and limit to page through the exchange up to 500 symbols at a time, pass symbols to fetch a specific list, and choose JSON or CSV with fmt. This endpoint reads live data.

Path Parameter

EXCHANGE_CODE string required
Exchange code, for example US, NYSE, NASDAQ, AMEX, BATS, or any non-US exchange code.

Query Parameters

api_token string required
Your EODHD API token
fmt string optional
Output format. Set to json for JSON; the default is CSV. We strongly recommend JSON. (Default: csv)
offset integer optional
Starting position for pagination — the number of symbols to skip. (Default: 0)
limit integer optional
Number of symbols to return. The maximum is 500 per request. (Default: 500)
symbols string optional
Comma-separated list of tickers to fetch. When set, the exchange code in the path is ignored.
version string optional
Output template. Use 1.2 for the full current Fundamentals template, including Earnings Trends — this is the recommended value and what the snapshot endpoint uses. JSON only. When omitted, the API falls back to 1.0, a leaner legacy format kept for backward compatibility that omits some blocks such as Earnings Trends. (Default: 1.2 recommended)

Request Examples

All examples below use version 1.2 for the full current-template output. Fundamentals for an entire exchange, in JSON:

https://eodhd.com/api/bulk-fundamentals/NASDAQ?api_token={YOUR_API_TOKEN}&fmt=json&version=1.2
(Sign up for free to get an API token)
curl --location "https://eodhd.com/api/bulk-fundamentals/NASDAQ?api_token={YOUR_API_TOKEN}&fmt=json&version=1.2"
(Sign up for free to get an API token)
$curl = curl_init();

curl_setopt_array($curl, array(
    CURLOPT_URL => 'https://eodhd.com/api/bulk-fundamentals/NASDAQ?api_token={YOUR_API_TOKEN}&fmt=json&version=1.2',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_ENCODING => '',
    CURLOPT_MAXREDIRS => 10,
    CURLOPT_TIMEOUT => 0,
    CURLOPT_FOLLOWLOCATION => true,
    CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
    CURLOPT_CUSTOMREQUEST => 'GET',
));

$data = curl_exec($curl);
curl_close($curl);

try {
    $data = json_decode($data, true, 512, JSON_THROW_ON_ERROR);
    var_dump($data);
} catch (Exception $e) {
    echo 'Error. '.$e->getMessage();
}
(Sign up for free to get an API token)
import requests

url = f'https://eodhd.com/api/bulk-fundamentals/NASDAQ?api_token={YOUR_API_TOKEN}&fmt=json&version=1.2'
data = requests.get(url).json()

print(data)
(Sign up for free to get an API token)
library(httr)
library(jsonlite)

url <- 'https://eodhd.com/api/bulk-fundamentals/NASDAQ?api_token={YOUR_API_TOKEN}&fmt=json&version=1.2'
response <- GET(url)

if (http_type(response) == "application/json") {
    content <- content(response, "text", encoding = "UTF-8")
    cat(content)
} else {
    cat("Error while receiving data\n")
}
(Sign up for free to get an API token)
New to coding? Our ChatGPT assistant can generate code in any language tailored to our API. Simply describe how you want to use our data, and get a working piece of code. Don’t forget to replace the API token with your own.

Try it now (it's free)!

How to use it (YouTube)

Pagination — retrieve 200 symbols starting from symbol #1000:

https://eodhd.com/api/bulk-fundamentals/NASDAQ?offset=1000&limit=200&api_token={YOUR_API_TOKEN}&fmt=json&version=1.2
(Sign up for free to get an API token)
curl --location "https://eodhd.com/api/bulk-fundamentals/NASDAQ?offset=1000&limit=200&api_token={YOUR_API_TOKEN}&fmt=json&version=1.2"
(Sign up for free to get an API token)
$curl = curl_init();

curl_setopt_array($curl, array(
    CURLOPT_URL => 'https://eodhd.com/api/bulk-fundamentals/NASDAQ?offset=1000&limit=200&api_token={YOUR_API_TOKEN}&fmt=json&version=1.2',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_ENCODING => '',
    CURLOPT_MAXREDIRS => 10,
    CURLOPT_TIMEOUT => 0,
    CURLOPT_FOLLOWLOCATION => true,
    CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
    CURLOPT_CUSTOMREQUEST => 'GET',
));

$data = curl_exec($curl);
curl_close($curl);

try {
    $data = json_decode($data, true, 512, JSON_THROW_ON_ERROR);
    var_dump($data);
} catch (Exception $e) {
    echo 'Error. '.$e->getMessage();
}
(Sign up for free to get an API token)
import requests

url = f'https://eodhd.com/api/bulk-fundamentals/NASDAQ?offset=1000&limit=200&api_token={YOUR_API_TOKEN}&fmt=json&version=1.2'
data = requests.get(url).json()

print(data)
(Sign up for free to get an API token)
library(httr)
library(jsonlite)

url <- 'https://eodhd.com/api/bulk-fundamentals/NASDAQ?offset=1000&limit=200&api_token={YOUR_API_TOKEN}&fmt=json&version=1.2'
response <- GET(url)

if (http_type(response) == "application/json") {
    content <- content(response, "text", encoding = "UTF-8")
    cat(content)
} else {
    cat("Error while receiving data\n")
}
(Sign up for free to get an API token)
New to coding? Our ChatGPT assistant can generate code in any language tailored to our API. Simply describe how you want to use our data, and get a working piece of code. Don’t forget to replace the API token with your own.

Try it now (it's free)!

How to use it (YouTube)

A specific list of symbols (the exchange code in the path is ignored):

https://eodhd.com/api/bulk-fundamentals/NASDAQ?&symbols=AAPL.US,MSFT.US&api_token={YOUR_API_TOKEN}&fmt=json&version=1.2
(Sign up for free to get an API token)
curl --location "https://eodhd.com/api/bulk-fundamentals/NASDAQ?&symbols=AAPL.US,MSFT.US&api_token={YOUR_API_TOKEN}&fmt=json&version=1.2"
(Sign up for free to get an API token)
$curl = curl_init();

curl_setopt_array($curl, array(
    CURLOPT_URL => 'https://eodhd.com/api/bulk-fundamentals/NASDAQ?&symbols=AAPL.US,MSFT.US&api_token={YOUR_API_TOKEN}&fmt=json&version=1.2',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_ENCODING => '',
    CURLOPT_MAXREDIRS => 10,
    CURLOPT_TIMEOUT => 0,
    CURLOPT_FOLLOWLOCATION => true,
    CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
    CURLOPT_CUSTOMREQUEST => 'GET',
));

$data = curl_exec($curl);
curl_close($curl);

try {
    $data = json_decode($data, true, 512, JSON_THROW_ON_ERROR);
    var_dump($data);
} catch (Exception $e) {
    echo 'Error. '.$e->getMessage();
}
(Sign up for free to get an API token)
import requests

url = f'https://eodhd.com/api/bulk-fundamentals/NASDAQ?&symbols=AAPL.US,MSFT.US&api_token={YOUR_API_TOKEN}&fmt=json&version=1.2'
data = requests.get(url).json()

print(data)
(Sign up for free to get an API token)
library(httr)
library(jsonlite)

url <- 'https://eodhd.com/api/bulk-fundamentals/NASDAQ?&symbols=AAPL.US,MSFT.US&api_token={YOUR_API_TOKEN}&fmt=json&version=1.2'
response <- GET(url)

if (http_type(response) == "application/json") {
    content <- content(response, "text", encoding = "UTF-8")
    cat(content)
} else {
    cat("Error while receiving data\n")
}
(Sign up for free to get an API token)
New to coding? Our ChatGPT assistant can generate code in any language tailored to our API. Simply describe how you want to use our data, and get a working piece of code. Don’t forget to replace the API token with your own.

Try it now (it's free)!

How to use it (YouTube)

Full-Exchange Snapshot Endpoint

GET /api/v2/bulk-fundamentals/{EXCHANGE_CODE} returns every symbol on the exchange in one JSON response, served from a snapshot that is rebuilt hourly in the background. It takes no offset, limit, or symbols parameters — those are ignored — and always returns the full exchange. This is the fastest way to pull a complete exchange in a single call and avoids the many paginated requests the bulk endpoint would need.

Path Parameter

EXCHANGE_CODE string required
Exchange code, for example NYSE, NASDAQ, AMEX, TO, or V.

Query Parameters

api_token string required
Your EODHD API token. No other parameters are used — the full exchange is always returned.

When the hourly snapshot is being rebuilt, the endpoint returns 503 Service Unavailable with a Retry-After header giving the number of seconds to wait. Back off for that interval and retry, rather than treating it as a hard error. The response can be large — stream it instead of buffering the whole payload in memory.

Request Example

https://eodhd.com/api/v2/bulk-fundamentals/NASDAQ?api_token={YOUR_API_TOKEN}
(Sign up for free to get an API token)
curl --location "https://eodhd.com/api/v2/bulk-fundamentals/NASDAQ?api_token={YOUR_API_TOKEN}&fmt=json"
(Sign up for free to get an API token)
$curl = curl_init();

curl_setopt_array($curl, array(
    CURLOPT_URL => 'https://eodhd.com/api/v2/bulk-fundamentals/NASDAQ?api_token={YOUR_API_TOKEN}&fmt=json',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_ENCODING => '',
    CURLOPT_MAXREDIRS => 10,
    CURLOPT_TIMEOUT => 0,
    CURLOPT_FOLLOWLOCATION => true,
    CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
    CURLOPT_CUSTOMREQUEST => 'GET',
));

$data = curl_exec($curl);
curl_close($curl);

try {
    $data = json_decode($data, true, 512, JSON_THROW_ON_ERROR);
    var_dump($data);
} catch (Exception $e) {
    echo 'Error. '.$e->getMessage();
}
(Sign up for free to get an API token)
import requests

url = f'https://eodhd.com/api/v2/bulk-fundamentals/NASDAQ?api_token={YOUR_API_TOKEN}&fmt=json'
data = requests.get(url).json()

print(data)
(Sign up for free to get an API token)
library(httr)
library(jsonlite)

url <- 'https://eodhd.com/api/v2/bulk-fundamentals/NASDAQ?api_token={YOUR_API_TOKEN}&fmt=json'
response <- GET(url)

if (http_type(response) == "application/json") {
    content <- content(response, "text", encoding = "UTF-8")
    cat(content)
} else {
    cat("Error while receiving data\n")
}
(Sign up for free to get an API token)
New to coding? Our ChatGPT assistant can generate code in any language tailored to our API. Simply describe how you want to use our data, and get a working piece of code. Don’t forget to replace the API token with your own.

Try it now (it's free)!

How to use it (YouTube)

Sign up & Get Data

Output

Both endpoints return a JSON array, one object per company. Each object groups fundamentals into the blocks below. The set mirrors the single-ticker Fundamentals API with fewer fields, and the Earnings and Financials blocks are capped at the last 4 quarters and last 4 years. Here is an example CSV output for Apple Inc. (AAPL.US) and Microsoft Corporation (MSFT.US).

Use version 1.2 on the bulk endpoint for the full current-template output (Earnings Trends included) — it is a superset of the older format and matches what the snapshot endpoint returns. Version 1.2 is JSON only, and the 4 quarters / 4 years history limit still applies. Note on version 1.0: when version is omitted the API falls back to the original 1.0 format, built on an earlier fundamentals template — it is leaner and omits some blocks such as Earnings Trends, and is kept only for backward compatibility with existing integrations.

[
  {
    "General":        { "Code": "AAPL", "Type": "Common Stock", "Name": "Apple Inc.", "Exchange": "NASDAQ", "ISIN": "US0378331005", "Sector": "Technology", "...": "..." },
    "Highlights":     { "MarketCapitalization": 3200000000000, "PERatio": 32.1, "DividendYield": 0.0045, "EarningsShare": 6.61, "...": "..." },
    "Valuation":      { "TrailingPE": 32.1, "ForwardPE": 28.4, "PriceSalesTTM": 8.2, "PriceBookMRQ": 51.3, "...": "..." },
    "SharesStats":    { "SharesOutstanding": 14900000000, "SharesFloat": 14880000000, "...": "..." },
    "Technicals":     { "Beta": 1.24, "52WeekHigh": 260.1, "52WeekLow": 164.1, "50DayMA": 228.4, "...": "..." },
    "SplitsDividends":{ "ForwardAnnualDividendRate": 1.04, "PayoutRatio": 0.15, "ExDividendDate": "2026-05-12", "...": "..." },
    "AnalystRatings": { "Rating": 4.2, "TargetPrice": 275.0, "Buy": 21, "Hold": 8, "...": "..." },
    "Earnings":       { "Trend": { "...": "..." }, "Last_0": { "...": "..." }, "Last_1": { "...": "..." } },
    "Financials":     { "Balance_Sheet": { "...": "..." }, "Cash_Flow": { "...": "..." }, "Income_Statement": { "...": "..." } }
  }
]
BlockContents
GeneralIdentification and classification: Code, Name, Exchange, currency, country, ISIN, CUSIP, CIK, sector and industry, GIC classification, IPO date, delisting flag, description
HighlightsHeadline metrics: market capitalization, EBITDA, PE and PEG ratios, book value, dividend share and yield, EPS and EPS estimates, margins, and returns
ValuationValuation multiples: trailing and forward PE, price-to-sales, price-to-book, enterprise value and its ratios
SharesStatsShare statistics: shares outstanding and float, insider and institutional ownership, short interest
TechnicalsTechnical figures: beta, 52-week high and low, 50- and 200-day moving averages, short-interest figures
SplitsDividendsForward dividend rate and yield, payout ratio, dividend and ex-dividend dates, last split factor and date
AnalystRatingsConsensus rating, target price, and the count of strong buy, buy, hold, sell, and strong sell ratings
EarningsEarnings trend plus the last 4 reported quarters (Last_0 to Last_3)
FinancialsBalance sheet, cash flow, and income statement, limited to the last 4 quarters and last 4 years

Response Codes

CodeMeaning
200Success; fundamentals returned
401Unauthorized; missing or invalid API token
403Plan does not include Extended Fundamentals access
422Invalid query parameters
503Snapshot endpoint only; the hourly snapshot is being rebuilt. Wait the number of seconds in the Retry-After header and retry
Compare plans and find your fit
Free and paid plans for individual and commercial use
Go to Pricing
Chat