Workflows

How do I bulk-export fund data for a whole universe?

GET /api/v1/export streams every stored fund matching issuer, asset class, currency or since-date filters as NDJSON (optionally gzipped) in one request on the Scale plan. Loading a universe first, and the batch alternative on Pro.Updated 12 September 2026 · by FundFacts API

Short answer

On the Scale plan, GET https://fundfactsapi.com/api/v1/export?issuer=&assetClass=&currency=&since=&gzip=1 streams every fund in the store that matches the filters as NDJSON, one full payload per line, for one request. The store contains funds that have been loaded, so seed your universe first with batch calls (POST /funds, 200 ISINs per call on Scale). On Pro, the same result is a loop of batch calls of 50 with the payloads written to your own database.

Export on Scale

bash
curl -s "https://fundfactsapi.com/api/v1/export?issuer=ishares&assetClass=Equities&gzip=1" -H "Authorization: Bearer $FUNDFACTS_API_KEY" \
| gunzip | head -n 2 | jq '{isin, name, ter: .data.headlineMetrics.ter}'

Filters: issuer, assetClass, currency, since (payloads generated after this date). Each line is the same envelope as GET /funds/{isin}. One request per call regardless of size.

Seeding the universe

The export returns what is in the store. To cover a universe:

  1. Build the ISIN list (your own, or from the coverage pages and GET /search).
  2. Load it with POST /funds in batches of the plan size with wait: true; re-send pending entries after a minute.
  3. Repeat daily for the ISINs whose expiresAt has passed; on Scale, or Pro with overage, that is one request per ISIN per day.

On Pro

python
import os, requests, json, time
H = {"Authorization": f"Bearer {os.environ['FUNDFACTS_API_KEY']}"}
isins = [l.strip() for l in open("universe.txt") if l.strip()]
with open("funds.ndjson", "a") as out:
for i in range(0, len(isins), 50):
r = requests.post("https://fundfactsapi.com/api/v1/funds", json={"isins": isins[i:i+50], "wait": True}, headers=H, timeout=300).json()
for x in r["results"]:
if x["status"] == "ok":
out.write(json.dumps(x) + "\n")
elif x["status"] == "pending":
isins.append(x["isin"]) # try again at the end
time.sleep(1)

Keeping it current

Pair the export with the change feed to update only the funds whose tracked fields moved, rather than re-exporting everything. The quant research use case covers the analysis side.

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

Is there a full dump of every fund you have?

The export is per account and returns the funds in the store matching your filters; there is no separate static dataset. Enterprise customers with universe-scale needs get metered pricing and a named contact.

Try it on your own ISINs

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