Skip to main content
29M UK properties, one API call Get your API key →

Code Examples

Copy-paste examples for Python, JavaScript, and PHP. Drop them into your project and make your first API call in minutes — no dependencies, no setup.

Copy-paste or download. All three clients are single-file wrappers — drop them straight into your project with zero build step. npm and PyPI packages planned.

All clients target the V1 API (https://api.homedata.co.uk/api) with namespaced resources, typed errors, and rate-limit tracking built in.

Zero to first call

The fastest way to make your first API call — no wrapper, no installation. All you need is your API key and the language you already know.

Requires requests. Install with pip install requests.

quickstart.py 10 lines
import requests

API_KEY = "your_api_key_here"
UPRN    = "100023336956"  # get UPRNs via /address/find

response = requests.get(
    f"https://api.homedata.co.uk/api/properties/{UPRN}",
    headers={"Authorization": "Api-Key YOUR_API_KEY"},
)

data = response.json()
print(data["property_type"], data["current_energy_efficiency"], data["last_sold_date"])
Find a property by address first
# Step 1: resolve address → UPRN (API key required)
resp = requests.get(
    "https://api.homedata.co.uk/api/address/find",
    headers={"Authorization": "Api-Key YOUR_API_KEY"},
    params={"q": "10 Downing Street London"},
)
suggestions = resp.json()["suggestions"]
uprn = suggestions[0]["uprn"]

Uses the native fetch API — no dependencies, works in Node.js 18+ and all modern browsers.

quickstart.js 10 lines
const API_KEY = 'your_api_key_here';
const UPRN    = '100023336956';  // get UPRNs via /address/find

const response = await fetch(
  `https://api.homedata.co.uk/api/properties/${UPRN}`,
  { headers: { 'Authorization': `Api-Key ${API_KEY}` } }
);

const property = await response.json();
console.log(property.property_type, property.current_energy_efficiency, property.last_sold_date);
Find a property by address first
// Step 1: resolve address → UPRN (API key required)
const res = await fetch(
  'https://api.homedata.co.uk/api/address/find?q=10+Downing+Street+London',
  { headers: { 'Authorization': 'Api-Key YOUR_API_KEY' } }
);
const { suggestions } = await res.json();
const uprn = suggestions[0].uprn;

npm client planned

The homedata REST client is not published on npm yet. (The MCP server is: npx -y homedata-mcp — see the MCP page.) In the meantime, copy homedata.js from the JavaScript tab below — it's a single-file zero-dependency wrapper that drops straight into any project.

quickstart.ts homedata v1.0 — TypeScript + JavaScript
import Homedata from 'homedata';

// Initialise once — all methods use the key automatically
const client = new Homedata('hd_your_api_key');

// Search addresses
const { data: addresses } = await client.address.find('10 Downing Street');
const uprn = addresses[0].uprn;  // 100023336956

// Get full property record
const { data: property } = await client.properties.get(uprn);
console.log(property.address, property.bedrooms + ' bed', property.epc_rating);

// Nearby schools
const { data: schools } = await client.schools.nearby({ postcode: 'SW1A 2AA', phase: 'Primary' });
schools.forEach(s => console.log(s.name, s.ofsted.rating, `${s.distance_miles}mi`));


// Broadband speeds
const { data: broadband } = await client.broadband.get('SW1A 2AA');
console.log(`${broadband.avg_download_speed} Mbps down / ${broadband.avg_upload_speed} Mbps up`);

Full API surface — v1.0

Method Description
client.address.find(query)Address autocomplete
client.address.retrieve(uprn)Enriched property by UPRN
client.properties.get(uprn)Full property record
client.schools.nearby(opts)Schools by postcode + radius
client.broadband.get(postcode)Broadband speeds
client.demographics.area(opts)Area demographics + deprivation
client.priceGrowth.get(outcode)Price growth by outcode
client.postcodeProfile.get(pc)5 datasets in one call

TypeScript, fully typed, zero dependencies beyond node-fetch (Node 18+ ships it natively).


Full client libraries

Complete wrappers. Python and JavaScript expose all 7 V1 resource namespaces; PHP covers the same surfaces as flat methods. Copy the single file directly into your project — no build step, no dependencies beyond your language's HTTP library. npm and PyPI packages planned.

Python Client

Requires Python 3.9+ and the requests library.

PyPI client planned

pip install homedata is not yet available. (pip install homedata-mcp is — that's the MCP server, see the MCP page.) Copy the class below instead — save it as homedata.py and import it directly. One file, no build step.

homedata.py copy-paste — pip package planned
"""Homedata V1 API client — namespaced resources, zero deps beyond requests."""
import requests

BASE_URL = "https://api.homedata.co.uk/api"


class HomedataError(Exception):
    def __init__(self, message: str, status: int, code: str):
        super().__init__(message)
        self.status = status
        self.code = code


class _Resource:
    def __init__(self, client):
        self._client = client

    def _get(self, path: str, params: dict = None) -> dict:
        return self._client._request(path, params)


class AddressResource(_Resource):
    """Address search and UPRN retrieve."""

    def find(self, query: str, limit: int = 20) -> list:
        resp = self._get("/address/find", {"q": query, "limit": limit})
        return resp.get("suggestions", resp)

    def retrieve(self, uprn: int) -> dict:
        return self._get(f"/property/{uprn}/address")


class PropertiesResource(_Resource):
    def get(self, uprn: int) -> dict:
        return self._get(f"/properties/{uprn}")


class SchoolsResource(_Resource):
    def nearby(self, postcode: str = None, lat: float = None, lng: float = None,
               radius_km: float = 3, limit: int = 20, phase: str = None) -> list:
        params = {"radius_km": radius_km, "limit": limit}
        if postcode: params["postcode"] = postcode
        if lat: params["lat"] = lat
        if lng: params["lng"] = lng
        if phase: params["phase"] = phase
        resp = self._get("/schools/nearby", params)
        return resp.get("schools", resp)


class BroadbandResource(_Resource):
    def get(self, postcode: str) -> dict:
        return self._get("/broadband", {"postcode": postcode})


class DemographicsResource(_Resource):
    def area(self, postcode: str) -> dict:
        return self._get("/demographics", {"postcode": postcode})


class PriceGrowthResource(_Resource):
    def get(self, outcode: str) -> dict:
        return self._get(f"/price-growth/{outcode}")


class PostcodeProfileResource(_Resource):
    def get(self, postcode: str) -> dict:
        return self._get("/postcode-profile", {"postcode": postcode})


class Homedata:
    """Homedata V1 API client.

    Usage:
        client = Homedata("hd_your_api_key")
        results = client.address.find("10 Downing Street")
        prop    = client.properties.get(100023336956)
        schools = client.schools.nearby(postcode="SW1A 2AA")
    """

    def __init__(self, api_key: str, base_url: str = BASE_URL, timeout: int = 30):
        if not api_key:
            raise ValueError("API key required — get one at homedata.co.uk/register")
        self._base_url = base_url.rstrip("/")
        self._timeout = timeout
        self._session = requests.Session()
        self._session.headers.update({
            "Authorization": f"Api-Key {api_key}",
            "Accept": "application/json",
        })
        self.tokens = {"charged": 0, "balance": 0}

        # Resource namespaces
        self.address         = AddressResource(self)
        self.properties      = PropertiesResource(self)
        self.schools         = SchoolsResource(self)
        self.broadband       = BroadbandResource(self)
        self.demographics    = DemographicsResource(self)
        self.price_growth    = PriceGrowthResource(self)
        self.postcode_profile = PostcodeProfileResource(self)

    def _request(self, path: str, params: dict = None) -> dict:
        url = f"{self._base_url}{path}"
        clean = {k: v for k, v in (params or {}).items() if v is not None}
        resp = self._session.get(url, params=clean, timeout=self._timeout)
        self.tokens = {
            "charged": int(resp.headers.get("X-Tokens-Charged", 0)),
            "balance": int(resp.headers.get("X-Tokens-Balance", 0)),
        }
        if not resp.ok:
            body = resp.json() if resp.content else {}
            err  = body.get("error", {})
            raise HomedataError(
                err.get("message", f"HTTP {resp.status_code}"),
                resp.status_code,
                err.get("code", "UNKNOWN"),
            )
        return resp.json()

Usage Examples

Quick start
from homedata import Homedata, HomedataError

client = Homedata("hd_your_api_key")

# Address search
results = client.address.find("10 Downing Street")
for addr in results:
    print(f"{addr['address']}, {addr['postcode']} — UPRN: {addr['uprn']}")

# Full property record by UPRN
prop = client.properties.get(100023336956)
print(f"Type: {prop['property_type']}, EPC score: {prop['current_energy_efficiency']}")


# Nearby schools
schools = client.schools.nearby(postcode="SW1A 2AA", phase="Primary")
for s in schools:
    print(f"{s['name']} — Ofsted: {s['ofsted']['rating']}, {s['distance_miles']:.1f}mi")

# Rate limit tracking
print(f"{client.tokens['balance']} tokens left — this call cost {client.tokens['charged']}")

# Error handling
try:
    prop = client.properties.get(999)
except HomedataError as e:
    print(f"Error {e.status}: {e.code}")
Bulk property lookup
# Enrich a list of UPRNs you already hold
from homedata import Homedata
import time

client = Homedata("hd_your_api_key")

uprns = [100023336956, 10093609154]
for uprn in uprns:
    prop = client.properties.get(uprn)
    print(f"{uprn} {prop['property_type']} — EPC score: {prop.get('current_energy_efficiency', 'N/A')}")
    time.sleep(0.1)  # respect rate limits

# Full area profile in one call
profile = client.postcode_profile.get("SW1A 2AA")
print(profile.keys())  # deprivation, broadband, schools, transport, prices

JavaScript / TypeScript Client

Zero-dependency client for Node.js 18+ (uses native fetch) and modern browsers. Save as homedata.js or homedata.ts.

homedata.js copy-paste · npm planned
/** Homedata V1 API client — namespaced resources, zero dependencies. */

const BASE_URL = "https://api.homedata.co.uk/api";

export class HomedataError extends Error {
  constructor(message, status, code) {
    super(message);
    this.status = status;
    this.code = code;
  }
}

class _Resource {
  constructor(client) { this._client = client; }
  _get(path, params) { return this._client._request(path, params); }
}

class AddressResource extends _Resource {
  /** Search addresses. */
  find(query, { limit = 20 } = {}) {
    return this._get("/address/find", { q: query, limit })
      .then(r => r.suggestions ?? r);
  }
  /** Enriched property by UPRN. */
  retrieve(uprn) {
    return this._get(`/property/${uprn}/address`);
  }
}

class PropertiesResource extends _Resource {
  get(uprn) {
    return this._get(`/properties/${uprn}`);
  }
}


class SchoolsResource extends _Resource {
  nearby({ postcode, lat, lng, radius_km = 3, limit = 20, phase } = {}) {
    return this._get("/schools/nearby", { postcode, lat, lng, radius_km, limit, phase })
      .then(r => r.schools ?? r);
  }
}

class BroadbandResource extends _Resource {
  get(postcode) { return this._get("/broadband", { postcode }); }
}

class DemographicsResource extends _Resource {
  area({ postcode }) {
    return this._get("/demographics", { postcode });
  }
}

class PriceGrowthResource extends _Resource {
  get(outcode) { return this._get(`/price-growth/${outcode}`); }
}

class PostcodeProfileResource extends _Resource {
  get(postcode) { return this._get("/postcode-profile", { postcode }); }
}

export class Homedata {
  /**
   * @param {string} apiKey   Your Homedata API key
   * @param {{ baseUrl?: string, timeout?: number }} [opts]
   *
   * const client = new Homedata('hd_your_api_key');
   * const addresses = await client.address.find('10 Downing Street');
   * const property  = await client.properties.get(100023336956);
   * const schools   = await client.schools.nearby({ postcode: 'SW1A 2AA' });
   */
  constructor(apiKey, { baseUrl = BASE_URL, timeout = 30_000 } = {}) {
    if (!apiKey) throw new Error("API key required — get one at homedata.co.uk/register");
    this._baseUrl = baseUrl.replace(/\/$/, "");
    this._headers = { "Authorization": `Api-Key ${apiKey}`, "Accept": "application/json" };
    this._timeout = timeout;
    this.tokens = { charged: 0, balance: 0 };

    this.address         = new AddressResource(this);
    this.properties      = new PropertiesResource(this);
    this.schools         = new SchoolsResource(this);
    this.broadband       = new BroadbandResource(this);
    this.demographics    = new DemographicsResource(this);
    this.priceGrowth     = new PriceGrowthResource(this);
    this.postcodeProfile = new PostcodeProfileResource(this);
  }

  async _request(path, params = {}) {
    const url = new URL(`${this._baseUrl}${path}`);
    Object.entries(params).forEach(([k, v]) =>
      v !== undefined && v !== null && url.searchParams.set(k, v));

    const ctrl = new AbortController();
    const timer = setTimeout(() => ctrl.abort(), this._timeout);
    try {
      const res = await fetch(url.toString(), {
        headers: this._headers, signal: ctrl.signal
      });
      this.tokens = {
        charged: +res.headers.get("X-Tokens-Charged") || 0,
        balance: +res.headers.get("X-Tokens-Balance") || 0,
      };
      const body = await res.json();
      if (!res.ok) {
        const e = body?.error || {};
        throw new HomedataError(e.message || `HTTP ${res.status}`, res.status, e.code || "UNKNOWN");
      }
      return body;
    } finally { clearTimeout(timer); }
  }
}

export default Homedata;

Usage Examples

Quick start (Node.js)
import { Homedata, HomedataError } from "./homedata.js";

const client = new Homedata("hd_your_api_key");

// Address search
const addresses = await client.address.find("10 Downing Street");
addresses.forEach(a => console.log(`${a.address}, ${a.postcode} — UPRN: ${a.uprn}`));

// Full property record
const prop = await client.properties.get(100023336956);
console.log(`${prop.property_type} — EPC score: ${prop.current_energy_efficiency}`);


// Nearby schools — Ofsted ratings
const schools = await client.schools.nearby({ postcode: "SW1A 2AA", phase: "Primary" });
schools.forEach(s => console.log(`${s.name}: ${s.ofsted.rating} — ${s.distance_miles.toFixed(1)}mi`));

// Rate limit tracking
console.log(`${client.tokens.balance} tokens left — this call cost ${client.tokens.charged}`);

// Error handling
try {
  await client.properties.get(999);
} catch (e) {
  if (e instanceof HomedataError) {
    console.error(`Error ${e.status}: ${e.code}`);
  }
}
Express.js middleware example
// Property lookup API route
import express from "express";
import { Homedata } from "./homedata.js";

const app = express();
const hd = new Homedata(process.env.HOMEDATA_API_KEY);

app.get("/property/:uprn", async (req, res) => {
  try {
    const uprn = Number(req.params.uprn);
    const [prop, schools] = await Promise.all([
      hd.properties.get(uprn),
      hd.schools.nearby({ postcode: req.query.postcode }),
    ]);
    res.json({ property: prop, schools });
  } catch (e) {
    res.status(e.status || 500).json({ error: e.message });
  }
});

PHP Client

Requires PHP 8.0+ with curl extension (enabled by default). Save as Homedata.php.

Homedata.php PHP 8.0+
<?php
/** Homedata V1 API client — PHP 8.0+, cURL only. */

class HomedataError extends \RuntimeException
{
    public string $errorCode;
    public int $httpStatus;

    public function __construct(string $code, string $message, int $status)
    {
        $this->errorCode = $code;
        $this->httpStatus = $status;
        parent::__construct("{$code}: {$message}", $status);
    }
}

class Homedata
{
    private string $apiKey;
    private string $base;
    private int $timeout;
    public array $tokens = ['charged' => 0, 'balance' => 0];

    public function __construct(
        string $apiKey,
        string $baseUrl = 'https://api.homedata.co.uk/api',
        int $timeout = 30,
    ) {
        $this->apiKey  = $apiKey;
        $this->base    = rtrim($baseUrl, '/');
        $this->timeout = $timeout;
    }

    private function get(string $path, array $params = []): array
    {
        $url = $this->base . $path;
        if ($params) $url .= '?' . http_build_query(array_filter($params, fn($v) => $v !== null));

        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HEADER         => true,
            CURLOPT_HTTPHEADER     => [
                "Authorization: Api-Key {$this->apiKey}",
                'Accept: application/json',
            ],
            CURLOPT_TIMEOUT => $this->timeout,
        ]);
        $response   = curl_exec($ch);
        $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
        $status     = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $headers    = substr($response, 0, $headerSize);
        $body       = substr($response, $headerSize);
        curl_close($ch);

        // Track token spend
        preg_match('/X-Tokens-Charged:\s*(\d+)/i', $headers, $m);
        $this->tokens['charged'] = (int) ($m[1] ?? 0);
        preg_match('/X-Tokens-Balance:\s*(\d+)/i', $headers, $m);
        $this->tokens['balance'] = (int) ($m[1] ?? 0);

        $data = json_decode($body, true) ?: [];
        if ($status >= 400) {
            $err = $data['error'] ?? [];
            throw new HomedataError(
                $err['code'] ?? 'UNKNOWN',
                $err['message'] ?? $body,
                $status,
            );
        }
        return $data;
    }

    // ── Address ──
    public function addressFind(string $query, int $limit = 20): array
    {
        $r = $this->get('/address/find', ['q' => $query, 'limit' => $limit]);
        return $r['suggestions'] ?? $r;
    }

    public function addressRetrieve(int $uprn): array
    {
        return $this->get("/property/{$uprn}/address");
    }

    // ── Properties ──
    public function propertiesGet(int $uprn): array
    {
        return $this->get("/properties/{$uprn}");
    }

    // ── Schools ──
    public function schoolsNearby(array $params): array
    {
        $r = $this->get('/schools/nearby', $params);
        return $r['schools'] ?? $r;
    }

    // ── Broadband (free) ──
    public function broadband(string $postcode): array
    {
        return $this->get('/broadband', ['postcode' => $postcode]);
    }

    // ── Demographics ──
    public function demographics(string $postcode): array
    {
        return $this->get('/demographics', ['postcode' => $postcode]);
    }

    // ── Price Growth ──
    public function priceGrowth(string $outcode): array
    {
        return $this->get("/price-growth/{$outcode}");
    }

    // ── Postcode Profile (5 datasets in one call) ──
    public function postcodeProfile(string $postcode): array
    {
        return $this->get('/postcode-profile', ['postcode' => $postcode]);
    }
}

Usage Example

Laravel integration
// In a Laravel controller or service
$hd = new Homedata(config('services.homedata.key'));

// Address search
$addresses = $hd->addressFind('10 Downing Street');
$uprn = $addresses[0]['uprn'];

// Property + schools
$property = $hd->propertiesGet($uprn);
$schools = $hd->schoolsNearby(['postcode' => 'SW1A 2AA', 'phase' => 'Primary']);

// Token balance after these calls
$balance = $hd->tokens['balance'];

return view('property.show', compact('property', 'schools'));

Library Features

Feature Python JavaScript PHP
All 7 V1 resources covered ✓ ✓ ✓
TypeScript types — ✓ —
Typed error handling ✓ ✓ ✓
Rate limit tracking ✓ ✓ ✓
Zero external dependencies requests ✓ ✓
Configurable timeout ✓ ✓ ✓
Package manager install pip (soon) npm (soon) —
Single file, copy-paste ✓ ✓ ✓

Using Claude, Cursor, or Windsurf?

Skip the wrapper code entirely — our MCP server gives your AI tool direct access to all 16 endpoints. One config block, no code.

Prefer raw HTTP?

These wrappers are optional. The API is standard REST — any HTTP client works. See the Getting Started guide for cURL examples, or use the Interactive API Explorer to test endpoints in your browser.

Integrate into your own product

Pay as you go
Homedata API Scientific, granular measurements

The Homedata Python, JavaScript, and PHP clients are single-file wrappers with zero build steps — covering address lookup, property intelligence, EPC, risks, schools, and area demographics across 29M UK properties with typed errors and rate-limit tracking built in.

Structured as JSON · queryable by UPRN or postcode · ready to embed in any application

Exact measurements

Real values — distances, concentrations, counts — not rounded ratings

29 million UK properties covered

Every address queryable by UPRN or postcode

REST API

JSON responses, OpenAPI docs, sandbox — first call in under 5 minutes

Pay as you go: 100 tokens = £1 across every endpoint, no subscription needed. Bonus tokens with an optional monthly subscription, and your first top-up matched 100%. See pricing →