How do I get notified when a fund's TER, risk indicator or holdings change?
The change feed (GET /api/v1/changes?since=; your watched funds from Starter, every fund on Scale) lists every tracked field that moved between refreshes, and signed webhooks push the same events. Which fields are tracked, the event shape and the signature check.Published September 12, 2026 · by FundFacts APIShort answer
Watch the funds first: POST https://fundfactsapi.com/api/v1/watchlist { "isins": [...] } (free; 10 funds on Starter, 100 on Pro). Then GET https://fundfactsapi.com/api/v1/changes?since=<ISO date>&isins=A,B returns every tracked field that changed between refreshes (TER, risk indicator, AUM, manager, distribution policy, holdings count, benchmark, objective and the top-five holdings) as fund.changed events, plus fund.refreshed when the as-of date moves, on your watched funds from Starter and on every fund in the store on Scale. Webhooks registered through POST /webhooks push the same events to your URL, signed with an HMAC in the X-FundFacts-Signature header, with retries. The watchlist and the feed are free to read.
See it on a real fund page: the IE00B4L5Y983 page shows the figures this answer describes, with their as-of date.
Watch the funds first
bashcurl -s -X POST https://fundfactsapi.com/api/v1/watchlist -H "Authorization: Bearer $FUNDFACTS_API_KEY" -H "Content-Type: application/json" \-d '{"isins":["IE00B4L5Y983","IE00B3RBWM25"]}'
The watchlist is a fund list named "API watchlist" in the dashboard, with alerts on; GET /watchlist lists it with each fund's name and last refresh, DELETE /watchlist removes funds. Starter watches up to 10 funds and Pro up to 100 across every list with alerts on (400 watchlist_full past that); on Scale and Enterprise the feed and the webhooks cover every fund in the store, so the watchlist is optional there. From Claude or ChatGPT the same list is kept with the MCP tools watch_fund, unwatch_fund and my_watched_funds.
Tracked fields
A refresh compares the new payload with the one it replaces. A difference in any of these produces one fund.changed row per field, with the old and the new value:
headlineMetrics.terriskRatingkeyFacts.aum,keyFacts.manager,keyFacts.distribution,keyFacts.holdingsbenchmarkName,investmentObjective- the top-five names in
topHoldings
A change of data.dataAsOf alone (a new factsheet with the same tracked values) produces fund.refreshed.
Polling the feed
bashcurl -s "https://fundfactsapi.com/api/v1/changes?since=2026-09-01T00:00:00Z&isins=IE00B4L5Y983,IE00B3RBWM25" -H "Authorization: Bearer $FUNDFACTS_API_KEY"
json{"since": "2026-09-01T00:00:00.000Z", "count": 2, "hasMore": false, "nextSince": "2026-09-11T06:02:11.000Z","changes": [{ "id": 8812, "isin": "IE00B4L5Y983", "name": "iShares Core MSCI World UCITS ETF", "event": "fund.changed", "field": "headlineMetrics.ter", "old": "0.20%", "new": "0.18%", "dataAsOf": "2026-09-01", "changedAt": "2026-09-11T06:02:11.000Z" },{ "id": 8813, "isin": "IE00B4L5Y983", "name": "iShares Core MSCI World UCITS ETF", "event": "fund.refreshed", "field": "dataAsOf", "old": "2026-08-01", "new": "2026-09-01", "dataAsOf": "2026-09-01", "changedAt": "2026-09-11T06:02:11.000Z" }]}
Filter with event=fund.changed or fund.refreshed and page with limit (up to 1,000); store nextSince and pass it on the next call while hasMore is true. On Starter and Pro the response says scope: "watchlist", ?isins= is cut to the watched funds (the rest come back in unwatched) and an empty watchlist answers an empty feed with a note; on Scale it says scope: "all". Reading the feed is free; the funds must be in the store, so keep them warm with a daily batch call.
Webhooks
bashcurl -s -X POST https://fundfactsapi.com/api/v1/webhooks -H "Authorization: Bearer $FUNDFACTS_API_KEY" -H "Content-Type: application/json" \-d '{"url":"https://example.com/hooks/fundfacts","events":["fund.changed"]}'
Every delivery is signed: X-FundFacts-Signature: t=<unix>,v1=<HMAC-SHA256(secret, "<t>.<body>")>. Verify the HMAC with the secret returned at creation, reject stale timestamps, and answer 2xx quickly; failed deliveries are retried with back-off up to six times, and an endpoint that keeps failing is disabled. On Starter and Pro a webhook without isins follows every watched fund, including the ones you add later, and named isins must be on your watchlist (400 watchlist_required otherwise); on Scale it covers every fund in the store or the ISINs you name. Manage endpoints from the Webhooks tab of the dashboard or GET/POST /webhooks, DELETE /webhooks/{id}.
Without code
Starter and Pro e-mail you when the fees, holdings or risk score of a fund on your lists change (up to 10 and 100 watched funds, the API watchlist included). The compliance monitoring use case describes the full workflow.
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]}'