UK Land Registry Sold Prices Scraper avatar

UK Land Registry Sold Prices Scraper

Pricing

Pay per event

Go to Apify Store
UK Land Registry Sold Prices Scraper

UK Land Registry Sold Prices Scraper

Pull HM Land Registry Price Paid Data — every registered property sale in England and Wales — as flat, spreadsheet-ready rows. We flatten the nested linked-data envelope, convert dates to ISO-8601, and split addresses into BS7666 components. Filter by postcode, town, district, county, price or date.

Pricing

Pay per event

Rating

0.0

(0)

Developer

DevilScrapes

DevilScrapes

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

a day ago

Last modified

Categories

Share


🎯 What this 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.items nesting, {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 maxItems cap 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-After honoured.
  • 🧩 Address components kept intactsaon/locality stay 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.

⚙️ How to use it

  1. Click Try for free at the top of the page.
  2. Fill in the input form — most fields have sensible defaults.
  3. Click Start. Output streams into the run's dataset.
  4. Export from Storage → Dataset as JSON, CSV, or Excel — or fetch via the API.

📥 Input

FieldTypeRequiredDefaultNotes
postcodestringno'M1 1AE'UK postcode to filter sales by, e.g. M1 1AE. Matched against propertyAddress.postcode on the Land…
townstringno'—'Town name to filter sales by, e.g. MANCHESTER. Matched against propertyAddress.town.
districtstringno'—'District name to filter sales by, e.g. LEEDS. Matched against propertyAddress.district.
countystringno'—'County name to filter sales by, e.g. GREATER MANCHESTER. Matched against propertyAddress.county.
minPricePaidintegerno'—'Only include sales at or above this price, in GBP.
maxPricePaidintegerno'—'Only include sales at or below this price, in GBP.
minTransactionDatestringno'—'Earliest sale date to include (inclusive), ISO YYYY-MM-DD.
maxTransactionDatestringno'—'Latest sale date to include (inclusive), ISO YYYY-MM-DD.
sortstringno'-transactionDate'Land Registry _sort value, e.g. -transactionDate for newest first or transactionDate for…
maxItemsintegerno1000Stop after this many sold-price rows have been collected across all pages.
proxyConfigurationobjectno{'useApifyProxy': True}Apify Proxy configuration. The Land Registry endpoint is public and keyless with no anti-bot layer, but proxy rotation…

Example input

{
"postcode": "M1 1AE",
"maxItems": 5,
"sort": "-transactionDate",
"proxyConfiguration": {
"useApifyProxy": true
}
}

📤 Output

Every row is one dataset item.

FieldTypeNotes
transaction_idstringLand Registry transaction UUID.
transaction_urlstringAbsolute Land Registry transaction URI, from hasTransaction.
price_paid_gbpintegerSale price in GBP, as recorded by Land Registry.
transaction_datestringSale date, ISO-8601 YYYY-MM-DD, converted from Land Registry's day-name date string.
property_typestringFlattened property type label, e.g. 'Flat-maisonette'.
estate_typestringFlattened estate type label, e.g. 'Leasehold' or 'Freehold'.
new_buildbooleanWhether Land Registry recorded this sale as a new build.
transaction_categorystringFlattened transaction category label (standard price paid entry vs additional price paid entry).
record_statusstringFlattened record status label — 'Add' for a new entry, or a change/delete marker.
paonstringPrimary addressable object name — usually the house number or name.
saonstringSecondary addressable object name — flat or unit number, when present.
streetstringStreet name.
localitystringLocality, when Land Registry recorded one.
townstringTown/post-town.
districtstringDistrict.
countystringCounty.
postcodestringPostcode.
addressstringSingle-line human-readable address assembled from the individual BS7666 components.
scraped_atstringWhen this row was recorded (ISO-8601 UTC).

Example output

{
"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

Pay-Per-Event — you pay only when these events fire:

EventUSDWhat it is
actor-start$0.05One-off warm-up charge per run
transaction-scraped$0.005PPE event

Example: 1 000 results at the rates above ≈ $5.05. No subscription, no minimum, no card to start — Apify gives every new account $5 of free credit.

🚧 Limitations

  • Residential Price Paid Data only — no commercial-property special handling beyond passing the transaction_category label 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?

Yes — HM Land Registry publishes Price Paid Data under the Open Government Licence specifically for reuse, and this Actor only queries the public, keyless API endpoint Land Registry already exposes.

Why did my search return zero rows?

A narrow filter combination — a postcode or date range with genuinely no sales — legitimately returns zero rows. The run still succeeds; check the status message for what was searched.

What does saon being empty mean?

Most properties have no secondary addressable object (flat/unit number) — saon and locality are optional fields that Land Registry itself frequently leaves blank.

Can I filter by multiple locations in one run?

Each run takes one set of location filters (postcode/town/district/county). Run it multiple times, or chain runs via the Apify API, to cover multiple areas.

💬 Your feedback

Spotted a bug, hit a weird edge case, or need a new field? Open an issue on the Actor's Issues tab on Apify Console — we ship fixes weekly and we read every report.