Skip to content

The CrUX 28-day rolling window

Published:

We ship a fix for a performance problem, verify it works with lab tools, and sit there staring at CrUX waiting for the number to drop. A week goes by and nothing moves. Another week, still the same. It feels like the data is arriving late, or like the change did nothing.

Neither is true (well, the second one I can’t guarantee). In my audits this is one of the confusions I have to clear up most often, and it almost always comes from the same misunderstanding: conflating two things that are independent.

On one side there’s the size of the aggregation window. On the other, the publishing cadence. The window is always 28 days. Daily, weekly and monthly aren’t different windows: they’re different cadences over the same data.

Four doors into the same dataset

Every CrUX source aggregates the data over a 28-day rolling window. What changes between them is how often the data refreshes and how finely we can slice it.

SourceCadenceWindowGranularity
CrUX APIDaily, around 04:00 UTC, roughly two days behind28 rolling daysOrigin or page · phone, tablet, desktop
History APIWeekly, on Mondays, through the previous SaturdayWeekly series; each point, 28 days. Up to 40 points (25 by default)Origin or page · phone, tablet, desktop
BigQueryMonthly, the second Tuesday after the collection periodLast 28 days of the monthOrigin only · phone, tablet, desktop · approximated percentiles
PSI and Search ConsoleDerived from the API28 daysVaries by tool

The most commonly misread part: the data isn’t 28 days old, it’s two days old. What takes 28 days is for the window to fully turn over.

The History API row is the densest in the table, because it describes a time series rather than a single value. Here’s the breakdown:

What happens to the window after a deploy

Each day that passes, the window drops one old day and picks up a new one. At first the fix sits in a tiny sliver; as the days go by, it takes up more of the window.

Drag the control to see how much of the window already holds post-fix sessions, and which percentile of the old data you’re actually reading.

That third readout is what explains the feeling that nothing is happening. CrUX doesn’t report the mean, it reports the 75th percentile, and the p75 of a mixture doesn’t move linearly with the fraction of new data.

Why p75 looks stuck and then falls off a cliff

Assuming the fix puts every new session in the good bucket, at a fraction f of refreshed data the p75 of the mixture is equivalent to reading percentile (0.75 − f) / (1 − f) of the old distribution.

In practice:

So for the first week and a half nothing seems to move, then somewhere between week two and week three it drops all at once. The data isn’t late: the metric is resistant to change by design. And if traffic isn’t uniform, whether from weekends or regional seasonality, the curve distorts further, because CrUX weights by navigation volume, not by day.

What you’re actually looking at in CrUX Vis

CrUX Vis visualizes the History API, not the daily API. This has two concrete consequences:

Drag the control to separate the two points and watch the overlap shrink. The dates are a real example from CrUX Vis: point A closes its window on Saturday, January 10, and point B is the one that comes one or more weeks later.

That’s why the series looks so smooth: each point is already a 28-day aggregate, and CrUX Vis draws them overlapping week by week. A jump between two adjacent points never represents a real weekly change, it represents the effect of swapping out 7 days of 28.

CrUX is the scoreboard, not the feedback loop

The framing error is treating CrUX as a fast feedback system. Its slowness is a deliberate property: it smooths out noise so the number stays stable, because it’s the field-data system of record behind the Core Web Vitals assessment. Fast feedback belongs in a different layer.

One operational note: if you need to slice by country, BigQuery is the only CrUX source that exposes that dimension. The daily API and the History API only give you form factor. For regional analysis, the monthly dataset isn’t “the slow one”, it’s the only one that answers the question.

Conclusion

CrUX doesn’t measure our deploy. It measures the people who visit us over 28 days. If we want to know whether the fix worked today, we ask our RUM tool. If we want to know whether Google has registered it, we ask CrUX in three weeks.

References: CrUX methodology, History API and CrUX Vis on Chrome for Developers.


Next Post
A 301 on every click: the trailing slash that slowed down all internal navigation