Skip to main content
Apps

FuelRoute: From Overpaying in Sussex to a CloudFlare Worker

I overpaid for petrol while driving through Sussex to look at a horse I might buy. That’s not the most embarrassing way I’ve wasted money, but it’s unusually specific.

I didn’t know the area, had no signal for much of the drive, and filled up at the first station I saw. A few miles down the road, a friend who knew the region pointed out a station with prices 10p a litre cheaper. By then I’d already paid for a full tank. That difference seems minor until you realise it happens constantly to drivers everywhere — uncertainty about whether you’re getting ripped off because you can’t see what’s available nearby.

On the drive back, I heard it on the radio: the UK Government had just launched Fuel Finder. Under The Motor Fuel (Open Data) Regulations 2025, every petrol station in the country is now legally required to report its prices within 30 minutes of any change.

It’s run by the Department for Energy Security and Net Zero, with the Competition and Markets Authority holding enforcement powers. The infrastructure had landed. The data existed.

And the first thing I thought was: Apple CarPlay. Not a mobile app you pull out while driving. Drivers need this information while they’re deciding where to fuel up, right there on their dashboard.

Building FuelRoute

I started on 13 February 2026 — eleven days after the scheme launched. The app pulls live fuel price data and displays it the way drivers actually need it: nearby stations sorted by price, distance, current cost. A map view so drivers can see where stations are geographically. A price picker with a liquid glass design where the differences pop visually. Route planning so you can decide whether it’s worth detouring to the cheaper station.

The initial version launched quickly. CarPlay entitlements working, location permissions handling reliably, a screenshot mode for App Store screenshots that skips live API calls. Privacy manifest. The plumbing that makes an app feel solid rather than hacky.

But the Fuel Finder API had constraints I couldn’t ignore.

The API Problem

The Fuel Finder API covers all 7,600+ UK stations in one place — comprehensive and authoritative. But it’s rate-limited to 30 requests per minute and requires OAuth 2.0 credentials. The developer guidelines are explicit: client secrets must not be embedded in mobile apps. Even if I ignored that rule, the maths doesn’t work. Each full data pull takes about 36 paginated API calls. If a few hundred users refresh simultaneously, the rate limit is gone in seconds.

I needed a proxy cache. Something that fetches the data once, caches it, and serves every user from cache. But I didn’t want to add another Docker container to my DigitalOcean droplet for what’s essentially “fetch JSON every 5 minutes, cache it, serve it.” That felt like using a sledgehammer to crack a nut.

Then I remembered I already use CloudFlare for DNS. And CloudFlare has Workers.

What Actually Is a CloudFlare Worker?

If you haven’t come across Workers before, the mental model is surprisingly simple. A CloudFlare Worker is a small piece of JavaScript or TypeScript that runs on CloudFlare’s edge network — the same infrastructure that handles DNS and CDN for millions of websites.

When a request hits a URL you define, CloudFlare intercepts it and runs your function on the nearest edge server instead of routing it to a traditional backend. There are over 300 of these edge locations worldwide.

The interesting technical detail: Workers aren’t containers. They’re not even VMs. They’re V8 isolates — the same JavaScript engine that powers Chrome. CloudFlare runs thousands of V8 instances on each edge server. When your Worker starts, it gets its own lightweight sandboxed execution context that spins up in under 5 milliseconds. No cold boot of an OS, no container image pull, no kernel. Just a JS runtime sandbox.

The trade-off is you’re limited to JavaScript, TypeScript, or WebAssembly. You can’t run arbitrary binaries. But for “fetch, cache, serve” that’s more than enough. CloudFlare’s CLI tool wrangler handles the rest — npx wrangler dev for local development, npx wrangler deploy to push to production.

The Architecture

The whole thing is straightforward:

UK Gov Fuel Finder API
    → CloudFlare Worker (OAuth + cache in KV)
        → CloudFlare Edge CDN (300+ locations)
            → FuelRoute iOS app / CarPlay

The Worker handles OAuth authentication, fetches station metadata and fuel prices from the government API, caches everything in CloudFlare KV, and serves three JSON endpoints that the iOS app calls. A cron trigger refreshes the data every 5 minutes.

Total code: about 400 lines of TypeScript.

Getting It Into Production

The developer portal registration was straightforward — GOV.UK One Login, register as an “Information Recipient”, create an application, get a client_id and client_secret. The fair use policy shapes the design though: you must call the API at least every 5 minutes on average, present prices unbiasedly, and include a way for users to report discrepancies. All reasonable stuff, but it directly influenced how the Worker behaves.

Credentials go into CloudFlare’s encrypted secrets store via npx wrangler secret put — never touching disk or version control. This is exactly the separation the government API terms require: credentials on the server side, never in the mobile app.

The Gotchas

A few things bit me that aren’t obvious from the documentation.

The government API sits behind AWS CloudFront. My Worker runs on CloudFlare’s edge. When CloudFront sees a request coming from a known CDN/proxy IP range with minimal headers, it returns a 403. Local development worked perfectly — wrangler dev runs on your machine with a normal IP. Production failed silently. The fix was adding browser-like headers to all outbound requests:

const API_HEADERS = {
  "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)...",
  "Accept": "application/json, text/html, */*",
  "Accept-Language": "en-GB,en;q=0.9",
  "Referer": "https://www.fuel-finder.service.gov.uk/",
  "Origin": "https://www.fuel-finder.service.gov.uk",
};

Two CDN providers, both doing their job, creating friction where there shouldn’t be any.

The developer documentation also referenced api.fuelfinder.service.gov.uk as the base URL. That domain doesn’t resolve. The actual base URL is www.fuel-finder.service.gov.uk/api/v1 — 20 minutes of confused curl commands before the DNS failure made it obvious. And the API returns raw JSON arrays at the top level, not wrapped in {"data": [...]} like most APIs, while the OAuth token response is wrapped. Consistency isn’t the API’s strongest feature, but the data is comprehensive and that matters more.

I also added a /sync endpoint that triggers the data fetch on demand. During development, waiting for the cron to fire to see if a code change worked was painful. Being able to curl https://fuel.dreamfold.dev/api/v1/sync and immediately see either {"status": "synced"} or the full error message made debugging the CloudFront issue much faster.

7,674 Stations

Once the Worker was syncing, the numbers told the story. The government API returns 7,674 stations — every UK fuel retailer, mandated by law. Every independent, every supermarket forecourt, every motorway service station.

The API also includes fields like is_motorway_service_station, is_supermarket_service_station, amenities (24-hour fuel, car wash, toilets), and opening hours. The motorway flag meant I could finally add motorway filtering — a feature I’d been sketching out for weeks but couldn’t build without reliable data to back it.

Caching Strategy

This is where the engineering got satisfying. The Worker uses CloudFlare KV with different TTLs for different data:

  • Station metadata — cached for 24 hours. Stations don’t move. New ones open rarely.
  • Fuel prices — cached for 10 minutes. Prices change throughout the day.
  • OAuth tokens — cached for just under an hour (they expire at 3,600 seconds).

Price syncs use incremental updates. The first fetch pulls everything; subsequent fetches pass an effective-start-timestamp parameter and only get prices that changed since the last sync. This keeps the request size small and stays well within the 30 requests-per-minute rate limit.

The iOS app refreshes from the Worker every 30 minutes. The Worker’s Cache-Control: public, max-age=300 header means CloudFlare’s edge CDN also caches the response — so even if thousands of users hit the endpoint simultaneously, most requests never reach the Worker at all.

Was a Worker the Right Choice?

I already run Docker containers on a DigitalOcean droplet for my other projects. A FastAPI container would have been the consistent choice — same stack, same deployment pipeline, same monitoring. It would have worked fine.

But the Worker is genuinely better for this specific job. It’s stateless, it’s edge-cached, it scales to zero when nobody’s using it, and I don’t think about it. The cron runs, the cache fills, the app fetches. No container to monitor, no process to restart, no disk to fill up.

The cost model sealed it. The free tier gives you 100,000 requests per day — if you exceed it, the Worker stops responding rather than charging you. No surprise bills. The paid tier is $5/month for 10 million requests. To put that in perspective: 10,000 daily active users making 5 requests each is 50,000 requests, half the free tier. I’d need a genuinely successful app before this costs anything. And when it does scale, CloudFlare’s edge network handles it — 300+ locations, automatic load distribution, no capacity planning on my end.

Four hundred lines of TypeScript, a cron trigger, and a KV namespace. The government API sees one client making 7 requests every 5 minutes, well within its rate limits. Every FuelRoute user gets fresh data from the nearest edge server. The rate limit problem that started this whole thing simply doesn’t exist anymore.

The whole journey — from overpaying in Sussex to a CarPlay app to a CloudFlare Worker proxying a government API — is the kind of chain reaction that only happens when boring infrastructure decisions create space for interesting products. The government opened the data. CloudFlare made the edge accessible. I just connected the dots.

FuelRoute is available on the App Store. The Worker source is on GitHub.