# Why multiple data providers No single free data source does everything well. zfin aggregates several, using each for what it's best at, and falls back gracefully when one is unavailable. This page explains the design; for signup links, limits, and the full table, see [Data providers and API keys](../reference/providers.md). ## One source per job Each data type has a primary provider chosen for coverage and quality: | Data | Primary | Notes | |-------------------------------|----------------------|-----------------------------------------------| | Daily candles | Tiingo | Deep history; stocks, ETFs, mutual funds | | Real-time quotes | Yahoo | No key required | | Dividends / splits | Polygon | Carries forward-looking declared events | | Options chains | CBOE | No key; 15-minute delayed | | Earnings | FMP | Actuals + analyst estimates | | ETF profiles / classification | SEC EDGAR + Wikidata | Authoritative holdings; needs a contact email | The [data service](concepts.md#the-data-service-and-caching) hides this behind one interface -- commands ask for "candles for VTI," not "call Tiingo." That's also what makes the cache and rate limiting uniform across providers. ## Fallback, not single-point-of-failure Where a second source can stand in, zfin uses it: - **Candles:** Tiingo is primary; **Yahoo** is the fallback if Tiingo is unavailable or lacks the symbol. - **Quotes:** Yahoo is primary; **TwelveData** is the fallback. - **Dividends/splits:** Polygon is primary, but Tiingo's price-series response carries per-row dividend and split data that zfin merges in to rescue events Polygon's reference endpoints occasionally miss. How a failure propagates depends on its kind. **Transient** errors (server 5xx, connection drops) stop a refresh so you don't get a half-updated view. **Permanent** errors (not-found, parse failures) fall through to the next provider, and a rate-limit hit triggers a single backoff-and-retry. ## Why some data needs a key and some doesn't Quotes (Yahoo) and options (CBOE) are unauthenticated. The rest want a free API key, and SEC EDGAR wants a contact email in its `User-Agent` (set via `ZFIN_USER_EMAIL`) rather than a key. None are individually required -- missing a key just removes that one data type, and everything else keeps working. See [which key unlocks what](../reference/config/environment.md#api-keys). ## A few deliberate quirks - **Yahoo is unofficial.** Yahoo Finance has no public API; zfin reads an undocumented endpoint that needs no key but carries no guarantees -- it can change shape or stop responding at any time. Quotes (and the candle fallback) ride on it, so keeping `TIINGO_API_KEY` set for candles and `TWELVEDATA_API_KEY` for quote fallback cushions a Yahoo outage. - **TwelveData is no longer used for candles.** Its split-adjusted closes proved unreliable for the return math, so it's quote-fallback only now. Yahoo is the candle fallback. - **Mutual-fund and dual-class coverage varies.** Some symbols (ETFs, CUSIPs, a few dual-class shares like BRK.B) have no earnings on the free FMP tier and show "no earnings data" -- a documented limitation, not a bug. See the [FAQ](faq-troubleshooting.md). ## See also - [Data providers and API keys](../reference/providers.md) -- the reference table. - [Caching and data freshness](caching.md) -- how fetched data is reused.