FreshnessData qualityArchitecture

How fresh is fund data? As-of dates, publication lags and why a 24-hour cache is enough

Daily NAV, monthly holdings, yearly KID: what 'as of' means for each factsheet block, how to store it, and why a daily refresh matches how funds publish.Published 21 September 2026 · 6 min read · by FundFacts API

Fund data is not one dataset with one update frequency. A fund's NAV is struck daily, its holdings and exposures are published monthly (daily for some ETF issuers), its factsheet is monthly, its KID is reviewed at least annually, and its fund size moves with every dealing day but is reported at the factsheet's date. "Fresh" therefore means "no older than the fund's own publication cadence", and for factsheet-type data that cadence is measured in days to months. A store that refreshes each fund every 24 hours is at least as current as the documents it is built from, and the important thing is not the refresh rate but carrying the as-of date with every figure.

In short
Every figure on a factsheet has an "as of" date, and different blocks can have different dates. NAV: daily, published the next business day. Holdings and exposures: monthly for most funds, daily for some ETFs. Fees and risk indicator: from the KID, updated at least yearly. A 24-hour refresh cycle is fine for all of this; intraday updates only matter for exchange prices, which are not factsheet data. Store dataAsOf alongside the payload and show it.

What "as of" means on a factsheet

The date printed near the top of a factsheet is the reporting date: the day the portfolio snapshot was taken, usually the last business day of the month. But it is a snapshot of several things that are not equally fresh:

BlockUnderlying cadenceTypical lag behind the reporting date
NAV and performanceDaily NAV, published the next business dayNone at month end; the factsheet itself appears days to weeks later
Fund size (AUM)DailyReported at the factsheet date
Top holdings, sector, countryMonthly for most funds; daily holdings files for many ETF issuersSome issuers lag holdings one month behind performance
Number of holdingsSame as holdingsSame
TER / ongoing chargesFrom the KID, reviewed at least annually and when costs changeCan be up to a year old relative to today's actual costs
Risk indicator (SRI)Recalculated per KID rules, updated when it changes classSame
Calendar-year returnsAnnual, fixed once the year closesNone

So a factsheet dated 31 August can carry August performance, July holdings and a February KID's fee. This is normal and not an error. The practical consequence is that a single "last updated" timestamp on a fund record is not enough; you want the date each figure refers to.

The two dates you should store

FundFacts API returns two dates for every fund, and they answer different questions:

  • data.dataAsOf is the date the figures refer to: the latest NAV observation when the issuer publishes a NAV history, otherwise the factsheet's reporting date. This is the date to show next to the numbers ("data as of 31 August 2026").
  • generatedAt (in the envelope and in data) is when the payload was produced from the documents. expiresAt is when the store will refresh it on the next request, 24 hours later.

The distinction removes a common confusion. A payload generated this morning can legitimately carry a dataAsOf from the end of last month, because the fund has not published anything newer. Conversely, a payload with yesterday's dataAsOf was built from a daily NAV file. Both are fresh in the sense that matters: they reflect what the fund has disclosed.

bash
curl https://fundfactsapi.com/api/v1/funds/IE00B4L5Y983 \
-H "Authorization: Bearer ffk_live_..."
json
{
"isin": "IE00B4L5Y983",
"cached": true,
"generatedAt": "2026-09-21T06:02:11.408Z",
"expiresAt": "2026-09-22T06:02:11.408Z",
"data": {
"dataAsOf": "2026-09-18",
"generatedAt": "2026-09-21T06:02:11.408Z",
"keyFacts": { "aum": "USD 105.4 bn" },
"headlineMetrics": { "ter": "0.20%" }
}
}

Why a 24-hour cycle matches how funds publish

Consider what could change between two requests an hour apart. The NAV, if the issuer publishes it daily, changes once per business day, after the close. Holdings files from issuers who publish daily are updated once a day. Factsheets change once a month. KIDs change a few times a year at most. Nothing in a factsheet payload changes intraday, so refreshing more often than daily produces identical payloads and costs the issuer's servers and yours.

A 24-hour store also gives the behaviour products need: the first request for an ISIN loads the fund's documents (a cold load takes roughly one to three minutes for a fund nobody has asked for yet), and every later request within 24 hours returns in well under a second with cached: true. After 24 hours the next request rebuilds the payload and serves the new one. Concurrent requests for the same cold fund trigger one load. Funds in the pre-warmed universe are refreshed on a schedule, so the popular ETFs are always warm. The freshness and sourcing docs describe the mechanics.

For the same reason, caching on your side until expiresAt is safe. The ISIN lookup post shows a client that does this, and the pattern cuts usage to one request per fund per day regardless of how many users ask.

When you do need something faster

Two kinds of data are genuinely intraday, and neither is factsheet data:

  1. Exchange prices of ETF shares. Market price, bid and ask, and the premium or discount to NAV move all day and belong to a market-data feed. The NAV vs market price post explains why published performance uses NAV instead.
  2. Order and settlement data for your own transactions, which comes from your broker or custodian.

If your product shows a factsheet with a live price next to it, keep the two visibly separate: "price 15:42" and "data as of 31 August". Mixing a live price into a NAV-based series makes the last point incomparable with the rest.

Detecting when something actually changed

Daily refreshes produce many identical payloads. What most products want to know is when a tracked figure moved: the TER changed, the risk indicator moved a class, the manager changed, the fund size crossed a threshold, or the top-five holdings changed. Diffing payloads yourself works; the GET /api/v1/changes feed (Scale and Enterprise) does it server-side and returns the tracked fields that moved between refreshes, plus a fund.refreshed event whenever dataAsOf advances. Webhooks on the same plans push those events. The compliance monitoring use case is built on this.

bash
curl "https://fundfactsapi.com/api/v1/changes?since=2026-09-01T00:00:00Z&isins=IE00B4L5Y983,IE00B3RBWM25" \
-H "Authorization: Bearer ffk_live_..."

Displaying freshness honestly

  • Show `dataAsOf` next to every block that carries figures, and in the same format everywhere. Users rarely object to a month-old holdings date; they object to not being told.
  • Do not show `generatedAt` as "updated" without explanation. It says when the payload was rebuilt, not when the fund published.
  • Expect block-level skew. If you have both a NAV-derived dataAsOf and a factsheet-derived holdings snapshot, the holdings can be older; say so in a footnote.
  • Keep history. Store each day's dataAsOf and the tracked fields, so you can answer "when did the fee change" later. The change feed gives you this for free on Scale.
  • Treat empty as unknown, not zero. A blank dataAsOf or an empty metric string means the document does not state it.

For AI assistants the same rule applies in prose: every figure the model quotes should carry its date. The LLM grounding post turns that into a system-prompt rule, and the MCP tool descriptions ask the model to do it by default.

The short version: fund data is slow-moving by construction, a daily store is faster than the documents it reads, and the as-of date is the field that makes the whole thing trustworthy. Try it on a fund you follow with a free key and compare dataAsOf with the date on the issuer's factsheet.

FAQ

How often is ETF holdings data updated?

It depends on the issuer. Many large ETF issuers publish a full holdings file every business day; most traditional funds publish holdings monthly, sometimes with a one-month lag, and some only disclose their top ten. The dataAsOf field reports the date the returned figures refer to.

Is fund data available in real time?

Factsheet data is not real-time by nature: NAV is struck once a day and holdings, fees and risk indicators change monthly or yearly. Only the exchange price of an ETF's shares moves intraday, and that is market data rather than fund data.

What is the difference between dataAsOf and generatedAt?

dataAsOf is the date the fund's figures refer to (the latest NAV observation or the factsheet's reporting date). generatedAt is when the payload was built from the documents. A payload can be generated today with figures as of last month if the fund has published nothing newer.

Is caching fund data for 24 hours acceptable?

Yes. Nothing in a factsheet changes intraday, so a daily refresh is at least as current as the documents. FundFacts stores each fund for 24 hours, returns expiresAt so clients can cache until then, and offers a change feed for detecting when tracked fields actually moved.

Try it on your own ISINs

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