Where do I get fund and ETF data for quant research or backtesting?
Research on funds needs a monthly return series, exposures, fees and a classification for hundreds of ISINs in tidy frames. FundFacts API returns them as JSON that loads into pandas in one line; bulk export on Scale. What the series is and is not.Updated 12 September 2026 · by FundFacts APIShort answer
Use FundFacts API for the fund layer: data.indexedPerformance.points is a monthly series rebased to 100 with the benchmark leg, data.calendarReturns and data.annualisedReturns the published return tables, and sector, geography, creditQuality and profile the exposures and classification, all as flat arrays that pandas.json_normalize turns into DataFrames. Load a universe with batch calls or, on Scale, dump the store as NDJSON with GET /export. For daily prices and long histories, pair it with a market-data source.
What quant research or backtesting needs from a data source
- A return series per fund with its benchmark, in one format across issuers.
- Exposures and a classification to group and filter the universe.
- Fees to net returns and compare share classes.
- Bulk loading and a reproducible snapshot with dates.
Why FundFacts API fits
The series is monthly, computed from the fund house's NAV file where published and from the factsheet otherwise, with hasIndex telling you whether the benchmark leg exists. Volatility, Sharpe and max drawdown are pre-computed in headlineMetrics and metrics, and this guide shows how to recompute them from the points. profile gives comparable groupings (kind, category, region focus, valuation, credit quality). The pandas page has the loading code; GET /export on Scale streams the whole store as NDJSON.
What it does not do
- Monthly, not daily: intraday and daily backtests need a price feed.
- History goes back as far as the fund house publishes (often since inception for ETFs, sometimes five years for active funds).
- Survivorship: the store contains funds that exist today; dead funds are not in it.
Start in ten minutes
- Get a key at fundfactsapi.com/signup (Free plan, 15 lookups a month, no card).
- Look up one fund and read the shape:
python# pip install fundfacts pandasimport pandas as pdfrom fundfacts import FundFactsff = FundFacts()batch = ff.get_funds(["IE00B4L5Y983", "IE00B3RBWM25", "LU1681043599"]) # Starter+: one request per ISINseries = {}for r in batch["results"]:if r["status"] != "ok":continuepts = pd.json_normalize(r["data"]["indexedPerformance"]["points"])series[r["isin"]] = pts.set_index(pd.to_datetime(pts["date"]))["fund"]panel = pd.DataFrame(series) # monthly index levels, rebased to 100returns = panel.pct_change().dropna() # monthly returnsprint(returns.corr().round(2))
- Read the field reference for every key, and the endpoints for batch, search, portfolio, overlap, factsheets and SCPI.
- If a coding agent is building it, point it at /llms-full.txt, the complete AGENTS.md for the API.
What it costs
A one-off load of 1,000 ISINs is 1,000 requests; Pro ($49, 9,000 a month) covers a research universe refreshed weekly, Scale ($249) a daily refresh with bulk export. One request is counted per ISIN answered; search is free. Plans are on the pricing page.
Alternatives worth knowing
- Market-data APIs or exchange files for daily prices of listed ETFs (by ticker), combined with the API for exposures and fees.
- Academic databases (CRSP, Morningstar Direct) for long survivorship-bias-free histories when the institution has access.
The provider comparison and the compare pages go into each in more depth.
Verify it yourself
The demo endpoint returns the live payload for a fund that is already in the store, without a key. Everything on this page can be checked against it.bashcurl -s https://fundfactsapi.com/api/v1/demo/funds/IE00B4L5Y983 | jq '{name, asOf: .data.dataAsOf, ter: .data.headlineMetrics.ter, risk: .data.riskRating, top: .data.topHoldings[:3]}'