# Overview

What FundFacts API returns, the endpoints, and how requests are counted.

HTML version: https://fundfactsapi.com/docs · Updated 2026-10-01

## 1. What the API is, and is not

- One resource: `GET https://fundfactsapi.com/api/v1/funds/{isin}`. Input is a 12-character ISIN; output is the envelope described in section 3 with a `data` object described in section 7.
- Coverage: UCITS funds, ETFs, money-market funds and most open-end funds worldwide. Equities, bonds, crypto, indices and structured products are not funds and return `404 fund_not_found`.
- Every payload is the fund's latest published figures. It is refreshed at most once per 24 hours per ISIN; `cached` tells you whether this call reused the stored payload.
- The API does not stream. To work on a universe of funds, send ISINs in batches of up to the plan's batch size (`POST /funds`) rather than one call per ISIN; `/search` turns names into ISINs but only over funds that have already been loaded.
- Values are shaped for display: money and percentages arrive as formatted strings (`"USD 105.4 bn"`, `"0.20%"`); breakdown weights and return series are numbers in percent.

## 3. Endpoint and response envelope

`GET https://fundfactsapi.com/api/v1/funds/{isin}` — `isin` is case-insensitive; the response echoes the canonical upper-case form.

| Path | Type | Meaning |
|---|---|---|
| `isin` | `string` | Canonical upper-case ISIN that was resolved. |
| `name` | `string \| null` | Fund / share-class name. |
| `cached` | `boolean` | true when the payload was served from the 24-hour cache, false when it was refreshed for this request. |
| `generatedAt` | `string (ISO 8601)` | When the payload was produced. |
| `expiresAt` | `string (ISO 8601)` | When the payload will be considered stale and refreshed on the next request. |
| `plan` | `"free" \| "starter" \| "pro" \| "scale" \| "enterprise"` | Plan of the API key used for the request. |
| `quota` | `{ limit: number \| null; remaining: number \| null; used: number; overageUsed: number; resetAt: string; burst: { limit: number \| null; remaining: number \| null } }` | Quota state after this request: the monthly request allowance (limit), requests left, requests used this month, requests billed as overage, the next monthly reset (UTC) and the per-minute burst allowance. null limit means metered / unlimited. |
| `data` | `object` | The structured factsheet. See the field reference below. |

### Other endpoints

| Endpoint | Purpose | Cost |
|---|---|---|
| `POST https://fundfactsapi.com/api/v1/funds` body `{ "isins": ["…"], "wait": true }` | Batch lookup; `results[]` carries `{ input, isin, status, name, cached, generatedAt, expiresAt, data }` with `status` = `ok` / `not_found` / `pending` / `invalid` / `error`. `pending` ISINs are being warmed in the background: re-send them later. | 1 per ok / not_found |
| `GET https://fundfactsapi.com/api/v1/search?q=msci world&limit=10` | Name, issuer or ISIN-prefix search over already-loaded funds. `results[]`: `{ isin, name, issuer, currency, assetClass, shareClass, income, category, dataAsOf, url }`. | free |
| `POST https://fundfactsapi.com/api/v1/portfolio` body `{ "positions": [{ "isin": "…", "weight": 60 }, …] }` | Look-through: weights normalised to 100; returns `positions[]`, `coverage`, `fees.weightedTer`, `risk.weightedSrri`, `kinds`, `assetAllocation`, `sector`, `geography`, `region`, `creditQuality`, `currency`, `topHoldings` (each breakdown `{ items: [{ label, weight }], coverage }`), `pending[]`. Starter+. | 1 per position |
| `GET https://fundfactsapi.com/api/v1/overlap?isins=A,B,C` | Pairwise holdings overlap: `pairs[] = { a, b, overlap, sharedCount, shared: [{ label, a, b }], sharedNames, disclosed }`; `overlap` is null (not 0) when a fund publishes names without weights (listed in `namesOnly`). Starter+. | 1 per ISIN |
| `GET https://fundfactsapi.com/api/v1/changes?since=<ISO>&isins=A,B` | Change feed (Scale+): tracked fields that moved between refreshes (`fund.changed`) and as-of date moves (`fund.refreshed`); page with `nextSince`. Webhooks (`/webhooks`) push the same events, signed with `X-FundFacts-Signature`. | free |
| `GET https://fundfactsapi.com/api/v1/export?issuer=&assetClass=` | Bulk NDJSON of every stored fund matching the filter (Scale+). | 1 per call |
| `POST https://fundfactsapi.com/api/v1/extract` (multipart `file`, or JSON `{ url }` / `{ text }`) | Any KID / KIID / factsheet PDF → the same `data` shape, for funds outside the covered issuers (Pro+). | 5 per document |
| `POST https://fundfactsapi.com/api/v1/factsheets` body `{ "isin": "…", "format": "html" | "pdf", "deliver": "file" | "link", "theme", "accent", "title", "branding", "logo" (https URL, Pro+), "template" (id or name, or "none"; Pro+: the account's default template applies when omitted), "font", "header", "palette", "secondary", "hide", "contact", "disclaimer" (Pro+) }` | One-page factsheet rendered with @fundfactsapi/widgets. Returns the document (text/html or application/pdf; share links in `X-Factsheet-Url` / `X-Factsheet-Pdf-Url`) or, with `deliver: "link"`, JSON `{ id, isin, name, issuer, format, options, favorite, createdAt, dataAsOf, urls: { html, pdf, embed } }`. The `urls` need no key: mail the PDF, link the page, embed the iframe. `GET /factsheets` lists, `GET /factsheets/{id}?format=pdf` re-renders, `PATCH` `{ favorite }`, `DELETE`; `GET /factsheets/templates` lists the saved templates (designed in the dashboard). | 1 (HTML) / 2 (PDF); list, get, share links free |
| `GET https://fundfactsapi.com/api/v1/me` | `{ email, plan: { id, name, requests, burstPerMinute, batchMax, overageUsd }, quota, usage: { thisMonth, monthStart } }`. Use once at startup to confirm the key, not on every request. | free |
| `GET https://fundfactsapi.com/api/v1/demo/funds/{isin}` | Keyless read of a fund already in the store (30/min per IP). For verifying the shape, never for production. | none |

Full reference: https://fundfactsapi.com/llms-full.txt
