data_updater_v2.data_clients.eodhd
EODHDClient — EOD Historical Data: the raw calls (fetch_*) and, on the same class, the
processed verbs the DataClient combines with FMP’s — FMPClient’s section names and shapes:
2 · UNIVERSE listings() — the exchange's ticker list, each line read for what it IS · ids() — the id-mapping endpoint, every listing's ISIN, FIGI, LEI, CUSIP, CIK4 · PRICES daily() / intraday() per symbol · split_factors() · daily_bulk() — one exchange's day, every ticker5 · CORPORATE ACTIONS splits() / dividends() per symbol · splits_calendar() / dividends_calendar() from the daily bulk files · symbol_changes()Base https://eodhd.com/api/; the key is the api_token QUERY parameter (EODHD takes
no header; common-http redacts it from logs); fmt=json on every route (CSV is the
default and drops fields). Every fact below was probed live on our plan (2026-10-05,
scratchpad fmpu/eod1-eod4) or is quoted from EODHD’s Bulk API page (saved as
Data V2/Bulk API_ Whole-Exchange EOD, Splits and Dividends.pdf), as marked.
Routes and what they serve:
technical/{TICKER} function=splitadjusted: the daily series adjusted for splits
only (5 calls): raw close / its close = the day’s split factor
eod/{TICKER} one ticker’s daily history in one call, no row cap: open / high /
low / close RAW (TQQQ 2025-11-18 close 98.36), adjusted_close
split- and dividend-adjusted, and volume SPLIT-ADJUSTED as of
today (TQQQ 2025-11-18: 267,348,000 = 2 x the raw 133.7M; AAPL
2019-01-02: 4 x). No row for the day still trading.
intraday/{TICKER} 1m / 5m / 1h bars, from / to in UNIX seconds, at most 120 /
600 / 7,200 days a call (422 beyond, probed at each wall); stamps
in UTC epoch at the bar’s start. Listings: RAW prices and volume
(TQQQ before its split: 71.62 = raw), pre- and post-market
included (04:00-20:00 ET; FMP serves 09:30-15:59 only). Depth:
1m from ~2016 (SPY) / 2008 (AAPL), 5m and 1h from ~2022, GSPC 1m
from 2020, BTC 1m from 2016-18. Crypto minute volume does not
reconcile with EODHD’s own daily (sum x price $63.5B vs $46.5B on
2026-10-02) nor with coins: its unit is unknown.
div/{TICKER} dividend history: value split-ADJUSTED, unadjustedValue the
cash as paid (AAPL 2019-02-08: 0.1825 vs 0.73), period
(Quarterly, Semiannual, Monthly, Annual, Other, or null —
FMP says Semi-Annual), declaration / record / payment dates.
Amounts are rounded coarser than FMP’s (TQQQ 0.275 vs 0.27541).
splits/{TICKER} split history: split = “NEW/OLD” (2-for-1 “2.000000/1.000000”,
1-for-8 “1.000000/8.000000”). Announced future splits appear
(DXJ 3:1 on 2026-10-09), not always (SOXX 11-05 on FMP only).
eod-bulk-last-day/{EX} one exchange, one day, every ticker: type eod / splits /
dividends, a FLAT 100 calls each (doc). US = the composite
(~50k rows: listings, OTC, mutual funds); NYSE / NASDAQ / AMEX /
BATS = that venue only (5,886 / 5,258 / 282 / 1,555 on 10-02).
INDX (1,547) and CC (1,847) are their own exchanges. The doc
says a past non-trading day answers the previous session and a
future day the latest one; probed, both answered [] — every row’s
date is checked against the ask either way. Same-day volume is
raw; an older day’s volume is split-adjusted once a split has
happened since (TQQQ 2025-11-18 bulk: 267.3M). Dividends and splits
for a FUTURE day list what is announced (10-06: 28 dividends, 3
splits) — never an undeclared ETF amount (BIL, SPY absent).
exchange-symbol-list/{EX} the exchange’s ticker list (US 50,950 on 2026-10-18: Code, Name,
Country, Exchange, Currency, Type, Isin; 13,298 of them on the
listed venues — NASDAQ 5,479, NYSE 3,417, NYSE ARCA 2,501, BATS
1,574, AMEX 292 — the rest NMFQS fund classes and OTC); with
delisted=1 the tickers that have left (US 60,433), same fields,
no delisting date.
fundamentals/{TICKER} one JSON document: General (profile fields), ETF_Data, SharesStats,
outstandingShares, SplitsDividends … 10 calls. NOT on our plan
(HTTP 403, probed 2026-10-18).
id-mapping every listing’s ids, one exchange (filter[ex]) or one ticker
(filter[symbol]): symbol, isin, figi (EODHD’s; on 159 of our
listings an OLD one a reorganisation replaced: RKLB, SIRI, the DFA
ETFs — OpenFIGI has the current), lei, cusip, cik. US 110,174 rows
(73,116 with a FIGI), INDX 359 (103), CC none. 1,000 a page.
symbol-change-history every US ticker rename from from on: exchange (US), old_symbol,
new_symbol, company_name, effective (the first day under the new
ticker). Without from it answers the last year only (323 on
2026-10-09); with from 2000-01-01, 1,224 from 2021-04-05 — its
floor. Same day as FMP’s on 1,213 of the 1,215 renames both tell.
Spelling: listings as FMP (BRK-B) plus .US; indices GSPC.INDX (FMP ^GSPC) —
NOT one to one: of FMP’s 302 US indices priced on 10-02, 72 have the same code and close on
EODHD, 227 have no EODHD code, and ^TNX / ^TYX / ^FVX are TEN times larger on EODHD (CBOE’s
index points: TNX 52.77 vs FMP’s yield 5.28); crypto BTC-USD.CC (FMP BTCUSD),
daily volume in DOLLARS (identical to FMP’s composite: BTC 2026-09-28 42,589,028,679).
Index sentinel: closed days carry 999999.9999 prices on some indices (MERV: 693 rows since
2015; none on GSPC, NDX, VIX, TNX).
Limits: 1,000 calls a minute and 100,000 a day on our plan; bulk 100 calls, intraday 5 (measured on the account counter), everything else 1.
Classes
EODHDClient
Bases: BaseClient
EOD Historical Data: the raw calls and the processed verbs. Carried state: the base’s only.
daily
daily(symbols: str | Symbols, start: dt.date | None = None, end: dt.date | None = None, asset: Asset) -> RawResponse | dict[str, RawResponse]Daily bars, one symbol or many (our spelling or EODHD’s), oldest first, as traded
(unadjusted), finished days only (bars.settled). start None = the vendor’s
first day; end None = today. asset picks the spelling and the handling (the
table above). Listings’ volume as served — divided by every split since the day
(TQQQ 2025-11-18: 267.3M for the 133.7M traded); back to shares as traded with the
splits table.
daily_bulk
daily_bulk(day: dt.date, exchange: str = 'US') -> RawResponseOne EODHD exchange’s daily bars for ONE day, every ticker, in the common shape:
US (the composite: listings, OTC, mutual funds — filter to the universe in the
flow), a venue (NYSE / NASDAQ / AMEX / BATS), INDX (symbols as EODHD codes:
GSPC, TNX — not FMP’s) or CC (crypto, symbols in FMP’s spelling: BTC-USD ->
BTCUSD). 100 calls whatever the size. Rows dated other than day are not that
day’s and are left out (an empty list means not published, or no session).
Volume is shares as traded for the latest day only: for an older day EODHD has
divided it by any split since (un-adjust with the splits table).
dividends
dividends(symbols: str | Symbols, start: dt.date | None = None, end: dt.date | None = None) -> RawResponse | dict[str, RawResponse]US listings’ dividend histories (EODHD), oldest first, in FMP’s shape: symbol,
ex_date, period (as EODHD words it), amount (unadjustedValue: cash as paid),
declaration / record / payment dates. start / end bound the ex-dates.
dividends_calendar
dividends_calendar(start: dt.date, end: dt.date) -> RawResponseEvery US dividend with its ex-date in [start, end] from EODHD’s daily bulk files, one call (100) per calendar day; a future day lists the announced ones — never an undeclared ETF amount (BIL’s appears on its ex-date).
fetch_bulk
fetch_bulk(exchange: str, kind: str, day: str) -> RawResponseOne exchange’s file for one day (kind eod / splits / dividends), as served.
fetch_dividends
fetch_dividends(ticker: str, start: str | None = None, end: str | None = None) -> RawResponseOne ticker’s dividends as served (value, unadjustedValue, period, dates).
fetch_eod
fetch_eod(ticker: str, start: str | None, end: str | None) -> RawResponseOne ticker’s daily rows as served (date, OHLC raw, adjusted_close, volume
split-adjusted), oldest first; start / end ISO dates, None = all.
fetch_fundamentals
fetch_fundamentals(ticker: str, filter: str | None = None) -> RawResponseOne ticker’s fundamentals document as served (General: Code, Type, Name,
Exchange, ISIN, CUSIP, CIK, IPODate, Sector, Industry, Description; ETF_Data;
SharesStats; outstandingShares; SplitsDividends …), or one block of it
(filter: “General”, “General::Sector”). 10 calls. NOT on our plan today: a 403
comes back as a failed answer (probed 2026-10-18, AAPL.US and SPY.US); declared so the
day the plan carries it the profile for EODHD-only names is one call away.
fetch_id_mapping
fetch_id_mapping(exchange: str | None = None, symbol: str | None = None) -> RawResponseEODHD’s ids for one exchange’s listings (every page, fetched at once) or one
ticker (SPY.US), as served: symbol, isin, figi, lei, cusip, cik.
fetch_intraday
fetch_intraday(ticker: str, interval: str, start: int, end: int) -> RawResponseOne ticker’s bars of one EODHD width (1m / 5m / 1h) whose start lies in [start, end] (UNIX seconds), as served: timestamp, gmtoffset, datetime, OHLCV.
fetch_split_adjusted
fetch_split_adjusted(ticker: str, start: str | None, end: str | None) -> RawResponseOne ticker’s daily OHLCV adjusted for SPLITS ONLY (the Technical API’s
function=splitadjusted), oldest first, whole history in one request (5 calls).
Its close against eod’s raw close is the split factor of each day, the one thing
that puts eod’s split-adjusted volume back to shares as traded (checked against
FMP’s raw volume: AAPL 2020, TQQQ 2025, NVDA 2024, XLE 2018 exact).
fetch_splits
fetch_splits(ticker: str, start: str | None = None, end: str | None = None) -> RawResponseOne ticker’s splits as served (date, split “NEW/OLD”).
fetch_symbol_changes
fetch_symbol_changes(start: str, end: str | None = None) -> RawResponseEvery US rename effective in [start, end] as served (start an ISO day: without
one EODHD answers the last year only).
fetch_symbols
fetch_symbols(exchange: str, delisted: bool = False) -> RawResponseOne exchange’s ticker list (Code, Name, Country, Exchange, Currency, Type, Isin):
today’s listings, or with delisted the tickers that have left it (US: 60,433 on
2026-10-18 — 22,249 of them NMFQS mutual-fund share classes, 11,519 NASDAQ, 7,052 NYSE,
9,829 PINK; no delisting date on the row: the last bar, FMP’s list or the SEC’s Form
25 date it).
ids
ids(exchange: str | None = None, symbols: Iterable[str] | None = None) -> dict[str, dict[str, Any]]EODHD’s ids for its listings: one exchange’s every listing (exchange: US, INDX;
111 pages for US, fetched at once, ~5 minutes), or only symbols (EODHD’s tickers,
SPY.US; one call each). Its ticker -> {symbol, isin, figi, lei, cusip, cik}, each
listing once (the same row served twice is one). EODHD’s FIGI can be one a
reorganisation replaced (RKLB, SIRI): OpenFIGI’s is the current. A failed or cut
answer raises FetchError (ids are a catalog: no holes).
intraday
intraday(symbols: str | Symbols, start: dt.date | None = None, end: dt.date | None = None, interval: str = '1min', asset: Asset) -> RawResponse | dict[str, RawResponse]Intraday bars of the family’s days [start, end] (UTC days for crypto, ET session
days for listings), one symbol or many, oldest first, in the common shape.
start None = INTRADAY_FLOOR; end None = today. 1min / 5min / 1hour only
(INTERVALS). Listings RAW with pre- and post-market bars (04:00-20:00 ET, as served
— FMP serves the regular session only); crypto minute volume is coins (probed
2026-10-07): dollars = coins x the bar’s typical price.
listings
listings(exchange: str = 'US', delisted: bool = False) -> list[dict[str, Any]]EODHD’s ticker list for one exchange in one call, as served: Code (our spelling for
US listings: BRK-B), Name, Country, Exchange, Currency, Type, Isin (US: 50,984 on
2026-10-05); with delisted the tickers that have left it instead (US: 60,433 on
2026-10-18, no delisting date). Type is a second opinion only — wrong both ways in a 615-name sample
(6 of 250 ETFs “Common Stock”; OZ, a common stock, “Unit”; DPU “ETF”; GJP, a STRATS
trust, “Common Stock”) — and Isin can lag a reverse split (8 of 246 equities kept
the old one). Each row also says what it IS as nc-feeds reads an EODHD line
(papers.eodhd_kind: the spelling, the Type with the venue and the name):
asset_class, security_type, and refused (None, or why the line is not a
listing: a fund class code, a when-issued or test line). A failed or empty list
raises FetchError (a catalog: no holes).
split_factors
split_factors(symbols: str | Symbols, start: dt.date | None = None, end: dt.date | None = None) -> dict[str, dict[dt.date, float]]For US listings (EODHD’s spelling or ours), each day’s split factor since: EODHD’s raw
close / its split-only adjusted close (eod and the Technical API, 1 + 5 calls a
symbol). EODHD’s daily volume divided by it is the volume as traded. A symbol whose
calls fail has no factors (its EODHD volume cannot be put back: the caller drops it).
splits
splits(symbols: str | Symbols, start: dt.date | None = None, end: dt.date | None = None) -> RawResponse | dict[str, RawResponse]US listings’ split histories (EODHD), oldest first, in FMP’s shape: symbol, date,
split_to, split_from, split_type (None: EODHD does not say). Announced future splits
appear when EODHD has them. start / end bound the dates. Processed: a row
with no factor or the same split twice taken out, named (actions.sound_splits).
splits_calendar
splits_calendar(start: dt.date, end: dt.date) -> RawResponseEvery US split dated in [start, end] from EODHD’s daily bulk files, one call (100)
per calendar day; a future day lists the announced ones. Processed as splits.
symbol_changes
symbol_changes(since: dt.date | None = None) -> RawResponseEvery US rename EODHD tells effective from since on (None: as far back as it
reaches, 2021-04-05), oldest first, in the common shape (changes.sound_changes:
old_symbol / new_symbol in our spelling, date, name), the lines that are no rename
taken out, named. A failed answer comes back as it is.
Functions
eodhd_ticker
eodhd_ticker(symbol: str, asset: Asset) -> strOur symbol (FMP’s spelling) in EODHD’s: SPY -> SPY.US, ^GSPC ->
GSPC.INDX, BTCUSD -> BTC-USD.CC. A symbol already carrying an EODHD suffix
passes as given. Not every FMP index exists under the same code on EODHD (72 of 302
did on 2026-10-02), and yields differ in scale (EODHD TNX = 10 x FMP ^TNX).