---
title: "UK Sold House Price Data: What Listing Sites Add"
description: "Land Registry's Price Paid Data is the free record of sold prices. What Zoopla and Rightmove add (asking prices, agents, valuation history) and fields to keep."
url: https://perceptlayer.com/blog/uk-house-price-data-sold-prices
---

[Blog](https://perceptlayer.com/blog) / [Web data by industry](https://perceptlayer.com/blog/category/web-data)

# UK Sold House Price Data: What Listing Sites Add

29 Jul 2026 Perceptlayer 7 min read

For UK sold house prices, start with HM Land Registry's Price Paid Data: it is free, covers residential sales in England and Wales, and is the authoritative record of what was paid. Listing sites add what the register leaves out: the asking price and how it changed, the agent, rooms and size, and valuation history. The useful dataset joins the two, and since August 2026 Land Registry publishes a UPRN for new sales, which makes that join exact.

## Key takeaways

-   Price Paid Data gives the price, completion date, postcode, address, property type ( `D`, `S`, `T`, `F`, `O`), new-build flag and tenure for each sale, under the Open Government Licence.
-   It has no asking price, agent, bedrooms or floor area, and the latest two months are incomplete because registration lags the sale.
-   A recorded Rightmove search gives the asking side: `price.amount`, `bedrooms`, `tenure.tenureType`, `displaySize`, `customer.branchDisplayName` and `listingUpdate`, but its `displayAddress` is often only street and outcode.
-   Land Registry's new transaction-to-UPRN lookup lets you join a sale to Zoopla's UPRN endpoints, such as `/v1/zoopla/property-valuation-history`, at 1 credit per call.
-   Comparing median asking and sold prices by outcode and property type is a fair first measure of the asking-to-sold gap; a property-level gap needs an exact address or UPRN match.

## Where should UK sold prices come from?

From HM Land Registry, for England and Wales. Its Price Paid Data covers residential property sold for value and lodged for registration, is free to use for commercial and non-commercial purposes under the Open Government Licence, and is updated monthly ( [HM Land Registry](https://www.gov.uk/guidance/about-the-price-paid-data), 2026). It is the register, so it is the figure to use for "what did this sell for".

Each row has a transaction ID, the price from the transfer deed, the completion date, the postcode and address parts, a property type code ( `D` detached, `S` semi-detached, `T` terraced, `F` flat, `O` other), an `Old/New` flag ( `Y` for a new build), a duration ( `F` freehold or `L` leasehold) and a PPD category ( `A` for standard sales, `B` for additional types such as repossessions and buy-to-lets). The files have no header row; the columns come in that documented order.

It has limits worth knowing before you build on it. The two most recent months are incomplete, because the gap between a sale and its registration typically runs from two weeks to two months. Prices are transaction prices, not market values: a transfer within a family can be fixed far from market value. And it covers England and Wales only, so Scotland and Northern Ireland need other sources.

## What do listing sites add to sold prices?

The context around the sale. Land Registry records the end of the process, the price paid. A listing site records the process itself: what the seller asked, when the property was listed, whether the price was cut, who the agent was, and the rooms and size buyers saw. Zoopla also exposes valuation history and nearby sales for a property by its UPRN. The table maps each question to the source that answers it.

| Question | Best source | Endpoint or field |
| --- | --- | --- |
| What did it sell for, and when? | HM Land Registry | Price, Date of Transfer |
| What was the asking price? | Rightmove, Zoopla listings | `price.amount` on [Rightmove search](https://perceptlayer.com/apis/rightmove); [`/v1/zoopla/listing`](https://perceptlayer.com/apis/zoopla/listing) |
| Was the price reduced? | Rightmove | `addedOrReduced`, `listingUpdate.listingUpdateReason` |
| Which agent handled it? | Rightmove, Zoopla | `customer.branchDisplayName`; `/v1/zoopla/property-agents` |
| Bedrooms, size, tenure | Rightmove | `bedrooms`, `displaySize`, `tenure.tenureType` |
| How has its estimated value moved? | Zoopla | `/v1/zoopla/property-valuation-history` by `uprn` |
| What sold nearby? | Zoopla, Rightmove | `/v1/zoopla/property-nearby-sold`; `/v1/rightmove/sold-properties` |

Rightmove's `sold-properties` endpoint filters by `postcode`, `property_type` ( `DETACHED`, `FLAT`, `TERRACED`, `SEMI_DETACHED`), `tenure` and `sold_in` (3, 6, 12, 24 or 120 months). Zoopla's `sold` endpoint takes a `location` and sale-date bounds. Both are listing-site views of completed sales, useful for joining to listings; for the price itself, the register wins.

## Which listing fields are worth keeping?

Keep the fields that explain a price, and the ones you need to match records. Our recorded Rightmove search for `location=London&channel=BUY` shows the asking side. This excerpt is its first listing, trimmed with `…`:

```json
{
  "id": 90486465,
  "bedrooms": 3,
  "bathrooms": 2,
  "displayAddress": "Cavaye Place, Chelsea, London, SW10",
  "location": { "latitude": 51.486678, "longitude": -0.180225 },
  "propertySubType": "Apartment",
  "tenure": { "tenureType": "LEASEHOLD" },
  "listingUpdate": { "listingUpdateReason": "new", "listingUpdateDate": "2026-07-03T22:30:31Z" },
  "price": { "amount": 1495000, "currencyCode": "GBP", … },
  "customer": { "branchId": 298991, "branchDisplayName": "OB Private, Mayfair", … },
  "displaySize": "1,285 sq. ft.",
  "channel": "BUY",
  "addedOrReduced": "Added on 03/07/2026",
  …
}
```

| Field | Keep it because | Watch out for |
| --- | --- | --- |
| `price.amount` | The asking price, numeric | It's what the seller wants, not what the buyer paid |
| `listingUpdate.listingUpdateReason`, `listingUpdateDate` | Why and when the listing last changed ( `new` in the recording) | Store every observation to see changes over time |
| `addedOrReduced` | The site's own text, such as `Added on 03/07/2026` | Display text; parse the date carefully |
| `bedrooms`, `bathrooms` | Explain price differences within a type | Land Registry has no room counts to check against |
| `displaySize` | Floor area when the agent gives it | Text, sometimes empty |
| `tenure.tenureType` | Matches Land Registry's `F`/ `L` duration | Leasehold length isn't in either excerpt |
| `propertySubType` | Maps roughly to `D`, `S`, `T`, `F` | "Apartment" and "Flat" both map to `F` |
| `customer.branchDisplayName`, `branchId` | Agent share and agent-level analysis | A branch, not a person |
| `channel` | Filters out commercial stock | The same response has a `COMMERCIAL_BUY` listing |
| `location` | Distance to sales and to each other | Use it for distance, not to identify an address |

Two details from the same recording matter for sold-price work. The second listing in it is `Land` at `65000000` with `channel` `COMMERCIAL_BUY`, inside a residential `BUY` search, so filter on `channel` and `propertySubType` before computing anything. And two of the three `displayAddress` values stop at the street and outcode ( `SW10`, `W1W`), which is too coarse to match one listing to one Land Registry sale.

## How do you join a sale to a listing?

By UPRN where you can, by address where you must. HM Land Registry now publishes a lookup from Price Paid transaction IDs to UPRNs. It was first published on 28 August 2026, isn't back-dated, includes a UPRN only where Land Registry can reliably link the title address to an addressable location, and carries its own licence conditions and an Ordnance Survey attribution ( [HM Land Registry](https://www.gov.uk/government/statistical-data-sets/transaction-unique-identifier-and-uprn-look-up-table-dataset), 2026).

With a UPRN, every Zoopla property endpoint is one call: `/v1/zoopla/property`, `property-valuation-history`, `property-agents` and `property-nearby-sold` each take `uprn` and cost 1 credit. That turns a bare sale row into a sale with its valuation history and agents. We don't have a recorded Zoopla response, so check which fields each returns in the playground on the [Zoopla API page](https://perceptlayer.com/apis/zoopla) before you design your tables.

For older sales, or for Rightmove, match on address. Normalise the postcode (upper case, single space), the house number or name ( `PAON`), the flat ( `SAON`) and the street, and treat a match as certain only when all of them agree. The article on [deduplicating listings across sources](https://perceptlayer.com/blog/deduplicate-listings-across-sources) covers the matching method. Where the listing address stops at the outcode, don't force a property-level match; use the aggregate comparison below instead.

## How do you measure the asking-to-sold gap for an area?

Compare like with like at area level: median asking price from current listings against median sold price from the register, for the same outcode and property type. This works without any property-level match. It measures the market, not individual negotiations, so it's a first look rather than a precise discount. The script reads a Price Paid CSV by column position, which follows Land Registry's documented order, and pages a Rightmove search for one outcode.

```python
import csv
import os
import statistics
import requests

OUTCODE, LR_TYPE, RM_TYPES = "SW10", "F", {"Flat", "Apartment"}

# Land Registry Price Paid Data: no header row; columns in documented order.
sold = []
with open("pp-2026.csv", newline="") as f:
    for row in csv.reader(f):
        price, postcode, ptype, category = int(row[1]), row[3], row[4], row[14]
        if postcode.split(" ")[0] == OUTCODE and ptype == LR_TYPE and category == "A":
            sold.append(price)

asking, credits = [], 0
for page in range(1, 6):
    r = requests.get("https://api.perceptlayer.com/v1/rightmove/search",
                     params={"location": OUTCODE, "channel": "BUY", "page": page},
                     headers={"X-API-Key": os.environ["API_KEY"]}, timeout=60)
    if r.status_code != 200:
        break
    credits += int(r.headers["X-Credits-Used"])
    data = r.json()["data"]
    for p in data["properties"]:
        if p["channel"] == "BUY" and p.get("propertySubType") in RM_TYPES:
            asking.append(p["price"]["amount"])
    if not data.get("has_next_page"):
        break

if sold and asking:
    s, a = statistics.median(sold), statistics.median(asking)
    print(f"{OUTCODE} flats: median sold {s:,.0f} (n={len(sold)}), "
          f"median asking {a:,.0f} (n={len(asking)}), gap {(s - a) / a:+.1%}")
print("credits used:", credits)
```

We tested the script on a sample row and the recorded Rightmove fields, not against live calls. It keeps only category `A` sales, which drops repossessions and other non-standard transfers, and only `BUY` listings of flats. Read the result with its sample sizes. The two sets differ in timing (sales completed months ago against today's asking prices) and in mix (which flats happen to be listed now), so a gap of a few per cent can be noise. Run it monthly per outcode and watch the trend.

## What does the listing side cost?

Land Registry's data is free. Each listing-site call is 1 credit on success and 0 on failure, so the cost is pages and lookups. The script above reads up to 5 Rightmove pages per outcode: 20 outcodes monthly is at most 20 × 5 = 100 credits. Adding Zoopla valuation history and agents for 200 sales with a UPRN is 200 × 2 = 400 credits.

| Step | Calls a month | Credits |
| --- | --- | --- |
| Rightmove search, 20 outcodes × 5 pages | 100 | 100 |
| Zoopla `property-valuation-history` + `property-agents`, 200 UPRNs | 400 | 400 |
| **Total** | **500** | **500** |

That's 500 credits, inside the 1,000 free credits every verified account gets each month. The [credits docs](https://docs.perceptlayer.com/credits) explain the balance and [pricing](https://perceptlayer.com/pricing) has what packs cost. The [Rightmove API guide](https://perceptlayer.com/blog/rightmove-api-property-data) covers search paging and its record cap; to start, run a `sold-properties` call for your own postcode in the playground on the [Rightmove API page](https://perceptlayer.com/apis/rightmove).

## APIs in this post

-   [Zoopla **1 credit per call** · 17 endpoints](https://perceptlayer.com/apis/zoopla)
-   [Rightmove **1 credit per call** · 8 endpoints](https://perceptlayer.com/apis/rightmove)

## Frequently asked questions

### Is HM Land Registry sold price data free?

Yes. Price Paid Data is free to download and use, for commercial or non-commercial purposes, under the Open Government Licence, with an attribution statement. It covers residential sales in England and Wales sold for value and is updated monthly. The transaction-to-UPRN lookup has its own licence conditions, so read those before you use it.

### Why are the latest months missing sales?

Price Paid Data follows registration, not completion. HM Land Registry says the gap between a sale and its registration typically runs from two weeks to two months, so the two most recent months are incomplete. Don't read a drop in recent volumes as a market change until those months fill in.

### Can listing sites tell me the actual sold price?

They show completed sales, and Rightmove's `sold-properties` and Zoopla's `sold` endpoints return them, but the authoritative price for England and Wales is the Land Registry record. Use listing-site sold data to join a sale to its listing, agent and valuation history, and the register for the price itself.

### How do I match a Rightmove listing to a Land Registry sale?

Only when both have the full address. The recorded Rightmove search shows some `displayAddress` values with only the street and outcode, which can't identify one property. Match on normalised postcode, house number or name, flat and street when they're all present; otherwise compare at outcode level by property type.

### What does it cost to add listing data to Land Registry sales?

Land Registry data costs nothing. Listing calls are 1 credit each when they succeed and nothing when they fail: 100 Rightmove search pages and 400 Zoopla UPRN lookups a month is 500 credits, inside the 1,000 free monthly credits. The [pricing page](https://perceptlayer.com/pricing) has what credit packs cost beyond that.

uk property sold prices land registry house prices real estate data

## More from the blog

1.  [Web data by industry](https://perceptlayer.com/blog/category/web-data) 21 Oct 2026 Perceptlayer 8 min read
    
    ## [Job Posting Data for Labour-Market Research](https://perceptlayer.com/blog/job-posting-data-labour-market-research)
    
    Which Indeed posting fields support trend analysis, where postings mislead versus hires, and a weekly collection plan that costs 3,000 credits a month.
    
2.  [Web data by industry](https://perceptlayer.com/blog/category/web-data) 6 Oct 2026 Perceptlayer 7 min read
    
    ## [Salary Data from Job Sites: Reading Ranges Without Bias](https://perceptlayer.com/blog/salary-data-job-sites-bias)
    
    Glassdoor's salaries endpoint returns p10 to p90 total pay, a currency and a national count. How to read those ranges and the biases of self-reported pay.
    
3.  [Web data by industry](https://perceptlayer.com/blog/category/web-data) 30 Sept 2026 Perceptlayer 7 min read
    
    ## [Software Review Data Analysis for Competitive Research](https://perceptlayer.com/blog/software-review-data-competitive-research)
    
    What Capterra, Trustpilot and Google Play reviews can and can't tell you about a competitor, which fields to keep, the sampling bias, and a worked rating tally.
