Workflows

How do I convert a KID or factsheet PDF to JSON?

POST /api/v1/extract with a PDF (file upload, URL or text) returns the same structured data shape as the ISIN endpoint: key facts, fees, risk indicator, KID cost table, holdings, breakdowns and returns. For funds outside the covered issuers. Code included.Updated 12 September 2026 · by FundFacts API

Short answer

Send the document to POST https://fundfactsapi.com/api/v1/extract as a multipart file, a JSON { "url": … } or { "text": … }. The response is the same data object the ISIN endpoint returns: key facts, TER, the 1–7 risk indicator, the KID cost table (read deterministically in EN, FR, DE, ES, IT, NL), holdings, sector and country tables, calendar returns and the as-of date, with empty values where the document does not print a figure. Pro and above; five requests per document.

The call

bash
# A PDF you have
curl -s -X POST https://fundfactsapi.com/api/v1/extract -H "Authorization: Bearer $FUNDFACTS_API_KEY" -F "file=@kid.pdf"
# A public URL
curl -s -X POST https://fundfactsapi.com/api/v1/extract -H "Authorization: Bearer $FUNDFACTS_API_KEY" \
-H "Content-Type: application/json" -d '{"url":"https://example.com/documents/factsheet.pdf"}'
python
import os, requests
with open("factsheet.pdf", "rb") as f:
r = requests.post("https://fundfactsapi.com/api/v1/extract", headers={"Authorization": f"Bearer {os.environ['FUNDFACTS_API_KEY']}"}, files={"file": f}, timeout=300)
d = r.json()["data"]
print(d["keyFacts"], d["headlineMetrics"]["ter"], d["riskRating"], d["costs"], d["topHoldings"][:5], d["dataAsOf"])

What it reads

  • KID / KIID: name, ISIN, risk indicator, cost table (entry, exit, ongoing, transaction, performance fee, reduction in yield, recommended holding period), SFDR article, objective text, in six languages with regex rules, then the document model for anything the rules did not catch.
  • Factsheet: key facts, TER, top holdings, sector / country / asset tables, calendar and annualised returns, portfolio characteristics.

The output shape is identical to GET /funds/{isin}, so one renderer or one database schema handles both sources. Fields the document does not print are empty, not guessed.

When to use it

  • The fund house is not among the covered issuers yet.
  • You receive documents from clients or counterparties rather than fetching them.
  • You need a specific historical document (last year's KID) rather than the current one.

For covered funds, GET /funds/{isin} is cheaper (one request instead of five) and adds the holdings file and the NAV-based series that a single PDF cannot provide.

Limits

Scanned images without a text layer are not read. Documents are processed on the fly and are not kept. Five requests per document; Pro and above. The UCITS KID explained for developers describes the document structure the parser relies on.

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

Can I extract holdings from a spreadsheet?

The extraction endpoint takes PDFs and text. For issuers that publish holdings files, the ISIN endpoint already reads them; for a file you have, load it with your spreadsheet library and map name and weight yourself.

Try it on your own ISINs

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