{
  "openapi": "3.1.0",
  "info": {
    "title": "ProofPrice API",
    "termsOfService": "https://proofprice.com/terms",
    "contact": {
      "name": "ProofPrice",
      "email": "hello@proofprice.com",
      "url": "https://proofprice.com"
    },
    "version": "0.3.0",
    "description": "What did people actually pay for X, here, recently? Returns a price range from official open data: median, 10th–90th percentile, sample size, period, confidence, source and licence. Aggregates only; never individual records."
  },
  "servers": [
    {
      "url": "https://proofprice.com"
    }
  ],
  "paths": {
    "/api/v1/cars": {
      "get": {
        "operationId": "carPrices",
        "summary": "Car-related prices actually paid (SG)",
        "description": "Use this when a user asks what people actually paid for the right to own or buy a car (for example Singapore's Certificate of Entitlement, COE) in Singapore car ownership rights (COE) (country SG). Returns the median winning price, a typical range (10th–90th percentile), the number of bidding rounds or sales, the period covered, a confidence label, and the official source and licence. Do not use for asking prices, dealer listings, or other countries.",
        "parameters": [
          {
            "name": "country",
            "in": "query",
            "required": true,
            "description": "SG = Singapore COE (Certificate of Entitlement) bidding results",
            "schema": {
              "type": "string",
              "enum": [
                "SG"
              ]
            }
          },
          {
            "name": "product",
            "in": "query",
            "required": false,
            "description": "Which price. 'coe' = Singapore Certificate of Entitlement bidding results. Default 'coe'. SG: 'coe'.",
            "schema": {
              "type": "string",
              "enum": [
                "coe"
              ]
            }
          },
          {
            "name": "months",
            "in": "query",
            "required": false,
            "description": "How many recent months to include. Default 12.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 60
            }
          },
          {
            "name": "vehicle_class",
            "in": "query",
            "required": false,
            "description": "SG only: COE category. A = cars up to 1600cc (default), B = cars above 1600cc, C = goods vehicles/buses, D = motorcycles, E = open.",
            "schema": {
              "type": "string",
              "enum": [
                "A",
                "B",
                "C",
                "D",
                "E"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A price answer: median, 10th–90th percentile range, sample size, period, confidence, source and licence.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "Missing or invalid parameter.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "The official source could not be reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/fuel": {
      "get": {
        "operationId": "fuelPricesNow",
        "summary": "Live fuel prices at the pump (ES, FR and IT)",
        "description": "Use this when a user asks the current price of petrol, diesel, E10, E85, LPG or CNG near a place in Spain, France or Italy, or where fuel is cheapest there. Returns the median and typical range of pump prices across stations in the area, the three cheapest stations, and the official source and licence. How fresh: Spain: several times a day; France: about every 10 minutes; Italy: daily (prices at 08:00 of the extraction day). Do not use for other countries or for historical prices.",
        "parameters": [
          {
            "name": "country",
            "in": "query",
            "required": true,
            "description": "ES = Spain; FR = France; IT = Italy",
            "schema": {
              "type": "string",
              "enum": [
                "ES",
                "FR",
                "IT"
              ]
            }
          },
          {
            "name": "fuel",
            "in": "query",
            "required": true,
            "description": "petrol95 = unleaded 95; methane = CNG (priced per kg). Italy: diesel, petrol95, lpg, methane only.",
            "schema": {
              "type": "string",
              "enum": [
                "diesel",
                "petrol95",
                "petrol98",
                "e10",
                "e85",
                "lpg",
                "methane"
              ]
            }
          },
          {
            "name": "postcode",
            "in": "query",
            "required": false,
            "description": "Postcode of the place. ES: 5-digit postcode, e.g. '28001'. FR: 5-digit postcode, e.g. '75015'.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "area",
            "in": "query",
            "required": false,
            "description": "Wider area when no postcode. ES: province code ('28' = Madrid). FR: department ('69', '2A'). IT: province ('MI', 'RM').",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "town",
            "in": "query",
            "required": false,
            "description": "Town name. ES: town name, e.g. 'Valencia'. IT: comune name, e.g. 'Bologna'.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "service",
            "in": "query",
            "required": false,
            "description": "Self-service or attended service, where a country prices them differently. IT: self-service (default) or attended service, which costs more.",
            "schema": {
              "type": "string",
              "enum": [
                "self",
                "served"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A price answer: median, 10th–90th percentile range, sample size, period, confidence, source and licence.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "Missing or invalid parameter.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "The official source could not be reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/fuel/history": {
      "get": {
        "operationId": "fuelPriceHistory",
        "summary": "Fuel price history (daily snapshots, building up from October 2026)",
        "description": "Use this when a user asks how pump prices have changed over recent days or weeks in a place, or whether fuel is getting cheaper. Returns the daily median and typical range of pump prices in the area, with the change over the period, from our daily snapshots of official data. Do not use for today's cheapest stations (use pricepaid_fuel_prices_now).",
        "parameters": [
          {
            "name": "country",
            "in": "query",
            "required": true,
            "description": "ES = Spain; FR = France; IT = Italy",
            "schema": {
              "type": "string",
              "enum": [
                "ES",
                "FR",
                "IT"
              ]
            }
          },
          {
            "name": "fuel",
            "in": "query",
            "required": true,
            "description": "petrol95 = unleaded 95; methane = CNG (priced per kg). Italy: diesel, petrol95, lpg, methane only.",
            "schema": {
              "type": "string",
              "enum": [
                "diesel",
                "petrol95",
                "petrol98",
                "e10",
                "e85",
                "lpg",
                "methane"
              ]
            }
          },
          {
            "name": "postcode",
            "in": "query",
            "required": false,
            "description": "Postcode of the place. ES: 5-digit postcode, e.g. '28001'. FR: 5-digit postcode, e.g. '75015'.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "area",
            "in": "query",
            "required": false,
            "description": "Wider area when no postcode. ES: province code ('28' = Madrid). FR: department ('69', '2A'). IT: province ('MI', 'RM').",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "town",
            "in": "query",
            "required": false,
            "description": "Town name. ES: town name, e.g. 'Valencia'. IT: comune name, e.g. 'Bologna'.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "service",
            "in": "query",
            "required": false,
            "description": "Self-service or attended service, where a country prices them differently. IT: self-service (default) or attended service, which costs more.",
            "schema": {
              "type": "string",
              "enum": [
                "self",
                "served"
              ]
            }
          },
          {
            "name": "days",
            "in": "query",
            "required": false,
            "description": "How many recent days to include. Default 30.",
            "schema": {
              "type": "integer",
              "minimum": 2,
              "maximum": 366
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A daily series: date, median, typical_low, typical_high, sample_size, with the change over the period, source and licence.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "Missing or invalid parameter.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "The official source could not be reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/home-sales": {
      "get": {
        "operationId": "homeSales",
        "summary": "Home sale prices actually paid (FR, GB, IE and SG)",
        "description": "Use this when a user asks what homes actually sold for in a town or area, what a fair price for a home is, or how much property costs there, in France (country FR), England & Wales (country GB), Ireland (country IE) or Singapore public housing (country SG). Returns the median sale price, a typical range (10th–90th percentile), the number of sales, the period covered, a confidence label, and the official source and licence, from government sale records. Do not use for rents, asking prices, commercial property, or other countries.",
        "parameters": [
          {
            "name": "country",
            "in": "query",
            "required": true,
            "description": "FR = France (DVF); GB = England & Wales (HM Land Registry); IE = Ireland (Property Price Register); SG = Singapore HDB resale flats",
            "schema": {
              "type": "string",
              "enum": [
                "FR",
                "GB",
                "IE",
                "SG"
              ]
            }
          },
          {
            "name": "town",
            "in": "query",
            "required": false,
            "description": "Town or city. FR: town, e.g. 'Bordeaux', or use postcode. GB: required, e.g. 'Bristol'. IE: a county ('Cork') or Dublin postal district ('Dublin 6'). Omit for all of Ireland. SG: required, e.g. 'Tampines'.",
            "schema": {
              "type": "string",
              "minLength": 2
            }
          },
          {
            "name": "postcode",
            "in": "query",
            "required": false,
            "description": "Postcode, for a precise area. FR: 5-digit postcode, e.g. '69003'; use instead of town for a precise area.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "property_type",
            "in": "query",
            "required": false,
            "description": "Kind of home. Default 'any'. FR: house or apartment. GB: detached, semi-detached, terraced or flat-maisonette. IE: any, new or second-hand (new homes are usually recorded without VAT).",
            "schema": {
              "type": "string",
              "enum": [
                "house",
                "apartment",
                "detached",
                "semi-detached",
                "terraced",
                "flat-maisonette",
                "new",
                "second-hand",
                "any"
              ]
            }
          },
          {
            "name": "months",
            "in": "query",
            "required": false,
            "description": "How many recent months to include. Default 12. IE: rounded to the nearest of 3, 6, 12, 24 or 36.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 36
            }
          },
          {
            "name": "postcode_district",
            "in": "query",
            "required": false,
            "description": "GB only: postcode district such as 'BS8' to narrow the area.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "county",
            "in": "query",
            "required": false,
            "description": "IE only: same as town; a county such as 'Dublin' or 'Cork'.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "flat_type",
            "in": "query",
            "required": false,
            "description": "SG only: HDB flat type.",
            "schema": {
              "type": "string",
              "enum": [
                "1 ROOM",
                "2 ROOM",
                "3 ROOM",
                "4 ROOM",
                "5 ROOM",
                "EXECUTIVE",
                "MULTI-GENERATION"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A price answer: median, 10th–90th percentile range, sample size, period, confidence, source and licence.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "Missing or invalid parameter.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "The official source could not be reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/coverage": {
      "get": {
        "operationId": "coverage",
        "summary": "Which countries, categories and sources are covered",
        "responses": {
          "200": {
            "description": "Sources with licence and update frequency.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          },
          "detail": {}
        },
        "required": [
          "error"
        ]
      }
    }
  }
}