By field

How do I get the holdings of an ETF or fund by ISIN?

Top holdings with weights for any ETF or fund from its ISIN, as JSON: data.topHoldings, the holdings count, and the overlap endpoint that compares two funds' holdings.Published September 12, 2026 · Updated October 13, 2026 · by FundFacts API

Short answer

Call GET https://fundfactsapi.com/api/v1/funds/{isin} with a bearer API key and read data.topHoldings in the JSON response (related fields: data.keyFacts.holdings, data.profile.concentration). Holdings are read from the issuer's holdings file where one is published (most large ETF ranges publish a daily CSV or XLSX) and from the factsheet's top-ten table otherwise. The Free plan gives 5 lookups a month with no card, and the keyless demo endpoint shows the shape for an already-loaded fund.

See it on a real fund page: the IE00B4L5Y983 page shows the figures this answer describes, with their as-of date.

Where it is in the response

PathTypeMeaning
data.topHoldingsArray<{ name, weight }>Largest positions with their weight in percent: the ten largest for most funds (from the factsheet table or the holdings file), up to 50 where the issuer's product data lists more.
data.keyFacts.holdingsnumber or stringTotal number of positions.
data.profile.concentrationstring"concentrated", "balanced" or "diversified" from the weight of the ten largest positions.

Sample for iShares Core MSCI World UCITS ETF (IE00B4L5Y983):

json
"topHoldings": [
{ "name": "NVIDIA", "weight": 5.48 },
{ "name": "APPLE", "weight": 5.24 },
{ "name": "MICROSOFT", "weight": 3.88 }
],
"keyFacts": { "holdings": 1253 },
"profile": { "concentration": "diversified" }

Where the figure comes from

Holdings are read from the issuer's holdings file where one is published (most large ETF ranges publish a daily CSV or XLSX) and from the factsheet's top-ten table otherwise. From a holdings file the API keeps the ten heaviest lines in topHoldings and uses every line for keyFacts.holdings and, where the file carries the columns, the sector, country and asset weights. Names are the issuer's own labels, so "NVIDIA" from one house and "NVIDIA CORP" from another are matched by name when GET /overlap compares two funds.

The request

bash
curl -s --max-time 300 https://fundfactsapi.com/api/v1/funds/IE00B4L5Y983 \
-H "Authorization: Bearer $FUNDFACTS_API_KEY" | jq '.data.topHoldings[:5]'
python
import os, requests
r = requests.get("https://fundfactsapi.com/api/v1/funds/IE00B4L5Y983",
headers={"Authorization": f"Bearer {os.environ['FUNDFACTS_API_KEY']}"}, timeout=300)
fund = r.json()
print(fund["name"], fund['data']['topHoldings'])
javascript
const res = await fetch("https://fundfactsapi.com/api/v1/funds/IE00B4L5Y983", {
headers: { Authorization: `Bearer ${process.env.FUNDFACTS_API_KEY}` },
signal: AbortSignal.timeout(300_000),
});
const fund = await res.json();
console.log(fund.name, fund.data.topHoldings);

Sign up at fundfactsapi.com/signup for a key; the Free plan is 5 lookups a month with no card. Without a key, GET https://fundfactsapi.com/api/v1/demo/funds/IE00B4L5Y983 returns the same payload for funds already in the store (30 calls a minute per IP; it never loads a new fund).

Caveats

  • The full constituent list is not part of the payload, even when the fund house publishes one: topHoldings stops at the ten largest lines (up to 50 for some issuers; Spanish funds from the CNMV register get the whole portfolio of their latest semi-annual report). For an active fund whose factsheet shows ten names, ten names is what you get; the payload never invents the rest.
  • Holdings files are usually one trading day old, factsheet tables one month old. data.dataAsOf tells you which.
  • To compare two ETFs' holdings, GET /overlap?isins=A,B (Starter and above) returns the shared names and the overlap percentage without you having to reconcile the lists.

Several funds at once

POST /funds with { "isins": ["…", "…"], "wait": true } returns the same payload for up to 10 ISINs per call on Starter, 50 on Pro and 200 on Scale. One request is counted per fund found. X-RateLimit-Remaining on every response tells you how many are left this month; a 429 rate_limited response carries Retry-After. Search and /me are free.

A fund never loaded before is read on demand from the documents the fund house publishes. That first call takes 15 seconds to about 3 minutes; set your client timeout to 300 seconds and do not fire a second request for the same ISIN while the first is running. Every later call returns from the store in well under a second with cached: true; past 24 hours the payload comes back flagged stale: true while a refresh runs in the background, and nothing older than 7 days is served without a new read.

Everything else in the payload is listed in the field reference.

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.
bash
curl -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]}'

Frequently asked questions

Do you return ISINs or tickers for each holding?

Only the name and weight as the fund house publishes them. Most factsheets print names only, and identifiers in a holdings file are not kept, so the overlap endpoint matches positions across funds by normalised name.

Do I need a credit card to try it?

No. The Free plan is 5 lookups per month with no card; sign up with an e-mail address or Google and the key is shown in the dashboard.

Which ISINs work?

Any share class of a UCITS fund, ETF, money-market fund or open-end fund whose management company publishes a product page, factsheet and KID. Each share class has its own ISIN and is looked up on its own. Stocks, bonds and indices are not funds and return 404 fund_not_found.

How current are the figures?

Each ISIN is re-read from the fund house's documents at most every 24 hours; a request past that is answered at once from the store (stale: true) while the refresh runs in the background. data.dataAsOf is the as-of date printed on the documents, typically the last month end for a factsheet and the last trading day for holdings files.

Try it on your own ISINs

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