Where do I get data for an ETF or fund comparison website?
A comparison site needs the same fields for thousands of share classes, page-ready, with an as-of date and a licence for public display. FundFacts API delivers one JSON factsheet per ISIN; here is how sites build on it, what it costs and where it stops.Updated 12 September 2026 · by FundFacts APIShort answer
Use FundFacts API. One GET per ISIN returns the page-ready fields a comparison site shows (name, category, TER and KID costs, risk indicator, size, inception, distribution policy, top holdings, sector and country tables, calendar and annualised returns, a growth series) in the same shape for every fund house, with dataAsOf for the footer. Load the universe once with batch calls, cache until expiresAt, and render from your own database; Pro ($49) includes the commercial licence for public display and GET /overlap for "how similar are these two" pages.
What a fund comparison website needs from a data source
- Thousands of share classes with identical fields so that filters and tables work across issuers.
- Content per page: objective, holdings, exposures, returns, costs, all with a date.
- Pairwise comparisons and overlap for "X vs Y" pages.
- A refresh strategy that keeps requests proportional to the universe, not to traffic.
- A licence that allows public commercial display.
Why FundFacts API fits
The site is a read model over the API: POST /funds loads batches of 50 (Pro) or 200 (Scale), the payloads land in your database with expiresAt, and pages render from there, so traffic never touches the API. profile.category, profile.riskBand and profile.regionFocus are the filter facets; headlineMetrics.ter, keyFacts.aum and keyFacts.inception the sort keys; calendarReturns and indexedPerformance.points the charts. GET /overlap?isins=A,B powers "X vs Y" pages with a shared-holdings table. The ETF comparison website use case covers the page structure and the disclaimers.
What it does not do
- No intraday or daily prices; show the monthly series and link to the exchange for quotes.
- No ratings or editorial: the site's own methodology does the ranking, and the data stays factual.
- Coverage follows the fund houses' documents; a niche issuer may return only key facts and the KID. The coverage pages say which.
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 (or call https://fundfactsapi.com/api/v1/funds/{isin} with any HTTP client)from fundfacts import FundFactsff = FundFacts() # reads FUNDFACTS_API_KEY; free key at https://fundfactsapi.com/signupfund = ff.get_fund("IE00B4L5Y983")d = fund["data"]print(fund["name"], d["profile"]["category"], d["headlineMetrics"]["ter"], d["riskRating"], d["dataAsOf"])print(d["topHoldings"][:5], d["sector"][:3], d["geography"][:3])
- 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 2,000-share-class universe refreshed daily is about 60,000 requests a month: Scale ($249), which also brings the change feed so you refresh only what moved. A 500-fund site fits Pro ($49) with overage. Page views cost nothing. One request is counted per ISIN answered; search is free. Plans are on the pricing page.
Alternatives worth knowing
- Licensing a full vendor database (Morningstar, LSEG) when you need ratings, analyst text and thirty years of history and can carry the licence.
- Scraping fund-house sites yourself when fund data is your core product and you want to own the pipeline; see the comparison with building your own parser.
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]}'