ETF holdings API

ETF holdings API: holdings, sector and country weights by ISIN as JSON

FundFacts API returns the holdings of an ETF or fund from its ISIN as JSON: the largest positions with their weights, the number of holdings, and the sector, country, region and asset-class weights, with the as-of date of the fund house's documents. One GET request with an API key; the Free plan gives 15 lookups a month without a card.Published October 6, 2026
FundFacts API turns any fund or ETF ISIN into one dated, structured factsheet (fees and KID costs, the 1–7 risk indicator, holdings, sector and country exposure, returns) read from the fund house's own documents: as JSON for your code, inside ChatGPT and Claude through its MCP server, or as a client-ready PDF factsheet.

What the holdings block contains

Every fund payload carries the same keys; one that does not apply to the fund is an empty array or null, never missing.

FieldShapeWhat it holds
data.topHoldings[{ name, weight }]Largest positions in the fund house's order, weight in percent.
data.holdingsBasis"portfolio", "substituteBasket" or nullThe fund's own positions, or a synthetic ETF's swap collateral.
data.keyFacts.holdingsnumberNumber of positions in the portfolio.
data.sector[{ label, weight }]Sector weights (GICS-style for equity funds).
data.geography, data.region[{ label, weight }]Country and regional weights.
data.assetAllocation[{ label, weight }]Equity, bond and cash split.
data.creditQuality, data.maturity[{ label, weight }]Rating and maturity buckets for bond funds.
data.profile.concentrationstringFrom the weight of the ten largest positions.
data.dataAsOfdateAs-of date of the figures.

The same payload carries fees, the risk indicator and returns: see the ETF data API page.

A request and a trimmed response

bash
curl -s https://fundfactsapi.com/api/v1/funds/IE00B4L5Y983 \
-H "Authorization: Bearer $FUNDFACTS_API_KEY"
json
{
"isin": "IE00B4L5Y983",
"name": "iShares Core MSCI World UCITS ETF",
"cached": true,
"generatedAt": "2026-09-02T20:32:58.872Z",
"expiresAt": "2026-09-03T20:32:58.872Z",
"data": {
"keyFacts": {
"holdings": 1253
},
"topHoldings": [
{ "name": "NVIDIA", "weight": 5.48 },
{ "name": "APPLE", "weight": 5.24 },
{ "name": "MICROSOFT", "weight": 3.88 }
],
"sector": [
{ "label": "Information Technology", "weight": 29.8 },
{ "label": "Financials", "weight": 16.46 },
{ "label": "Industrials", "weight": 11.03 }
],
"geography": [
{ "label": "United States", "weight": 72.07 },
{ "label": "Japan", "weight": 5.85 },
{ "label": "United Kingdom", "weight": 3.49 }
],
"assetAllocation": [
{ "label": "Equity", "weight": 99.75 },
{ "label": "Cash Collateral and Margins", "weight": 0.02 }
],
"profile": {
"concentration": "diversified"
},
"dataAsOf": "2026-09-01"
}
}

This is the real payload for iShares Core MSCI World UCITS ETF, trimmed to three rows per array. Its topHoldings lists the ten largest of 1,253 positions, which together weigh 26.7% of the fund. To check the shape without a key, the demo endpoint returns the same envelope for any fund already loaded (30 requests a minute per IP, not for production):

bash
curl -s https://fundfactsapi.com/api/v1/demo/funds/IE00B4L5Y983

Top ten or longer list: what each fund house publishes

topHoldings is what the fund house discloses. It is never estimated.

  • Most funds: the top ten. The list comes from the factsheet's top-ten table, or from the ten heaviest lines of the fund house's holdings file where it publishes one (iShares does).
  • Longer lists from a few fund houses. Where the fund house's data lists more positions, up to 50 are kept (Amundi, Franklin Templeton, J.P. Morgan and UBS among them).
  • Names without weights. Some fund houses publish names only. Every weight is then null; a list is never mixed.
  • Synthetic ETFs. A swap-based ETF holds a substitute basket that backs the swap. holdingsBasis is then substituteBasket, and sector and geography describe the index instead.

The sector, country and asset weights describe the whole portfolio, from the fund house's own tables or its holdings file, so they are not limited to the listed positions. Where a holdings file exists, keyFacts.holdings counts its lines. The coverage pages state, per fund house, which sections the payload fills.

Overlap and look-through

Two endpoints do the holdings arithmetic for you:

  • GET /overlap?isins=A,B returns, for each pair of funds, the overlap percentage (the sum of the smaller weight of every shared holding, matched by issuer name), the shared positions and how much of each portfolio was disclosed. When a fund publishes only its top ten, the figure is a lower bound; it is null, not 0, when a fund gives names without weights.
  • POST /portfolio takes weighted positions and returns the combined sector, country, region, asset and currency weights, the combined top holdings, the blended TER and the weighted risk indicator, each with a coverage figure.

Both are included in Pro, Scale and Enterprise and count one request per fund. The free ETF overlap checker and portfolio look-through run without a key on funds already loaded.

What it does not do

  • No complete constituent file: topHoldings stops at the fund house's list, and each row is a name and a weight, without the holding's ISIN or ticker.
  • No intraday holdings, and no regulatory filings such as 13F or N-PORT. Holdings come from the fund house's own documents.
  • No prices or quotes. Use a market-data API alongside for those.
  • Stocks, bonds and indices are not funds. They return 404 fund_not_found, which is not counted, up to as many misses a month as the plan's allowance.

Price

Free is $0: 15 lookups a month, no card, personal use, one ISIN per call. Starter is $9.99 a month for 500 requests and batches of 10, Pro $49 for 9,000 and batches of 50, Scale $249 for 60,000 and batches of 200 (VAT included). Holdings overlap, portfolio look-through and commercial use of the data come with Pro, Scale and Enterprise. One request is counted per fund found; /search and the demo endpoint are free. See pricing.

Get started in three steps

  1. See the payload without a key. curl -s https://fundfactsapi.com/api/v1/demo/funds/IE00B4L5Y983 returns the full payload of any fund already loaded, holdings and exposure included.
  2. Create a free key. Sign up with an e-mail address or Google. The Free plan gives 15 lookups a month without a card, and the key is in the dashboard.
  3. Request your ETF by ISIN. Call GET /api/v1/funds/{isin} with Authorization: Bearer <key>. A fund nobody requested in the last 24 hours takes 15 seconds to 3 minutes to load the first time, so set a 300-second timeout. The quickstart has the SDK version.

Frequently asked questions

Is there a free ETF holdings API?

Yes. The FundFacts API Free plan gives 15 lookups a month without a card, with the full payload (holdings, sector and country weights included) for personal, non-commercial use. The demo endpoint /api/v1/demo/funds/{isin} needs no key for funds already loaded.

Does it cover European UCITS ETFs?

Yes. UCITS ETFs are at the core of the coverage, including iShares, Xtrackers, SPDR, Amundi, Vanguard and Invesco. Each share class is looked up by its own ISIN, and the coverage pages say which sections each fund house fills.

Can I get the full holdings list?

Not as one complete file. topHoldings lists the largest positions the fund house publishes: ten for most funds, up to 50 where its data lists more. The sector, country and asset weights describe the whole portfolio, and keyFacts.holdings gives the number of positions.

How often are holdings updated?

Each ISIN is re-read from the fund house's documents when its payload is older than 24 hours. The holdings are as recent as those documents: data.dataAsOf dates them, typically the last month end for a factsheet's top-ten table.

Does it return ETF prices?

No. There are no quotes, intraday prices or iNAV. The payload carries calendar and annualised returns and monthly return series built from the fund house's published NAV history; use a market-data API alongside for prices.

Can I look up an ETF by ticker?

Yes, through GET /search?q=: it matches an exchange ticker such as VWCE or EUNL, a name or an ISIN prefix among funds already loaded, returns the ISIN and is not counted. The holdings call itself takes the ISIN.

Can I compare the holdings of two ETFs?

GET /overlap?isins=A,B returns the overlap percentage and the shared positions, on Pro, Scale and Enterprise. The free ETF overlap checker does the same in the browser for funds already loaded.

Try it on your own ISINs

One request returns key facts, holdings, risk and performance as JSON. Free plan, no card.