SDKs & Libraries
Lightweight client libraries for Python and JavaScript. Drop-in wrappers around the REST API — no dependencies beyond your language's HTTP library.
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://homedata.co.uk/api/v1) 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.
import requests API_KEY = "your_api_key_here" UPRN = "100023336956" # get UPRNs via /api/v1/address/find response = requests.get( f"https://homedata.co.uk/api/v1/properties/{UPRN}", headers={"Authorization": f"Api-Key {API_KEY}"}, ) data = response.json() print(data["address"], data["bedrooms"], "bed", data["property_type"])
# Step 1: resolve address → UPRN (2 calls, API key required) resp = requests.get( "https://homedata.co.uk/api/v1/address/find", headers={"Authorization": f"Api-Key {API_KEY}"}, params={"q": "10 Downing Street London"}, ) suggestions = resp.json()["data"] uprn = suggestions[0]["uprn"]
Full client libraries
Complete wrappers with all 8 V1 resource namespaces. 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 package planned
pip install homedata is not yet available. Copy the class below instead — save it as homedata.py and import it directly. One file, no build step.
"""Homedata V1 API client — namespaced resources, zero deps beyond requests.""" import requests BASE_URL = "https://homedata.co.uk/api/v1" 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 (2 calls) and UPRN retrieve (5 calls).""" def find(self, query: str, limit: int = 20) -> list: resp = self._get("/address/find", {"q": query, "limit": limit}) return resp.get("data", resp) def retrieve(self, uprn: int) -> dict: resp = self._get(f"/address/retrieve/{uprn}") return resp.get("data", resp) class PropertiesResource(_Resource): def search(self, postcode: str, page: int = 1) -> list: resp = self._get("/properties", {"postcode": postcode, "page": page}) return resp.get("data", resp) def get(self, uprn: int) -> dict: resp = self._get(f"/properties/{uprn}") return resp.get("data", resp) class ValuationsResource(_Resource): def estimate(self, uprn: int, type: str = "sale") -> dict: return self._get(f"/valuations/{uprn}", {"type": type}) class SchoolsResource(_Resource): def nearby(self, postcode: str = None, lat: float = None, lng: float = None, radius: float = 0.5, limit: int = 20, phase: str = None) -> list: params = {"radius": radius, "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 = None, lat: float = None, lng: float = None) -> dict: params = {} if postcode: params["postcode"] = postcode if lat: params["lat"] = lat if lng: params["lng"] = lng return self._get("/demographics", params) 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.rate_limit = {"limit": 0, "remaining": 0, "reset": 0} # Resource namespaces self.address = AddressResource(self) self.properties = PropertiesResource(self) self.valuations = ValuationsResource(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.rate_limit = { "limit": int(resp.headers.get("X-RateLimit-Limit", 0)), "remaining": int(resp.headers.get("X-RateLimit-Remaining", 0)), "reset": int(resp.headers.get("X-RateLimit-Reset", 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
from homedata import Homedata, HomedataError client = Homedata("hd_your_api_key") # Address search (2 calls) 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']}, Beds: {prop['bedrooms']}, EPC: {prop['epc_rating']}") # Automated valuation (Growth+) val = client.valuations.estimate(100023336956, type="sale") print(f"Estimate: £{val['estimate']:,} ({val['confidence']} confidence)") # 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.rate_limit['remaining']}/{client.rate_limit['limit']} calls remaining") # Error handling try: prop = client.properties.get(999) except HomedataError as e: print(f"Error {e.status}: {e.code}")
# Get properties from a postcode + enrich each one from homedata import Homedata import time client = Homedata("hd_your_api_key") # Search properties at a postcode properties = client.properties.search("SW1A 2AA") for summary in properties[:5]: uprn = summary["uprn"] prop = client.properties.get(uprn) val = client.valuations.estimate(uprn) print(f"{prop['address']} — EPC: {prop.get('epc_rating', 'N/A')}, AVM: £{val.get('estimate', 0):,}") 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
Library Features
| Feature | Python | JavaScript | PHP |
|---|---|---|---|
| 8 V1 resource namespaces | ✓ | ✓ | ✓ |
| 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.