A portfolio tracker is only as good as its prices. The usual answer is a paid market-data API. For Investolio we wrote a small service that collects prices, dividends and exchange rates from free sources that need no keys, stores them in Postgres, and serves them to the app. The app never talks to those sources directly.
Every path has a fallback
None of these sources is official except the European Central Bank's, which supplies FX. The rest can change or block us without notice, so every kind of data has an order of attempts. Daily history tries CNBC first, then a yfinance sidecar, then Yahoo's chart endpoint, then Nasdaq for US tickers. Crypto uses CoinGecko, then Yahoo. Latest quotes for crypto come from CoinGecko and for everything else from CNBC in batches, and whatever CNBC did not answer falls through to Nasdaq or Yahoo.
Dividends come from two places and are merged. A US-focused source supplies pay and record dates and declared upcoming payments; yfinance supplies full global history. Sources disagree on ex-dates by a day, so a dividend within five days of one already stored is treated as the same payment and only fills in missing dates.
Being a polite client
Unofficial endpoints punish rudeness. Every request goes through one small client with a timeout, a minimum gap per host, and a cool-down: after a 429 from a host, nothing goes there for ten minutes. A fallback is only useful if the primary is not hammered into failing again.
A symbol is backfilled the first time any client asks for it, and concurrent requests for the same new symbol share one in-flight backfill. Symbols no source recognises are remembered for six hours so a typo does not trigger a fresh round of failures. Quotes refresh every fifteen minutes in market hours, history and dividends daily, and symbols nobody has asked for in ninety days stop being refreshed, though the data is kept.
Deriving splits from the data
The free sources do not give a clean list of stock splits, so the service finds them. CNBC provides two daily series: one adjusted for splits only, and one unadjusted. The ratio between them is flat until a split, then steps by exactly the split ratio on the day it takes effect. The code walks the series, notices a step of more than about fifteen percent, and snaps the ratio to the nearest simple fraction with a denominator of ten or less.
Small details matter more than they should. London prices arrive in pence and have to be divided by a hundred. A quote's previous close equals the last price after the market closes, so the daily move is derived from the reported change instead. Prices are stored split-adjusted and returned raw, because the app's own calculation engine applies splits itself.
A rating computed here
The dividend rating is not fetched from anyone. It is a score from zero to a hundred, mapped to A through F, computed from fundamentals: safety at 35 percent, growth 25, stability 20, profitability 15 and valuation 5. Each factor maps a few metrics through simple piecewise scales. Real estate companies are judged on a looser payout scale, and a company that pays no dividend scores an F with a plain explanation.
It is a heuristic, and the weights are our judgement, not a standard.
The trade we made
This costs nothing in data fees and gives us full control, and the price is fragility. To keep that visible, the service reports data freshness: quotes older than two hours during market hours, or prices and FX older than four days, count as stale. Failures alone do not raise an alarm, because every job has a fallback and rate limits are routine. Stale data does.