By field

How do I get the number of holdings and concentration of an ETF by ISIN?

Holdings count and top-ten concentration for any fund or ETF from its ISIN as JSON: data.keyFacts.holdings and data.profile.concentration, with the top holdings to sum yourself.Updated 12 September 2026 · by FundFacts API

Short answer

Call GET https://fundfactsapi.com/api/v1/funds/{isin} with a bearer API key and read data.keyFacts.holdings in the JSON response (related fields: data.profile.concentration, data.topHoldings). The count comes from the factsheet's key facts; concentration is derived from the top holdings' weights with a published threshold, so "concentrated" means the same for a 30-stock active fund and a thematic ETF.. The Free plan gives 15 lookups a month with no card, and the keyless demo endpoint shows the shape for an already-loaded fund.

Where it is in the response

PathTypeMeaning
data.keyFacts.holdingsnumber or stringNumber of positions as printed.
data.profile.concentration"concentrated" or "balanced" or "diversified" or nullTop-ten weight ≥50% concentrated, 30–50% balanced, <30% diversified.
data.topHoldingsArray<{ name, weight }>Sum the weights for the exact top-ten figure.

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

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

Where the figure comes from

The count comes from the factsheet's key facts; concentration is derived from the top holdings' weights with a published threshold, so "concentrated" means the same for a 30-stock active fund and a thematic ETF.

The request

bash
curl -s --max-time 300 https://fundfactsapi.com/api/v1/funds/IE00B4L5Y983 \
-H "Authorization: Bearer $FUNDFACTS_API_KEY" | jq '{holdings: .data.keyFacts.holdings, concentration: .data.profile.concentration}'
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']['keyFacts']['holdings'])
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.keyFacts.holdings);

Sign up at fundfactsapi.com/signup for a key; the Free plan is 15 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 count includes cash and derivative lines when the fund house counts them.
  • Funds of funds count the underlying funds, not their constituents; use POST /portfolio for a look-through.

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 ISIN answered. 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 nobody has requested in the last 24 hours is loaded 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 within 24 hours returns from the store in well under a second with cached: true.

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]}'

Terms used on this page

Frequently asked questions

Do I need a credit card to try it?

No. The Free plan is 15 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. 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.