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 APIShort 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 havecurl -s -X POST https://fundfactsapi.com/api/v1/extract -H "Authorization: Bearer $FUNDFACTS_API_KEY" -F "file=@kid.pdf"# A public URLcurl -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"}'
pythonimport os, requestswith 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.bashcurl -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]}'