Files
fuelboard-relay-api/README.md
T
FuelBoard Contributor b315f4dc47 FuelBoard Relay API: official Fuel Finder API adapter (sibling of CSV relay)
- Talks to the OFFICIAL GOV.UK Fuel Finder API instead of the public CSV
  mirror: OAuth token (/api/v1/oauth/generate_access_token), batched
  paging (500/page via batch-number) over /api/v1/pfs and
  /api/v1/pfs/fuel-prices, joined on node_id.
- Price strings parsed to pence with pounds->pence correction (< 2.0);
  50-500p sane band keeps junk out of the cheapest reference.
- Fuel mapping: E5/E10 as-is; B7/B7_STANDARD/B7S/B7P/B10 -> DIESEL;
  HVO/SDV dropped (app tracks E5/E10/DIESEL only).
- lat/lng parsed from nested location strings.
- Output contract IDENTICAL to fuelboard-relay (/health + /api/v1/stations),
  so the app/widget/alerts work unchanged; switch = point app at :8789.
- CSV mirror kept as automatic fallback (FUEL_SOURCE=auto) so the full
  path is testable before GOV.UK One Login creds exist.
- 12 unit tests against documented API shapes (no network).
2026-08-12 15:34:57 +01:00

4.2 KiB
Raw Blame History

FuelBoard Relay API

Keyless proxy for the official UK Fuel Finder API (the OAuth-protected GOV.UK service). Sibling of fuelboard-relay (which reads the public CSV mirror): this one talks to the real API, joins the station-info and fuel-price endpoints on node_id, and normalises to the same output shape so the FuelBoard iOS app and widget work against either relay unchanged.

Holds the GOV.UK OAuth 2.0 client credentials server-side so the app never embeds a secret (secrets inside an IPA are extractable by anyone).

Architecture

FuelBoard app / widget
        │  GET http://192.168.1.131:8789/api/v1/stations?fuel=e10&lat=..&lng=..
        ▼
   FuelBoard Relay API (this repo, runs on the Mac Mini)
        │  POST /api/v1/oauth/generate_access_token  (token cached ~1h)
        │  GET  /api/v1/pfs?batch-number=N           (station info, 500/page)
        │  GET  /api/v1/pfs/fuel-prices?batch-number=N (prices, 500/page)
        ▼
   UK Fuel Finder API  (official gov.uk open data)
  • Token refreshed automatically before expiry (60s buffer); reused across the two batched loops (docs: reuse tokens until near expiry).
  • Both endpoints paginated 500 records/page via batch-number until a short page is returned.
  • Prices arrive as decimal strings in pence ("0120.0000" = 120.0p); some stations report pounds (values < 2.0 are multiplied by 100). Out-of-band values are dropped (50500p sane band) so a pounds-denominated column can't poison the nationwide cheapest reference.
  • Fuel types mapped: E5/E10 → same, B7/B7_STANDARD/B7S/B7P/B10DIESEL. Others (HVO, SDV, …) are ignored — the app tracks exactly E5/E10/DIESEL.
  • Station snapshot cached FUEL_CACHE_TTL (default 300s); stale cache is served on upstream failure so the app never sees a hard outage.
  • No credentials are exposed to clients — the app calls the relay keyless.

Setup

python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
pip install pytest          # only needed for the test suite
cp .env.example .env        # then fill in FUEL_API_CLIENT_ID / FUEL_API_CLIENT_SECRET
./scripts/run.sh            # serves on FUEL_RELAY_PORT (default 8789)

Without credentials, FUEL_SOURCE=auto falls back to the public hourly CSV mirror (same parser as fuelboard-relay), so the full relay→app path is testable end-to-end before GOV.UK One Login creds exist. /health reports the active source (csv vs api).

Endpoints

Endpoint Description
GET /health Cache status, active source, last sync, failure counters
GET /api/v1/stations All stations with prices (keyless)
GET /api/v1/stations?fuel=e10&lat=53.72&lng=-1.85&radius=10&limit=10 Filter by fuel, radius around a point, sorted nearest-first
GET /api/v1/stations?fuel=diesel (no lat/lng) Sorted cheapest-first

Fuel grades: e10, e5, diesel. Output contract identical to fuelboard-relay — the app can switch relays by changing the base URL only.

Getting credentials

  1. Sign in at https://developer.fuel-finder.service.gov.uk/get-started-ifr/onelogin with GOV.UK One Login.
  2. Register an application → get client_id + client_secret.
  3. Put them in .env (never in git, never in the app).

Fair use notes (from the Fuel Finder developer guideline)

  • Refresh at least every 5 minutes — we do (cron-style refresh on request + TTL cache).
  • Serve data unmodified — this relay only filters/sorts, never alters prices.
  • Keep timestamps — price_updated is passed through untouched.
  • Rate limits: ~100 rpm documented — batched loops issue ~2 requests per page (info + prices), comfortably inside the limit on a 5-minute cadence.

Tests

python -m pytest tests/ -q

Covers price-string parsing (pence, pounds, out-of-band), the node_id join, fuel-type mapping, and CSV-fallback parsing — against the documented API shapes, no network required.

Switching the app

Point Shared/FuelPriceProvider.swift's RelayFuelProvider.baseURL at http://192.168.1.131:8789 (API relay) instead of :8788 (CSV relay). No other app change needed — the station JSON is identical in shape.