{
  "openapi": "3.0.3",
  "info": {
    "title": "Fueltool Quote API",
    "description": "Live UK heating oil prices as JSON. Send a full UK postcode and a number of litres; receive current supplier quotes, cheapest first, with VAT-inclusive totals and real delivery dates (weekends, bank holidays and the 2pm cut-off already accounted for). Prices match the public site at fueltool.co.uk to the penny. Supplier identities are deliberately not included - customers see their supplier at checkout. When showing a price to a customer, link them to the order_url returned with it. Create a free private key instantly at https://www.fueltool.co.uk/api/#get-a-key. Each self-service key allows 60 requests per hour",
    "version": "1.0.0",
    "contact": {
      "name": "Fueltool",
      "url": "https://www.fueltool.co.uk/contact"
    }
  },
  "servers": [
    { "url": "https://www.fueltool.co.uk" }
  ],
  "paths": {
    "/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), averaged across active price listings updated within 30 days, excluding demo accounts. Each listing has equal weight; 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. 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.",
        "security": [],
        "parameters": [
          {
            "name": "days",
            "in": "query",
            "required": false,
            "description": "Length of the daily history series, 7 to 730 days",
            "schema": { "type": "integer", "default": 90, "minimum": 7, "maximum": 730 }
          }
        ],
        "responses": {
          "200": {
            "description": "Today's average, recent changes and the daily series",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/PriceTrackerResponse" }
              }
            }
          }
        }
      }
    },
    "/api/v1/quote/": {
      "get": {
        "operationId": "getHeatingOilQuotes",
        "summary": "Get live heating oil quotes for a UK postcode",
        "description": "Returns current supplier quotes for the given postcode and quantity, cheapest first. If no supplier covers the postcode yet, covered is false and the message explains honestly; the order_url page lets the customer leave an email to be told when their area is covered.",
        "security": [ { "apiKeyQuery": [] }, { "apiKeyHeader": [] } ],
        "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. Defaults to 1000.",
            "schema": { "type": "integer", "default": 1000, "example": 1000 }
          },
          {
            "name": "fuel",
            "in": "query",
            "required": false,
            "description": "kerosene = standard heating oil (default); premium = kerosene with additive; premium_kerosene = premium-grade kerosene (gasoil is accepted as a legacy alias)",
            "schema": { "type": "string", "enum": ["kerosene", "premium", "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", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } },
          "401": { "description": "Missing, unknown or disabled API key", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } },
          "429": { "description": "Hourly rate limit reached for this key", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKeyQuery": { "type": "apiKey", "in": "query", "name": "key" },
      "apiKeyHeader": { "type": "apiKey", "in": "header", "name": "X-Api-Key" }
    },
    "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": "Deep link to the live results page for this exact search - send customers here to order. Prices there match these exactly." },
          "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" }
        }
      },
      "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 },
              "supplier_count": { "type": "integer", "description": "Number of distinct supplier area accounts contributing to today's average, not separate companies. A supplier can have several area accounts. Existing field semantics are unchanged.", "example": 30 },
              "as_of": { "type": "string", "example": "2026-08-18 23:45 UK" }
            }
          },
          "change": {
            "type": "object",
            "properties": {
              "vs_week_ago_ppl": { "type": "number", "example": -0.4 },
              "vs_month_ago_ppl": { "type": "number", "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", "example": "https://www.fueltool.co.uk/" }
            }
          },
          "note": { "type": "string" }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "properties": {
          "error": { "type": "string", "enum": ["missing_key", "invalid_key", "invalid_postcode", "rate_limited", "server_error"] },
          "message": { "type": "string" }
        }
      }
    }
  }
}
