Workflows

How do I calculate the holdings overlap between two ETFs?

Overlap is the sum over shared holdings of the smaller of the two weights. Compute it from data.topHoldings, or call GET /overlap?isins=A,B for a name-matched result. Formula, Python and the free tool.Updated 12 September 2026 · by FundFacts API

Short answer

Overlap = Σ over holdings present in both funds of min(weight in A, weight in B), in percent of portfolio. GET https://fundfactsapi.com/api/v1/overlap?isins=A,B (Pro and above) does the name matching and returns overlap, sharedCount and the shared list per pair; on any plan you can compute the same from each fund's data.topHoldings, limited to the positions the fund houses publish. The free ETF overlap tool on this site runs the endpoint in the browser.

The formula

For two funds A and B with holdings weights wA[name] and wB[name] in percent:

text
overlap(A, B) = Σ_{name ∈ A ∩ B} min(wA[name], wB[name])

An overlap of 40 means 40% of each portfolio's weight sits in names the other fund also holds (at the smaller of the two weights). Two share classes of the same index fund overlap near 100; a world ETF and an emerging-markets ETF near 0.

From the payload

python
import os, requests
H = {"Authorization": f"Bearer {os.environ['FUNDFACTS_API_KEY']}"}
def holdings(isin):
d = requests.get(f"https://fundfactsapi.com/api/v1/funds/{isin}", headers=H, timeout=300).json()["data"]
return {h["name"].upper(): h["weight"] for h in d["topHoldings"]}
a, b = holdings("IE00B4L5Y983"), holdings("IE00B3RBWM25")
shared = {n: min(a[n], b[n]) for n in a.keys() & b.keys()}
print(round(sum(shared.values()), 2), "% overlap over published holdings;", len(shared), "shared names")

This is exact over the positions the fund houses publish (the full file for most large ETFs, the top ten for factsheet-only funds) and is what the endpoint does, with better name matching.

The endpoint

bash
curl -s "https://fundfactsapi.com/api/v1/overlap?isins=IE00B4L5Y983,IE00B3RBWM25" -H "Authorization: Bearer $FUNDFACTS_API_KEY"
json
{ "pairs": [ { "a": "IE00B4L5Y983", "b": "IE00B3RBWM25", "overlap": 71.4, "sharedCount": 612, "disclosed": { "a": 100, "b": 100 }, "shared": [ { "label": "NVIDIA", "a": 5.48, "b": 4.61 } ] } ] }

disclosed states what share of each portfolio the published holdings cover, so you can tell an exact 71% from a top-ten-only estimate. Several ISINs return every pair. One request per ISIN; Pro and above. The MCP server's fund_overlap tool wraps it for assistants.

Reading the number

Overlap is about names, not about risk: two funds with 30% overlap can still be 95% correlated because they hold the same sectors and countries. Pair it with the sector and geography tables, or with POST /portfolio for the aggregate view. How to calculate ETF holdings overlap goes deeper into matching and edge cases.

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

Why is the free tool result different from my own calculation?

Name matching: the endpoint normalises issuer labels ("NVIDIA" vs "NVIDIA CORP") and uses the full holdings file where available, while a quick script over topHoldings may see only the top ten of one fund.

Try it on your own ISINs

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