---
title: "How do I compare two ETFs or funds programmatically?"
description: "Fetch both ISINs in one batch call, put TER, risk indicator, size, top holdings, sector and country weights and calendar returns side by side, then add the overlap endpoint for shared holdings. Code included."
canonical: https://fundfactsapi.com/answers/how-to-compare-two-etfs-programmatically
published: 2026-09-12
updated: 2026-10-13
publisher: FundFacts API
---

# How do I compare two ETFs or funds programmatically?

**Short answer:** Send both ISINs to POST https://fundfactsapi.com/api/v1/funds (or two GET calls on the Free plan) and read the same fields from each payload: headlineMetrics.ter, riskRating, keyFacts.aum, profile.category, topHoldings, sector, geography, calendarReturns and annualisedReturns. For shared holdings call GET /overlap?isins=A,B (Starter and above), which returns the overlap percentage and the common names. The MCP server's compare_funds tool does the same for AI assistants.

## Fetch both

```python
import os, requests
H = {"Authorization": f"Bearer {os.environ['FUNDFACTS_API_KEY']}"}
r = requests.post("https://fundfactsapi.com/api/v1/funds", json={"isins": ["IE00B4L5Y983", "IE00B3RBWM25"], "wait": True}, headers=H, timeout=300)
funds = {x["isin"]: x for x in r.json()["results"] if x["status"] == "ok"}
```

## Put the figures side by side

```python
def row(label, get):
    return [label] + [get(funds[i]) for i in ("IE00B4L5Y983", "IE00B3RBWM25")]

table = [
    row("Name", lambda f: f["name"]),
    row("Category", lambda f: f["data"]["profile"]["category"]),
    row("TER", lambda f: f["data"]["headlineMetrics"]["ter"]),
    row("Risk (1-7)", lambda f: f["data"]["riskRating"]),
    row("Size", lambda f: f["data"]["keyFacts"]["aum"]),
    row("Holdings", lambda f: f["data"]["keyFacts"]["holdings"]),
    row("Top 10 weight", lambda f: round(sum(h["weight"] for h in f["data"]["topHoldings"][:10]), 1)),
    row("US weight", lambda f: next((g["weight"] for g in f["data"]["geography"] if g["label"] == "United States"), None)),
    row("3y vol", lambda f: f["data"]["headlineMetrics"]["volatility3y"]),
    row("Max drawdown", lambda f: f["data"]["metrics"]["maxDrawdown"]),
    row("2025 return", lambda f: dict(zip(f["data"]["calendarReturns"]["years"], f["data"]["calendarReturns"]["fund"])).get("2025")),
    row("As of", lambda f: f["data"]["dataAsOf"]),
]
for r in table:
    print(f"{r[0]:<14} {str(r[1]):<28} {str(r[2]):<28}")
```

## Shared holdings

```bash
curl -s "https://fundfactsapi.com/api/v1/overlap?isins=IE00B4L5Y983,IE00B3RBWM25" -H "Authorization: Bearer $FUNDFACTS_API_KEY" \
  | jq '.pairs[0] | {overlap, sharedCount, top: .shared[:5]}'
```

`overlap` is the percentage of portfolio weight the two funds have in common (the sum over shared names of the smaller weight), `shared` lists each common holding with its weight in both. Starter and above; the free [ETF overlap tool](https://fundfactsapi.com/tools/etf-overlap) runs it for you in the browser.

## Presenting it

Keep the strings as printed (a TER of "0.20%" next to "0.22%" reads better than 0.002), show `dataAsOf` under the table, and avoid ranking words: a comparison presents facts, not a recommendation. The [comparison tool guide](https://fundfactsapi.com/blog/build-fund-comparison-tool) covers the UI and caching, and the [ETF comparison website use case](https://fundfactsapi.com/use-cases/etf-comparison-website) the SEO angle.

## FAQ

### Can I compare more than two?

Yes: batch up to 10 ISINs on Starter, 50 on Pro and 200 on Scale, and /overlap accepts several ISINs and returns every pair.

### 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.

## About FundFacts API

FundFacts turns any fund or ETF ISIN into one dated 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 a client-ready PDF under your firm's name for advisers and wealth managers, as JSON through its API for developers, or inside ChatGPT and Claude through its MCP server.

FundFacts works inside ChatGPT and Claude as a connector (a remote MCP server): ask about any fund or ETF by ISIN, ticker or name and the assistant answers from FundFacts' dated figures, fees, risk, holdings, exposure and returns, with fund cards, side-by-side comparisons, holdings overlap and portfolio look-through.

- Free API key, no card: https://fundfactsapi.com/signup
- Documentation: https://fundfactsapi.com/docs (OpenAPI: https://fundfactsapi.com/openapi.json)
- MCP server: https://fundfactsapi.com/api/mcp (set-up for every client: https://fundfactsapi.com/mcp-server)
- Pricing: https://fundfactsapi.com/pricing
- Site index for AI assistants: https://fundfactsapi.com/llms.txt
- Informational data, not investment advice.
