DEV Community

Cover image for Our seven-day freshness filter was measuring the wrong thing
Fuel Price Today
Fuel Price Today

Posted on

Our seven-day freshness filter was measuring the wrong thing

In the last post about the UK's Fuel Finder feed, we wrote that price_last_updated means "last changed", not "last confirmed", and that we therefore let only prices from the last seven days into our averages.

The first half was right. The second half was a mistake, and it took six weeks of history to see it.

The number that kept shrinking

Every average on fuelpricetoday.co.uk carries the number of stations behind it. For unleaded, outside motorway services:

Day Stations in the feed Changed in the last 7 days Changed in the last 30 days
9 September 7,681 7,293 7,529
20 October 7,760 3,515 7,242

Nothing was wrong with the feed. Prices had simply settled. In early September prices were climbing fast and nearly every station repriced within the week. By late October more than half of them were holding a price for longer than seven days.

Why that is a bias, not just a smaller sample

The UK regulations require a forecourt to report a price change within 30 minutes. A price that has not changed for ten days is therefore not stale: it is the price on the pump. A seven-day window does not select fresh data, it selects stations that recently changed their price.

And those are not a random sample. On 19 October, the share of each brand's unleaded prices that had changed in the past week:

Brand Stations Changed in 7 days
Sainsbury's 316 314
Asda 404 298
Morrisons 341 244
Tesco 516 313
BP 997 490
Esso 1,440 609
Shell 892 321

Supermarkets reprice constantly, oil company forecourts much less. A seven-day window kept almost every Sainsbury's and about a third of Shells, so the average drifted towards supermarket prices. On the same day, the unleaded prices that had changed within the week averaged 172.7p; those unchanged for 8 to 30 days averaged 174.8p.

It also exaggerated the trend. The stations that just changed are the ones that just followed the market, so in a falling October the seven-day average ran ahead of the market:

Day 7-day window 30-day window
22 September 172.9p 172.7p
16 October 172.8p 173.9p
20 October 172.6p 173.7p

A 1.1p gap is small in absolute terms, but it is the size of a weekly move, which is exactly what a price tracker exists to report.

The fix

One rule across the site: a price counts unless it has been unchanged for more than 30 days. That threshold is not new to us; it is where we already flagged a price as suspect, because a month without a single change suggests a station that has stopped reporting rather than one holding steady. Averages, rankings and the heat map now use it too.

// A price counts on `day` unless it has been unchanged for more than
// `window` days. price_last_updated dates the last change, not a confirmation.
function counts(row: { day: string; priceLastUpdated: string | null }, window = 30) {
  if (!row.priceLastUpdated) return false;
  const age = (Date.parse(row.day) - Date.parse(row.priceLastUpdated.slice(0, 10))) / 86_400_000;
  return age <= window;
}
Enter fullscreen mode Exit fullscreen mode

The station count behind the national average was 7,137 on 22 October instead of sliding towards 3,000, and its composition no longer depends on how calm the market is that week.

Two things we kept:

  • Published figures were not rewritten. Our weekly press bulletins up to mid-October used the seven-day rule. Each bulletin now records the window it was computed with and says so on the page.
  • The age stays visible. Every price on the site still shows when it last changed. A price can count in an average and still be shown as "unchanged since 2 October".

The lesson

The mistake was treating a filter as neutral. Any rule that admits rows based on behaviour (here, "changed recently") selects for whoever behaves that way. The check that caught it was cheap: publish the denominator next to every average, and watch it.

The data

The full history, every price at every station each day since 7 September 2026, is free to download under the Open Government Licence:

  • fuelpricetoday.co.uk/data: daily UK and town averages, the station list, and one history file per month, refreshed every three hours.
  • GitHub, closed months mirrored weekly with a Data Package descriptor.

The official feed shows only today's price, so this history is the part nobody else gives away.

Top comments (0)