Files
fuel-price/docs/api-reference.md
Ovidiu U 1860cf0a49
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (8.3) (push) Has been cancelled
tests / ci (8.4) (push) Has been cancelled
tests / ci (8.5) (push) Has been cancelled
feat: add API key authentication and update tests
Adds `VerifyApiKey` middleware protecting all API routes with `X-Api-Key` header validation. Wraps `/api/stations`, `/api/stats/searches`, and `/api/prediction` in throttled middleware group (60 req/min). Updates StationSearchTest to use `RefreshDatabase`, adds `meta` assertion checks, and validates `fuel_type` in HTTP request assertions. Removes auth routes from API docs and replaces with API key authentication instructions. Adds `api_secret_key` config option.
2026-04-05 20:27:41 +01:00

7.6 KiB
Raw Blame History

FuelAlert API Reference

Base URL: https://fuel-price.test/api
All endpoints return JSON. All endpoints require an X-Api-Key header.

Authentication:

X-Api-Key: your-secret-key

All requests without a valid key return 403 Forbidden.


Stations

GET /api/stations

Returns nearby petrol stations with live prices for a given fuel type.

Location — provide one of:

Parameter Type Description
postcode string UK postcode, outcode, or place name (resolved via postcodes.io)
lat + lng float Decimal lat/lng (required if no postcode)

Optional:

Parameter Type Default Notes
fuel_type string Required. See fuel type aliases below
radius float 10.0 Search radius in km (0.150)
sort string "price" "price", "distance", "updated", or "brand"
pricing_mode string "pump" (reserved, no effect yet)

Sort values:

Value Sorts by
price Price ascending (cheapest first) — default
distance Distance ascending (closest first)
updated Price freshness descending (most recently updated first)
brand Brand name AZ

Fuel type aliases (fuel_type accepts any of these):

Alias Maps to
petrol, unleaded, e10 E10 (standard unleaded)
premium_unleaded, e5 E5 (super unleaded)
diesel, b7_standard B7 Standard (standard diesel)
premium_diesel, b7_premium B7 Premium (premium diesel)
b10 B10 (biodiesel blend)
hvo HVO (hydrotreated vegetable oil)

Example request:

GET /api/stations?postcode=SW1A1AA&fuel_type=petrol&radius=5&sort=price
GET /api/stations?lat=51.5074&lng=-0.1278&fuel_type=diesel&radius=10&sort=distance
GET /api/stations?postcode=M11AE&fuel_type=petrol&sort=updated
GET /api/stations?postcode=M11AE&fuel_type=petrol&sort=brand

Response:

{
  "data": [
    {
      "station_id": "0028acef5f3afc41...",
      "name": "Alex Fuel Station",
      "brand": "BP",
      "is_supermarket": false,
      "address": "123 High Street, London",
      "postcode": "SW1A 1AA",
      "lat": 51.5074,
      "lng": -0.1278,
      "distance_km": 1.23,
      "fuel_type": "e10",
      "price_pence": 14390,
      "price": 143.9,
      "price_updated_at": "2026-04-05T08:00:00.000Z"
    }
  ],
  "meta": {
    "count": 12,
    "fuel_type": "e10",
    "radius_km": 10.0,
    "lowest_pence": 14290,
    "highest_pence": 14890,
    "cheapest_price_pence": 14290,
    "avg_pence": 14523.5
  }
}

Notes:

  • price_pence is the raw integer (e.g. 14390 = 143.90p). price is the float in pence (e.g. 143.9).
  • Closed stations (temporary or permanent) are excluded.
  • Every call logs a search record used by the stats endpoint.

Error — postcode not found:

{ "errors": { "postcode": ["Postcode not found."] } }

Error — missing location:

{ "errors": { "lat": ["The lat field is required when postcode is not present."] } }

Stats

GET /api/stats/searches

Aggregate search statistics. Useful for a "social proof" counter on the frontend.

Parameter Type Default Notes
period string "week" "week" (7 days) or "month" (30 days)

Example request:

GET /api/stats/searches
GET /api/stats/searches?period=month

Response:

{
  "total_searches": 3842,
  "unique_searchers": 1201,
  "avg_results": 14.3,
  "avg_lowest_price": 141.2,
  "avg_highest_price": 148.7,
  "avg_price": 144.9,
  "period": "week",
  "message": "Helped 1201 drivers find cheaper fuel this week so far!"
}

Notes:

  • avg_lowest_price, avg_highest_price, avg_price are in pence as floats (e.g. 141.2 = 141.2p).
  • unique_searchers is based on hashed IP — approximate unique users.

Prediction

GET /api/prediction

National (or regional) fuel price direction forecast for the next 7 days, based on live price data signals.

Parameter Type Description
fuel_type string Required. Same aliases as /api/stations
lat float Optional. Enables regional momentum signal
lng float Optional. Enables regional momentum signal

Example request:

GET /api/prediction?fuel_type=diesel
GET /api/prediction?fuel_type=petrol&lat=51.5074&lng=-0.1278

Response:

{
  "fuel_type": "e10",
  "current_avg": 143.9,
  "predicted_direction": "down",
  "predicted_change_pence": -2.1,
  "confidence_score": 74.5,
  "confidence_label": "high",
  "action": "wait",
  "reasoning": "National prices have been falling at 0.3p/day. Supermarkets led last week — independents typically follow within 48h.",
  "prediction_horizon_days": 7,
  "region_key": "national",
  "methodology": "multi_signal_live_fallback",
  "signals": {
    "trend": {
      "score": -0.8,
      "confidence": 0.92,
      "direction": "down",
      "detail": "Slope: -0.30p/day over 5 days (R²=0.92) [Adaptive lookback active]",
      "data_points": 5,
      "enabled": true,
      "slope": -0.3,
      "r_squared": 0.92
    },
    "day_of_week": {
      "score": 0.0,
      "confidence": 0.0,
      "direction": "stable",
      "detail": "Insufficient history for day-of-week pattern.",
      "data_points": 0,
      "enabled": true
    },
    "brand_behaviour": {
      "score": -1.0,
      "confidence": 0.6,
      "direction": "down",
      "detail": "Supermarkets fell 3.5p vs majors 0.5p (divergence: 3.0p). Expect majors to follow.",
      "data_points": 2800,
      "enabled": true
    },
    "national_momentum": {
      "score": 0.0,
      "confidence": 0.0,
      "direction": "stable",
      "detail": "National momentum disabled for national predictions",
      "data_points": 0,
      "enabled": false
    },
    "regional_momentum": {
      "score": 0.0,
      "confidence": 0.0,
      "direction": "stable",
      "detail": "No coordinates provided for regional momentum analysis",
      "data_points": 0,
      "enabled": false
    },
    "price_stickiness": {
      "score": 0.05,
      "confidence": 0.8,
      "direction": "stable",
      "detail": "Sticky prices (avg hold: 5.2 days) — more predictable.",
      "data_points": 160,
      "enabled": true
    }
  }
}

Key fields:

Field Values Meaning
predicted_direction "up", "down", "stable" Price trend direction
action "fill_now", "wait", "no_signal" Consumer-facing recommendation
confidence_label "high" (≥70), "medium" (≥40), "low" (<40) Signal strength
predicted_change_pence float Expected p/litre change over 7 days
current_avg float Current national average in pence (e.g. 143.9 = 143.9p)

Signal structure (each signal in signals):

Field Type Notes
score float (-1.0 to 1.0) Negative = falling pressure, positive = rising
confidence float (0.01.0) How reliable this signal is
direction "up" / "down" / "stable" Signal direction
detail string Human-readable explanation
data_points int Number of price records used
enabled bool False if signal was skipped (missing data/coords)

Error — unknown fuel type:

{ "errors": { "fuel_type": ["Unknown fuel type. Use: diesel, petrol, e10, e5, hvo, b10."] } }

Error Shapes

Validation error (422):

{
  "message": "The fuel type field is required.",
  "errors": {
    "fuel_type": ["The fuel type field is required."]
  }
}

Forbidden (403) — missing or invalid X-Api-Key:

{ "message": "Forbidden." }