FuelWatch API

Errors and resilience

Recognize API failures, retry safely and preserve valid prices.

/v1 errors use JSON:API documents with an errors array and no data. HTTP status and errors[].status agree; the latter is a string. Branch on code, not the human-readable detail.

Product 3 does not exist. The following request was made against production and returned this complete error document.

Unsupported product rejected

Captured 2026-09-30T11:42:43.711+08:00 (AWST) · HTTP 400. Historical snapshot; repeat the request for current data.

Request
curl 'https://fuelwatch.oss.bhodges.me/v1?product=3' \
  -H 'Accept: application/vnd.api+json'
Example response
{
  "jsonapi": {
    "version": "1.1"
  },
  "errors": [
    {
      "status": "400",
      "code": "invalid_query",
      "detail": "Invalid or unsupported FuelWatch query."
    }
  ]
}

All error responses use Cache-Control: no-store. A HEAD error has the same status with no body. Browser preflight failures and static asset responses can also be bodyless; do not assume every HTTP failure contains JSON.

Error catalogue

HTTPCodeMeaning
400invalid_queryUnsupported, repeated or invalid query parameter.
404not_foundPath or image does not exist.
405method_not_allowedUse GET, HEAD or supported OPTIONS preflight.
406not_acceptableAccept does not permit JSON:API.
415unsupported_media_typeUnsupported JSON:API Content-Type parameters.
500internal_errorUnexpected server failure.
502invalid_feedSource data failed validation or replaced published prices with an empty feed.
502response_too_largeSource selection exceeds bounded response limits.
502upstream_deniedSource refused the request.
503upstream_unavailableSource temporarily unavailable or unreachable.
503cache_refresh_busyAnother invocation holds the snapshot fill lease; retry shortly.
504upstream_timeoutSource did not complete within the shared deadline.

Retry guidance

Fix 400, 404, 405, 406 and 415 requests before retrying. For temporary 500, 503 and 504 failures, wait for Retry-After if present, then use a limited retry count and jittered backoff. cache_refresh_busy includes Retry-After: 2.

502 means the Worker could not safely supply the source selection. Keep the last valid snapshot and retry conservatively; splitting a large selection can help response_too_large, while validation failures need the source to recover.

The Worker already bounds upstream attempts and deadlines. Aggressive client loops add load without making publication or source recovery faster. The source fetch deadline is not a promised end-to-end latency SLA.

Empty is not an error

A valid empty selection returns 200 with data: []. Inspect publicationStatus to distinguish an expected unpublished tomorrow feed from other empty results.

This request for LPG at Costco Perth Airport returned no quotes at capture time. It is a successful empty selection, not an upstream failure.

Valid selection with no LPG quotes

Captured 2026-09-30T11:42:43.142+08:00 (AWST) · HTTP 200. Historical snapshot; repeat the request for current data.

Request
curl 'https://fuelwatch.oss.bhodges.me/v1?product=5&brand=32&suburb=PERTH%20AIRPORT&surrounding=no' \
  -H 'Accept: application/vnd.api+json'
Example response
{
  "jsonapi": {
    "version": "1.1"
  },
  "meta": {
    "source": "fuelwatch.wa.gov.au",
    "product": 5,
    "sourceDate": "2026-09-30",
    "fetchedAt": "2026-09-30T11:00:54.545+08:00",
    "validFrom": "2026-09-30T06:00:00.000+08:00",
    "validUntil": "2026-10-01T06:00:00.000+08:00",
    "publicationStatus": "empty",
    "copyright": "Copyright 2025 Department of Local Government, Industry Regulation and Safety (source data); Copyright 2026 Bradley Hodges (api). All rights reserved.",
    "documentation": "https://docs.fuelwatch.oss.bhodges.me",
    "issues": "https://github.com/bradleyhodges/fuelwatch-api/issues",
    "version": "4.30.1"
  },
  "data": []
}

Multi-product failures are atomic: if any required source component fails, the response fails instead of returning a partial success document. A station legitimately omitting one requested product is different and remains valid.

Limits and diagnostics

There is no application API-key quota or documented per-client request limit in the Worker. This does not promise unlimited use; infrastructure can impose its own controls. Cache responses, honor conditional requests and report reproducible failures with the URL, HTTP status and non-sensitive response headers.

Google's request budget belongs to background enrichment and is not a quota for public price requests. Missing provider data does not itself fail the price response.

For the compatibility endpoint, the error shape is {"error":{"code":"...","message":"..."}}. See legacy responses.

On this page