{
  "openapi": "3.0.3",
  "info": {
    "title": "Fueltool heating oil prices for AI assistants (no key)",
    "description": "Keyless operations only, for AI assistants and GPT Actions: live UK heating oil quotes for a postcode (30 requests an hour per address) and the UK average price feed. Same prices as fueltool.co.uk, cheapest first, no supplier names; order_url opens Fueltool's order page on the cheapest quote, ready to confirm, with the other prices under it (each quote also has its own order_url), and the person confirms the order there, on Fueltool itself, not on a supplier's website. No card details are taken online: the chosen supplier phones the customer to take payment. Never put a person's name, address or email into a link. Credit \"Data: Fueltool.co.uk\". Full spec including the keyed endpoint: https://www.fueltool.co.uk/api/v1/openapi.json",
    "version": "1.0.0",
    "contact": {
      "name": "Fueltool",
      "url": "https://www.fueltool.co.uk/contact"
    }
  },
  "servers": [
    {
      "url": "https://www.fueltool.co.uk"
    }
  ],
  "components": {
    "schemas": {
      "QuoteResponse": {
        "type": "object",
        "properties": {
          "source": {
            "type": "string",
            "example": "Fueltool - UK heating oil price comparison"
          },
          "site": {
            "type": "string",
            "example": "https://www.fueltool.co.uk"
          },
          "docs": {
            "type": "string",
            "example": "https://www.fueltool.co.uk/api/"
          },
          "currency": {
            "type": "string",
            "example": "GBP"
          },
          "postcode": {
            "type": "string",
            "example": "MK401AA"
          },
          "litres": {
            "type": "integer",
            "example": 1000
          },
          "fuel": {
            "type": "string",
            "example": "Heating Oil"
          },
          "covered": {
            "type": "boolean",
            "description": "false means no supplier currently delivers this fuel to this postcode through Fueltool"
          },
          "quotes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Quote"
            }
          },
          "order_url": {
            "type": "string",
            "description": "Opens Fueltool's order page on the cheapest quote, ready to confirm, with the other prices for the postcode under it - send the person here to order. The price is checked again when the page opens; links work for 24 hours. The order is placed on Fueltool itself, not on a supplier's website, only when the person confirms it there; no card details are taken online, the chosen supplier phones the customer to take payment. When covered is false it opens the results page instead, where the person can leave an email address to hear when the area is covered."
          },
          "note": {
            "type": "string",
            "description": "Human-readable summary, including honest explanation when covered is false"
          }
        }
      },
      "Quote": {
        "type": "object",
        "properties": {
          "rank": {
            "type": "integer",
            "description": "1 = cheapest",
            "example": 1
          },
          "product": {
            "type": "string",
            "description": "Delivery service name, prefixed with its lead time",
            "example": "10 Day"
          },
          "total_gbp_inc_vat": {
            "type": "string",
            "description": "Total price in GBP including 5% VAT",
            "example": "889.98"
          },
          "vat_gbp": {
            "type": "string",
            "example": "42.38"
          },
          "pence_per_litre_ex_vat": {
            "type": "string",
            "example": "84.76"
          },
          "lead_days": {
            "type": "integer",
            "description": "Working-day lead time",
            "example": 10
          },
          "delivery_by": {
            "type": "string",
            "description": "Real delivery date accounting for weekends, UK bank holidays and the 2pm cut-off",
            "example": "Thu 3 Sep"
          },
          "order_url": {
            "type": "string",
            "description": "Opens Fueltool's order page on this quote, ready to confirm, with the other prices for the postcode under it. Works for 24 hours; the price is checked again when the page opens."
          }
        }
      },
      "PriceTrackerResponse": {
        "type": "object",
        "properties": {
          "source": {
            "type": "string",
            "example": "Fueltool - UK heating oil price comparison"
          },
          "site": {
            "type": "string",
            "example": "https://www.fueltool.co.uk"
          },
          "docs": {
            "type": "string",
            "example": "https://www.fueltool.co.uk/api/"
          },
          "unit": {
            "type": "string",
            "description": "What the figures mean and how they are averaged"
          },
          "methodology": {
            "type": "string",
            "description": "How the current average and supplier area count are calculated"
          },
          "today": {
            "type": "object",
            "properties": {
              "average_ppl": {
                "type": "number",
                "example": 88.38
              },
              "fill_1000_litres_gbp_inc_vat": {
                "type": "number",
                "description": "What 1,000 litres costs at average_ppl, including 5% VAT, rounded to the pound",
                "example": 928
              },
              "as_of": {
                "type": "string",
                "example": "2026-08-18 23:45 UK"
              }
            }
          },
          "change": {
            "type": "object",
            "properties": {
              "vs_week_ago_ppl": {
                "type": "number",
                "description": "The last day in the series minus the value 7 days earlier in the series (or its first day, if the series is shorter)",
                "example": -0.4
              },
              "vs_month_ago_ppl": {
                "type": "number",
                "description": "The last day in the series minus the value 30 days earlier in the series (or its first day, if the series is shorter)",
                "example": 1.2
              }
            }
          },
          "days": {
            "type": "integer",
            "example": 90
          },
          "series": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "date": {
                  "type": "string",
                  "example": "2026-08-18"
                },
                "average_ppl": {
                  "type": "number",
                  "example": 88.38
                }
              }
            }
          },
          "attribution": {
            "type": "object",
            "properties": {
              "required": {
                "type": "boolean",
                "example": true
              },
              "text": {
                "type": "string",
                "example": "Data: Fueltool.co.uk"
              },
              "link": {
                "type": "string",
                "description": "Today's national price page (https://www.fueltool.co.uk/heating-oil-prices); the county page in county mode",
                "example": "https://www.fueltool.co.uk/heating-oil-prices"
              }
            }
          },
          "county": {
            "type": "object",
            "description": "Only when county was asked for. The same figures as the county page.",
            "properties": {
              "name": {
                "type": "string",
                "example": "Flintshire"
              },
              "slug": {
                "type": "string",
                "example": "flintshire"
              },
              "url": {
                "type": "string",
                "description": "The county page; link to it when showing these figures",
                "example": "https://www.fueltool.co.uk/heating-oil/flintshire"
              },
              "local_price": {
                "type": "boolean",
                "description": "false when no Fueltool supplier price covers this county today; then from_ppl and fill_1000_litres_gbp_inc_vat are absent and today.average_ppl is the UK figure",
                "example": true
              },
              "from_ppl": {
                "type": "number",
                "description": "The cheapest price in pence a litre, ex VAT, for 1,000-1,499 litres across the county's postcode areas that a supplier covers today: a starting price, not an average",
                "example": 108.0
              },
              "fill_1000_litres_gbp_inc_vat": {
                "type": "number",
                "description": "from_ppl for 1,000 litres including 5% VAT, rounded to the pound",
                "example": 1134
              },
              "checked": {
                "type": "string",
                "example": "2026-10-24"
              },
              "basis": {
                "type": "string",
                "description": "How the figures are made, in words"
              }
            }
          },
          "note": {
            "type": "string"
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "enum": [
              "missing_key",
              "invalid_key",
              "invalid_postcode",
              "invalid_litres",
              "rate_limited",
              "busy",
              "cross_site",
              "bad_county",
              "unknown_county",
              "server_error"
            ]
          },
          "message": {
            "type": "string"
          }
        }
      }
    }
  },
  "paths": {
    "/api/v1/open-quote/": {
      "get": {
        "operationId": "getHeatingOilQuotesOpen",
        "summary": "Get live heating oil quotes for a UK postcode (no key needed)",
        "description": "Keyless version of the quote endpoint for AI assistants answering a person in conversation. Same prices, same rules: cheapest first, VAT-inclusive totals, real delivery dates, no supplier names. order_url opens Fueltool's order page on the cheapest quote, ready to confirm, with the other prices for the postcode under it, and each quote has its own order_url for that quote (links work for 24 hours; the price is checked again when the page opens). The person confirms the order there, on Fueltool itself, not on a supplier's website; no card details are taken online, the chosen supplier phones the customer to take payment. Limited to 30 requests an hour per address plus an overall hourly cap; for heavier use create a free key and use /api/v1/quote/. Never put a person's name, address or email into order_url.",
        "security": [],
        "parameters": [
          {
            "name": "postcode",
            "in": "query",
            "required": true,
            "description": "A full UK postcode, e.g. MK40 1AA",
            "schema": {
              "type": "string",
              "example": "MK40 1AA"
            }
          },
          {
            "name": "litres",
            "in": "query",
            "required": false,
            "description": "Quantity of oil in litres, a whole number from 500 to 40,000. Defaults to 1000.",
            "schema": {
              "type": "integer",
              "default": 1000,
              "minimum": 500,
              "maximum": 40000,
              "example": 1000
            }
          },
          {
            "name": "fuel",
            "in": "query",
            "required": false,
            "description": "kerosene = standard heating oil (default); premium = premium kerosene for boilers (premium_kerosene, and the legacy gasoil, mean the same); aga = AGA / range cooker oil, kerosene with a range cooker additive",
            "schema": {
              "type": "string",
              "enum": [
                "kerosene",
                "premium",
                "aga",
                "premium_kerosene",
                "gasoil"
              ],
              "default": "kerosene"
            }
          },
          {
            "name": "tanker",
            "in": "query",
            "required": false,
            "description": "Delivery vehicle size for restricted access: normal (default), small or baby",
            "schema": {
              "type": "string",
              "enum": [
                "normal",
                "small",
                "baby"
              ],
              "default": "normal"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Quotes for the postcode (may be empty with covered=false if nobody delivers there yet)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QuoteResponse"
                }
              }
            }
          },
          "400": {
            "description": "Not a valid UK postcode, or litres not a whole number from 500 to 40,000",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Called from another website's pages in a visitor's browser (cross_site); call from a server or an assistant instead",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests from this address this hour, or the overall hourly cap is reached",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/price/": {
      "get": {
        "operationId": "getUKAveragePrice",
        "summary": "UK average heating oil price - today and daily history (open, no key)",
        "description": "The Fueltool average domestic heating oil price in pence per litre (ex VAT, standard kerosene, 1,000-1,499 litre band), the median of active price listings updated within 30 days, excluding demo accounts (half the listings are cheaper and half dearer); suppliers may list several areas and delivery options. This is not a survey of every UK supplier. Includes change vs a week and a month ago and a daily series. The series runs to the last complete day (normally yesterday) and the change figures are measured on it, so they can be reproduced from the series; today.average_ppl is the live figure and can differ slightly from the last series day. No API key required. Free to use with attribution and a link to fueltool.co.uk. Historical figures are frozen - a past day's average never changes after the fact, so quoted numbers stay reproducible. Optional county: the name from a Fueltool county page address (https://www.fueltool.co.uk/heating-oil/<county>) adds a county object with that county's own figures, the same as the county page (its cheapest price today across the postcode areas a supplier covers, not an average), or local_price false where there is no local figure today; the series is then always 90 days.",
        "security": [],
        "parameters": [
          {
            "name": "days",
            "in": "query",
            "required": false,
            "description": "Length of the daily history series, 7 to 730 days (ignored when county is given: the series is then 90 days)",
            "schema": {
              "type": "integer",
              "default": 90,
              "minimum": 7,
              "maximum": 730
            }
          },
          {
            "name": "county",
            "in": "query",
            "required": false,
            "description": "A county, as named in its Fueltool county page address (https://www.fueltool.co.uk/heating-oil/<county>), lower case with hyphens. Adds the county object to the response. One county per call.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9-]{1,60}$",
              "example": "flintshire"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Today's average, recent changes and the daily series (and the county object when county was given)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PriceTrackerResponse"
                }
              }
            }
          },
          "400": {
            "description": "county is not a lower-case name with hyphens (error bad_county)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "No Fueltool county page has that name (error unknown_county)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    }
  }
}
