openapi: 3.0.3
info:
  title: Homedata API
  version: "1.0.0"
  description: >
    UK property data API covering 29M+ properties via REST. Address lookup, property
    attributes, EPC, environmental risks, planning applications, market listings,
    comparable sales, valuations, school data, and area intelligence.
  contact:
    email: support@homedata.co.uk
  x-logo:
    url: https://homedata.co.uk/img/logo.svg
x-stoplight-public: true
x-ai-summary: >
  REST API for UK property data. Base URL https://api.homedata.co.uk/api/.
  Auth header: Authorization: Api-Key YOUR_KEY.
  Most endpoints cost 1 API call; comparables = 10 calls; valuations/address-retrieve = 5 calls;
  address-find/postcode = 2 calls. Listing reveals (listing-address, property-listings,
  property-sale-events) are credit-billed at £0.20 per call.
servers:
  - url: https://api.homedata.co.uk/api
    description: Production API

security:
  - ApiKeyAuth: []

components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: Authorization
      description: "Format: `Api-Key YOUR_API_KEY`"

  schemas:
    AddressSuggestion:
      type: object
      properties:
        uprn: { type: integer, example: 100023336956 }
        display_address: { type: string, example: "10 Downing Street, London, SW1A 2AA" }
        postcode: { type: string, example: "SW1A 2AA" }

    PropertyRecord:
      type: object
      properties:
        uprn: { type: integer }
        display_address: { type: string }
        postcode: { type: string }
        property_type: { type: string, example: "Terraced" }
        bedrooms: { type: integer }
        bathrooms: { type: integer }
        floor_area_sqm: { type: number }
        epc_rating: { type: string, example: "D" }
        construction_age_band: { type: string }
        tenure: { type: string, example: "Freehold" }
        lat: { type: number }
        lng: { type: number }

    ValuationResponse:
      type: object
      properties:
        uprn: { type: integer }
        type: { type: string, enum: [sale, rent] }
        estimate_gbp: { type: integer, example: 425000 }
        range_low_gbp: { type: integer, example: 382000 }
        range_high_gbp: { type: integer, example: 467000 }
        confidence:
          type: object
          properties:
            level: { type: string, enum: [high, medium, low] }
            label: { type: string }
            comparable_count: { type: integer }
            reasons: { type: array, items: { type: string } }
        method:
          type: string
          enum: [listing-based, price-per-sqft, weighted-comparable-average]
        active_listings: { type: array, items: { type: object } }
        sold_comparables: { type: array, items: { type: object } }
        area_stats: { type: object }
        subject: { type: object }

    RiskResponse:
      type: object
      properties:
        uprn: { type: integer }
        risk_type: { type: string }
        level: { type: string, enum: [Low, Medium, High, "Very High"] }
        score: { type: number }

    ErrorResponse:
      type: object
      properties:
        detail: { type: string }
        code: { type: string }

paths:

  /address/find/:
    get:
      operationId: addressFind
      summary: Address autocomplete search
      description: >
        Full-text UK address search. Returns matched addresses with UPRNs.
        Costs 2 API calls (weighted billing).
      tags: [Address]
      parameters:
        - name: query
          in: query
          required: true
          schema: { type: string }
          example: "10 Downing Street"
          description: Street address, postcode, or place name
      responses:
        "200":
          description: Array of address suggestions
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/AddressSuggestion"
        "400":
          description: Missing query parameter
          content:
            application/json:
              schema: { $ref: "#/components/schemas/ErrorResponse" }

  /address/retrieve/{uprn}/:
    get:
      operationId: addressRetrieve
      summary: Enriched property record for a UPRN
      description: >
        Full address enriched with EPC, bedrooms, energy rating, and environmental risks.
        Costs 5 API calls.
      tags: [Address]
      parameters:
        - name: uprn
          in: path
          required: true
          schema: { type: integer }
          example: 100023336956
          description: Unique Property Reference Number (12 digits)
      responses:
        "200":
          description: Enriched address record
          content:
            application/json:
              schema: { $ref: "#/components/schemas/PropertyRecord" }

  /address/postcode/{postcode}/:
    get:
      operationId: addressPostcode
      summary: All addresses at a postcode
      description: >
        Returns all registered addresses at a postcode with UPRNs.
        Costs 2 API calls.
      tags: [Address]
      parameters:
        - name: postcode
          in: path
          required: true
          schema: { type: string }
          example: "SW1A2AA"
          description: UK postcode (spaces optional)
      responses:
        "200":
          description: List of addresses
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/AddressSuggestion"

  /properties/{uprn}/:
    get:
      operationId: propertyRecord
      summary: Core property record
      description: >
        Foundational property record keyed by UPRN. Returns ~40-60 core fields.
        Costs 1 API call.
      tags: [Property]
      parameters:
        - name: uprn
          in: path
          required: true
          schema: { type: integer }
          example: 100023336956
      responses:
        "200":
          description: Property record
          content:
            application/json:
              schema: { $ref: "#/components/schemas/PropertyRecord" }

  /properties/{uprn}/rooms/:
    get:
      operationId: propertyRooms
      summary: Room counts (bedrooms, bathrooms, habitable rooms)
      tags: [Property]
      parameters:
        - name: uprn
          in: path
          required: true
          schema: { type: integer }
      responses:
        "200":
          description: Room data
          content:
            application/json:
              schema:
                type: object
                properties:
                  bedrooms: { type: integer }
                  bathrooms: { type: integer }
                  habitable_rooms: { type: integer }
                  heated_rooms: { type: integer }

  /properties/{uprn}/garden/:
    get:
      operationId: propertyGarden
      summary: Garden presence and type
      tags: [Property]
      parameters:
        - name: uprn
          in: path
          required: true
          schema: { type: integer }
      responses:
        "200":
          description: Garden data
          content:
            application/json:
              schema: { type: object }

  /properties/{uprn}/parking/:
    get:
      operationId: propertyParking
      summary: Parking type (driveway, garage, off-street, etc.)
      tags: [Property]
      parameters:
        - name: uprn
          in: path
          required: true
          schema: { type: integer }
      responses:
        "200":
          description: Parking data
          content:
            application/json:
              schema: { type: object }

  /properties/{uprn}/dimensions/:
    get:
      operationId: propertyDimensions
      summary: Geometry area, building height, floor area
      tags: [Property]
      parameters:
        - name: uprn
          in: path
          required: true
          schema: { type: integer }
      responses:
        "200":
          description: Dimension data
          content:
            application/json:
              schema: { type: object }

  /risks/{risk_type}/:
    get:
      operationId: environmentalRisks
      summary: Environmental risk scores
      description: >
        Flood, radon, noise, landfill, coal mining, invasive plants, or air quality.
        Costs 1 API call.
      tags: [Risk]
      parameters:
        - name: risk_type
          in: path
          required: true
          schema:
            type: string
            enum: [flood, radon, noise, landfill, coal_mining, invasive_plants, air_quality_today, all]
          example: flood
        - name: uprn
          in: query
          required: true
          schema: { type: integer }
          example: 100023336956
      responses:
        "200":
          description: Risk scores
          content:
            application/json:
              schema: { $ref: "#/components/schemas/RiskResponse" }

  /flood-risk/:
    get:
      operationId: floodRisk
      summary: Dedicated flood risk endpoint (NaFRA2)
      description: >
        River/sea and surface water flood risk scores with Low/Medium/High/Very High labels.
        Local PostGIS query — no external round-trips. Costs 1 API call.
      tags: [Risk]
      parameters:
        - name: uprn
          in: query
          required: true
          schema: { type: integer }
      responses:
        "200":
          description: Flood risk scores
          content:
            application/json:
              schema: { type: object }

  /deprivation/:
    get:
      operationId: deprivation
      summary: Index of Multiple Deprivation scores
      description: >
        IMD income, employment, education, health, crime, housing, environment scores.
        Costs 1 API call.
      tags: [Area]
      parameters:
        - name: postcode
          in: query
          required: true
          schema: { type: string }
          example: "E1 6AN"
      responses:
        "200":
          description: Deprivation data
          content:
            application/json:
              schema: { type: object }

  /demographics/:
    get:
      operationId: demographics
      summary: ONS Census 2021 demographics (MSOA level)
      description: >
        Population, tenure breakdown, age bands, ethnicity, occupation, household size,
        car ownership. Resolved to the postcode's MSOA (~7,500 residents).
        Costs 1 API call.
      tags: [Area]
      parameters:
        - name: postcode
          in: query
          required: true
          schema: { type: string }
      responses:
        "200":
          description: Demographics data
          content:
            application/json:
              schema: { type: object }

  /postcode-profile/:
    get:
      operationId: postcodeProfile
      summary: All-in-one postcode summary
      description: >
        Deprivation index, crime rate, average property price, school count,
        transport links, and broadband speed in a single call. Costs 1 API call.
      tags: [Area]
      parameters:
        - name: postcode
          in: query
          required: true
          schema: { type: string }
          example: "SW1A 2AA"
      responses:
        "200":
          description: Postcode profile
          content:
            application/json:
              schema: { type: object }

  /price_trends/{outcode}/:
    get:
      operationId: priceTrends
      summary: Average property prices over time for an outcode
      tags: [Market]
      parameters:
        - name: outcode
          in: path
          required: true
          schema: { type: string }
          example: "SW1A"
      responses:
        "200":
          description: Price trend data
          content:
            application/json:
              schema: { type: object }

  /price_distributions/{outcode}/:
    get:
      operationId: priceDistributions
      summary: Property price distribution for an outcode
      description: Percentiles, median, and count by property type. Costs 1 API call.
      tags: [Market]
      parameters:
        - name: outcode
          in: path
          required: true
          schema: { type: string }
          example: "E1"
      responses:
        "200":
          description: Price distribution
          content:
            application/json:
              schema: { type: object }

  /price-growth/{outcode}/:
    get:
      operationId: priceGrowth
      summary: Capital appreciation data for an outcode
      description: Annual growth rate, 1Y/3Y/5Y/10Y returns. Costs 1 API call.
      tags: [Market]
      parameters:
        - name: outcode
          in: path
          required: true
          schema: { type: string }
          example: "SW1A"
      responses:
        "200":
          description: Price growth data
          content:
            application/json:
              schema: { type: object }

  /comparables/{uprn}/:
    get:
      operationId: comparables
      summary: Comparable properties for valuation
      description: >
        PostGIS spatial query returning similar properties by area, size, and type.
        Costs 10 API calls. First call may take 3–12s; subsequent calls are cached.
      tags: [Market]
      parameters:
        - name: uprn
          in: path
          required: true
          schema: { type: integer }
      responses:
        "200":
          description: Comparable properties list
          content:
            application/json:
              schema: { type: object }

  /valuations/estimate/:
    get:
      operationId: valuationsEstimate
      summary: Automated property valuation (AVM)
      description: >
        Estimated sale or rental value. Three-tier method: listing-based (active listing),
        price-per-sqft (sold comparables), or weighted-comparable-average (fallback).
        bedrooms and property_type are required and validated before billing.
        Costs 5 API calls flat. Note: currently marked as "Coming soon" — under repair.
      tags: [Market]
      parameters:
        - name: uprn
          in: query
          required: true
          schema: { type: integer }
          example: 100023336956
        - name: bedrooms
          in: query
          required: true
          schema: { type: integer, minimum: 1 }
          example: 3
        - name: property_type
          in: query
          required: true
          schema:
            type: string
            enum: [Bungalow, Detached, Flat, Maisonette, Semi-Detached, Terraced]
          example: Terraced
        - name: type
          in: query
          required: false
          schema:
            type: string
            enum: [sale, rent]
            default: sale
      responses:
        "200":
          description: Valuation estimate
          content:
            application/json:
              schema: { $ref: "#/components/schemas/ValuationResponse" }
        "400":
          description: Missing or invalid required parameters (not billed)
          content:
            application/json:
              schema: { $ref: "#/components/schemas/ErrorResponse" }

  /live-listings/search/:
    get:
      operationId: liveListingsSearch
      summary: Search current UK property listings
      description: >
        Current sale and rental listings with price, bedrooms, status, and agent.
        Billing: 1 call per listing returned (default page_size=30 = 30 calls).
        An empty page still costs 1 call.
      tags: [Listings]
      parameters:
        - name: boundary_id
          in: query
          schema: { type: integer }
          example: 14624
          description: Area filter — use /boundaries/autocomplete/ to find IDs
        - name: transaction_type
          in: query
          schema: { type: string, enum: [Sale, Rental] }
        - name: bedrooms
          in: query
          schema: { type: integer }
          description: Minimum bedrooms
        - name: max_bedrooms
          in: query
          schema: { type: integer }
        - name: min_price
          in: query
          schema: { type: integer }
        - name: max_price
          in: query
          schema: { type: integer }
        - name: property_type
          in: query
          schema: { type: string }
          example: "Detached,Flat"
        - name: status
          in: query
          schema: { type: string }
          example: "For sale,Sold STC"
        - name: page_size
          in: query
          schema: { type: integer, default: 30, maximum: 200 }
        - name: page
          in: query
          schema: { type: integer, default: 1 }
        - name: sort
          in: query
          schema:
            type: string
            enum: ["-added_date", "added_date", "-latest_price", "latest_price"]
            default: "-added_date"
      responses:
        "200":
          description: Listings results
          content:
            application/json:
              schema:
                type: object
                properties:
                  count: { type: integer }
                  results:
                    type: array
                    items:
                      type: object
                      properties:
                        id: { type: string, format: uuid }
                        display_address: { type: string }
                        latest_price: { type: integer }
                        bedrooms: { type: integer }
                        transaction_type: { type: string }
                        status: { type: string }
                        added_date: { type: string, format: date }
                        agent_name: { type: string }

  /listing-address/:
    post:
      operationId: listingAddressUnlock
      summary: Unlock UPRN and full address for a listing
      description: >
        Reveal the UPRN and full address for a live listing. £0.20 (20 credits) per
        first reveal. Re-fetches are free (charged: false in response).
      tags: [Listings]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [listing_id]
              properties:
                listing_id:
                  type: string
                  format: uuid
                  example: "d0be1095-b7a3-46e8-a4eb-6d93b3dc61c9"
      responses:
        "200":
          description: Unlocked listing data
          content:
            application/json:
              schema:
                type: object
                properties:
                  uprn: { type: integer }
                  address: { type: string }
                  charged: { type: boolean }

  /property_listings/:
    get:
      operationId: propertyListings
      summary: All historic listings for a UPRN
      description: >
        Every listing ever created for a UPRN — sale and rental going back as far as data exists.
        Cost: £0.20 (20 credits) per request regardless of listing count.
      tags: [Listings]
      parameters:
        - name: uprn
          in: query
          required: true
          schema: { type: integer }
      responses:
        "200":
          description: Historic listings
          content:
            application/json:
              schema: { type: object }

  /lr_sales/:
    get:
      operationId: lrSales
      summary: HMLR-confirmed sale completions
      description: >
        Land Registry confirmed transactions. Pass UPRN for all sales on a property,
        or sale ID for a single row. Returns txid, sold_let_date, sold_let_price.
        Costs 5 API calls flat.
      tags: [Market]
      parameters:
        - name: uprn
          in: query
          schema: { type: integer }
        - name: sale
          in: query
          schema: { type: integer }
          description: Sale ID from a listing's sale field
      responses:
        "200":
          description: Land Registry sale records
          content:
            application/json:
              schema: { type: object }

  /planning/search/:
    get:
      operationId: planningSearch
      summary: Planning applications near a location
      description: >
        National planning applications from 417 LPAs via PlanIt.
        Use postcode or lat/lng. Costs 1 API call.
      tags: [Planning]
      parameters:
        - name: postcode
          in: query
          schema: { type: string }
          example: "SW1A 2AA"
        - name: lat
          in: query
          schema: { type: number }
          example: 51.5032
        - name: lng
          in: query
          schema: { type: number }
          example: -0.1271
        - name: radius_km
          in: query
          schema: { type: number, default: 0.5, maximum: 5 }
        - name: days
          in: query
          schema: { type: integer, default: 90, maximum: 365 }
        - name: type
          in: query
          schema: { type: string }
          example: "Full"
        - name: status
          in: query
          schema: { type: string }
          example: "Approved"
      responses:
        "200":
          description: Planning applications
          content:
            application/json:
              schema: { type: object }

  /schools/nearby:
    get:
      operationId: schoolsNearby
      summary: Schools near a postcode (Ofsted ratings, DfE GIAS)
      description: >
        ~27,000 open schools in England. Returns Ofsted rating, phase, pupil numbers,
        and distance. Requires Starter+ plan. Costs 1 API call.
      tags: [Area]
      parameters:
        - name: postcode
          in: query
          required: true
          schema: { type: string }
          example: "SW1A 2AA"
        - name: radius
          in: query
          schema: { type: number, default: 0.5 }
          description: Radius in miles (0.1–5.0)
        - name: phase
          in: query
          schema:
            type: string
            enum: [Primary, Secondary, All-through, Nursery, "16 plus"]
        - name: ofsted
          in: query
          schema:
            type: string
            enum: [Outstanding, Good, "Requires Improvement", Inadequate]
        - name: limit
          in: query
          schema: { type: integer, default: 20, maximum: 50 }
      responses:
        "200":
          description: Nearby schools
          content:
            application/json:
              schema: { type: object }

  /broadband/:
    get:
      operationId: broadband
      summary: Ofcom broadband speed data for a postcode
      description: >
        Average/max download+upload speeds, superfast/ultrafast/gigabit coverage %.
        Note: limited coverage — returns 404 for many UK postcodes.
        Costs 1 API call.
      tags: [Area]
      parameters:
        - name: postcode
          in: query
          required: true
          schema: { type: string }
          example: "SW1A 2AA"
      responses:
        "200":
          description: Broadband data
          content:
            application/json:
              schema: { type: object }

  /crime/:
    get:
      operationId: crime
      summary: Crime statistics near a location
      description: >
        14 crime categories from Police UK. Monthly counts and category breakdown.
        Costs 1 API call.
      tags: [Area]
      parameters:
        - name: postcode
          in: query
          schema: { type: string }
          example: "SW1A 2AA"
        - name: lat
          in: query
          schema: { type: number }
        - name: lng
          in: query
          schema: { type: number }
        - name: date
          in: query
          schema: { type: string }
          example: "2025-12"
          description: YYYY-MM format — defaults to latest available
        - name: category
          in: query
          schema: { type: string }
          example: "burglary"
      responses:
        "200":
          description: Crime data
          content:
            application/json:
              schema: { type: object }

  /solar-assessment/{uprn}/:
    get:
      operationId: solarAssessment
      summary: Solar PV potential for a property
      description: >
        Roof area, estimated generation (kWh/yr), financial savings, payback period,
        and CO2 offset. Costs 5 API calls.
      tags: [Property]
      parameters:
        - name: uprn
          in: path
          required: true
          schema: { type: integer }
      responses:
        "200":
          description: Solar assessment
          content:
            application/json:
              schema: { type: object }

  /listed-buildings/:
    get:
      operationId: listedBuildings
      summary: Historic England listed buildings near a postcode
      description: >
        Grade I / II* / II entries with name, location, list date, and Historic England link.
        Costs 1 API call.
      tags: [Planning]
      parameters:
        - name: postcode
          in: query
          required: true
          schema: { type: string }
        - name: radius_km
          in: query
          schema: { type: number, default: 1, maximum: 5 }
        - name: grade
          in: query
          schema:
            type: string
            enum: ["I", "II*", "II"]
      responses:
        "200":
          description: Listed buildings
          content:
            application/json:
              schema: { type: object }

  /boundaries/autocomplete/:
    get:
      operationId: boundariesAutocomplete
      summary: Search UK administrative boundaries by name
      description: >
        Returns boundary IDs for use with the Live Listings endpoint.
        Free — no auth required, costs 0 calls.
      tags: [Area]
      security: []
      parameters:
        - name: q
          in: query
          required: true
          schema: { type: string }
          example: "Hackney"
      responses:
        "200":
          description: Boundary suggestions
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    id: { type: integer }
                    name: { type: string }

  /calculators/stamp-duty/:
    get:
      operationId: calcStampDuty
      summary: Stamp Duty Land Tax calculator (England/NI)
      description: >
        Total tax, effective rate, and band-by-band breakdown for standard/first-time/additional buyers.
        Free — costs 0 calls, counts zero against quota.
      tags: [Calculators]
      security: []
      parameters:
        - name: price
          in: query
          required: true
          schema: { type: integer }
          example: 350000
        - name: buyer_type
          in: query
          required: true
          schema:
            type: string
            enum: [standard, first_time, additional]
          example: standard
        - name: country
          in: query
          schema: { type: string, enum: [england], default: england }
      responses:
        "200":
          description: Stamp duty calculation
          content:
            application/json:
              schema: { type: object }

  /calculators/mortgage/:
    get:
      operationId: calcMortgage
      summary: Mortgage repayment calculator
      description: >
        Monthly payment, total repayment, total interest, LTV and LTI ratios.
        Free — costs 0 calls.
      tags: [Calculators]
      security: []
      parameters:
        - name: price
          in: query
          required: true
          schema: { type: integer }
          example: 350000
        - name: deposit
          in: query
          required: true
          schema: { type: integer }
          example: 35000
        - name: rate
          in: query
          required: true
          schema: { type: number }
          example: 4.5
          description: Annual interest rate as percentage
        - name: term_years
          in: query
          required: true
          schema: { type: integer }
          example: 25
      responses:
        "200":
          description: Mortgage calculation
          content:
            application/json:
              schema: { type: object }

  /council_tax_band/:
    get:
      operationId: councilTaxBand
      summary: Council tax band (A–H) for a UK property
      description: >
        Looks up the HMRC Valuation Office Agency. England and Wales only.
        Uses postcode + building number — not UPRN.
        Note: currently marked "Coming soon" — endpoint timing out on upstream provider.
        Costs 1 API call.
      tags: [Property]
      parameters:
        - name: postcode
          in: query
          required: true
          schema: { type: string }
          example: "SW1A 2AA"
        - name: building_number
          in: query
          required: true
          schema: { type: string }
          example: "10"
        - name: building_name
          in: query
          schema: { type: string }
          example: "Riverside House"
      responses:
        "200":
          description: Council tax band
          content:
            application/json:
              schema:
                type: object
                properties:
                  band: { type: string, example: "D" }
                  address: { type: string }

tags:
  - name: Address
    description: Address search, lookup, and postcode resolution
  - name: Property
    description: Property attributes, rooms, garden, parking, construction
  - name: Risk
    description: Environmental risk scores (flood, radon, noise, etc.)
  - name: Area
    description: Area intelligence — schools, crime, demographics, broadband
  - name: Market
    description: Market data — prices, trends, comparables, valuations
  - name: Listings
    description: Live and historic property listings
  - name: Planning
    description: Planning applications and listed buildings
  - name: Calculators
    description: Free calculators — stamp duty, mortgage
