Real estate
UK Land Registry Sold Prices Scraper
Flat, spreadsheet-ready HM Land Registry Price Paid Data — ISO dates, split BS7666 addresses, filtered by postcode/town/district/county/price/date — instead of the raw nested linked-data envelope.
Free Apify credit covers a first run. No credit card to try.
What this Actor scrapes
HM Land Registry's Price Paid Data is the official record of every residential property sale registered in England and Wales since 1995, and it is free and keyless — but the public API serves it as a deeply nested linked-data envelope: rows sit three levels down at result.items, categorical fields like property type and estate type arrive as {label:[{_value:...}]} objects instead of plain strings, and the sale date ships as a non-ISO string like Thu, 29 Jul 2004 instead of 2004-07-29. This Actor queries the endpoint on your behalf — filtered by postcode, town, district, county, price band, and transaction-date range — walks every page, and emits one flat row per sale: a plain price, an ISO-8601 date, flattened category labels, and the address split into its individual BS7666 components (house number, street, locality, town, district, county, postcode) plus a single-line assembled address. What normally means writing your own linked-data parser becomes a filtered dataset export.
What we handle for you
- 🧹 Envelope flattening — the linked-data
result.itemsnesting,{label:[{_value}]}category objects, and non-ISO date strings are unpacked into a flat, typed row. - 📄 Full pagination — we walk every page up to your
maxItemscap so a broad postcode or district search doesn't stop at page one. - 🔁 Retries with exponential backoff on
408 / 429 / 5xx— up to 5 attempts per page,Retry-Afterhonoured. - 🧩 Address components kept intact —
saon/localitystay nullable rather than silently collapsing sparse addresses into a schema-validation failure. - 🧊 Clean, typed dataset rows — Pydantic-validated, ISO-8601 dates, ready for CSV/JSON/Excel export straight from the Apify Console.
- 💰 Pay-Per-Event pricing — you only pay for sale rows that land in your dataset. No data, no charge beyond the warm-up fee.
Use cases
- Comparables — pull every recent sale on a street or postcode to price a listing or valuation.
- Market research — track sale-price trends for a town, district, or county over a date range.
- Proptech data feeds — ingest a clean, typed sold-price dataset instead of hand-rolling a linked-data parser.
- Investor due diligence — size a postcode's price band and turnover before making an offer.
Input
Paste this into the Apify Console, or send it as the run input over the API. Proxy settings are on by default; you rarely need to touch them.
| Field | Type | Required | What it does |
|---|---|---|---|
postcode | string | no | UK postcode to filter sales by, e.g. M1 1AE. Matched against `propertyAddress.postcode` on the Land Registry API. |
town | string | no | Town name to filter sales by, e.g. MANCHESTER. Matched against `propertyAddress.town`. |
district | string | no | District name to filter sales by, e.g. LEEDS. Matched against `propertyAddress.district`. |
county | string | no | County name to filter sales by, e.g. GREATER MANCHESTER. Matched against `propertyAddress.county`. |
minPricePaid | integer | no | Only include sales at or above this price, in GBP. |
maxPricePaid | integer | no | Only include sales at or below this price, in GBP. |
minTransactionDate | string | no | Earliest sale date to include (inclusive), ISO YYYY-MM-DD. |
maxTransactionDate | string | no | Latest sale date to include (inclusive), ISO YYYY-MM-DD. |
sort | string | no | Land Registry `_sort` value, e.g. -transactionDate for newest first or transactionDate for oldest first. |
maxItems | integer | no | Stop after this many sold-price rows have been collected across all pages. |
{
"postcode": "M1 1AE",
"maxItems": 5,
"sort": "-transactionDate",
"proxyConfiguration": {
"useApifyProxy": true
}
} Output
One row per result, schema-validated before it is written. Export JSON, CSV, Excel or XML from the run, or read it over the API.
transaction_idprice_paid_gbptransaction_dateproperty_typeestate_typenew_buildaddresspostcode
{
"transaction_id": "a1b2c3d4-0000-0000-0000-000000000000",
"transaction_url": "http://landregistry.data.gov.uk/data/ppi/transaction/a1b2c3d4-0000-0000-0000-000000000000",
"price_paid_gbp": 140950,
"transaction_date": "2004-07-29",
"property_type": "Flat-maisonette",
"estate_type": "Leasehold",
"new_build": false,
"transaction_category": "Standard Price Paid Transaction",
"record_status": "Add",
"paon": "12",
"saon": null,
"street": "EXAMPLE STREET",
"locality": null,
"town": "MANCHESTER",
"district": "MANCHESTER",
"county": "GREATER MANCHESTER",
"postcode": "M1 1AE",
"address": "12 Example Street, Manchester, M1 1AE",
"scraped_at": "2026-09-02T12:00:00+00:00"
} Pricing
| Event | Price | When |
|---|---|---|
| Actor start | $0.05 | Once per run, covers warm-up and proxy session setup. |
| Transaction scraped | $0.0050 | Per result written to the dataset. |
You pay only for results that land. Cap any run with maxTotalChargeUsd. See pricing & billing for worked examples.
Limitations
- Residential Price Paid Data only — no commercial-property special handling beyond passing the
transaction_categorylabel through as published. - No geocoding or lat/long enrichment — addresses are the BS7666 components Land Registry publishes, not resolved coordinates.
- Covers England and Wales only, matching HM Land Registry's own coverage — Scotland and Northern Ireland use separate registries not covered by this Actor.
FAQ
Is this legal?
Why did my search return zero rows?
What does `saon` being empty mean?
saon and locality are optional fields that Land Registry itself frequently leaves blank.Can I filter by multiple locations in one run?
Ready to run it?
Open the listing on Apify, paste the input above, and watch rows land. If it ever breaks, it is our problem before it is yours.
Related Actors