# UK Property Data API - EPC, Comps, Council Tax, Flood Risk (`nabeelbaghoor/uk-property-data-api`) Actor

Look up any of 30 million UK homes by UPRN: price and rent estimates with gross yield, land registry sale history and tenure, EPC ratings and scores, council tax band and cost, flood and coastal risk, title and plot, broadband and mobile coverage, comparable listings and the seller disclosure pack.

- **URL**: https://apify.com/nabeelbaghoor/uk-property-data-api.md
- **Developed by:** [Nabeel Hassan](https://apify.com/nabeelbaghoor) (community)
- **Categories:** Real estate, Business, Other
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 address matcheds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## UK Property Data API - EPC, Comps, Council Tax, Flood Risk

Look up any of 30 million UK residential properties by UPRN and get the whole picture: price and rent estimates with gross yield, land registry sale history, EPC ratings, council tax band and cost, tenure and title, flood and environmental risk, broadband and mobile coverage, comparable listings in eight separate portal statuses, and the material information pack a seller has to disclose.

### What this actor does

- **Address to UPRN, reliably.** Free text address search returns ranked candidates with a match score, the full address broken into its parts, the local authority, the Ordnance Survey classification and both latitude/longitude and eastings/northings. Postcode search lists every address in a postcode. The UPRN is the national identifier every UK property dataset aligns to, so this is the join key for everything else you own.
- **Three valuations, kept apart.** A model price estimate, the land registry's recorded value, and the price the property actually last changed hands for are three different numbers that answer three different questions. They arrive in three columns rather than being collapsed into one "value" that describes none of them, alongside the rent estimate and the gross yield.
- **EPC, council tax and tenure.** Current and potential energy rating and score, certificate number and date, and the certified floor area. Council tax band with its monthly and annual cost and the date it was last checked. Freehold or leasehold, the title number, and the registered plot area.
- **Flood risk as depth bands, not a yes or no.** Surface water and rivers-and-seas risk, at six depths, for the present day and for a future horizon. A single "flood risk" flag throws away the part anyone actually underwrites on, so the bands that apply are reported as they are.
- **Comparables in eight distinct statuses.** Currently for sale, sold subject to contract, withdrawn without selling, withdrawn after completion, to let, let agreed, let agreed then withdrawn, and let then withdrawn. Each comes with price, price per square foot, floor area, bedrooms, tenure, distance, time on market, the listing agent and the portal links.
- **Registered sales kept separate from listings.** Land registry transactions have their own row type. A completed sale at a registered price and a listing at an asking price are different events, and averaging them together is how a valuation goes wrong.
- **The material information pack.** The National Trading Standards disclosure set, in its own A, B and C grouping: lease term remaining, ground rent and service charge, construction type and whether it is non-standard, radon risk, restrictive covenants, greenbelt, conservation area, national park, coalfield, pylon proximity, building safety and the accessibility features.
- **Schools, transport, tree orders and listed buildings.** Each as its own rows, so a property with nine nearby schools does not push nine sets of columns onto the property row. Transport links are flattened out of the provider's six groups into one list sortable by distance.
- **Insider: everything on the market in an outcode.** Active listings or withdrawn ones across a whole postcode outcode, with the agent, asking price, key features, portal links and first-seen dates.
- **Pay per result.** You are charged for rows carrying a real record. Addresses the provider cannot match and properties it holds nothing for are free.

### Bring your own key

This actor calls the provider with **your own account key**. The key travels in the provider's own named header rather than as a bearer token, which is the detail most integrations get wrong first. Paste it into the API key field, or set it as the `DATA_API_KEY` secret. Nothing runs on anyone else's credentials, and a run without a key ends cleanly with an explanation rather than failing.

Some accounts also require HTTP basic credentials alongside the key. Fill both the username and password fields if yours does, and leave both empty if it does not: a half-filled basic header turns a working key into a refusal that reads like a bad key.

### The UPRN is not the provider's property ID

There are two identifiers in play and they are not interchangeable. The **UPRN** is the national Unique Property Reference Number. The **property ID** is the provider's own internal key. Some endpoints take one, some take the other, and passing the wrong one does not produce an error - it looks up a different property, or nothing.

So this actor works in UPRNs throughout and resolves to the internal ID through the provider's own bridge endpoint, caching the answer for the run. Where a UPRN has no internal ID, it falls back to the UPRN-keyed record and **says so in the log**, because that record carries no council tax, EPC, school or transport data and a silent fallback would look like a property with no energy certificate.

### Success is not the status code

Almost every response from this provider carries its own boolean `status` field, and an ordinary HTTP 200 can carry `status: false` meaning it holds nothing for that property. An integration that trusts the code alone writes out an empty row and calls it data. This actor reads the body, so a property with no record becomes a labelled miss rather than a row of nulls.

### Every number arrives as a string

Latitude, longitude, the price estimate, the rent estimate, the yield, council tax costs and every EPC score are typed as strings in the provider's own schema, and some carry currency symbols and thousands separators. Passed through, a latitude sorts as text and breaks a map, and a price column will not sum.

They are parsed once, here, before they reach your dataset. That parse is most of the difference between a dataset and a screenshot of one.

### Example output

```json
{
  "recordType": "property",
  "requested": "100023336956",
  "found": true,
  "uprn": "100023336956",
  "propertyId": 8842119,
  "address": "10 Downing Street, London, SW1A 2AA",
  "postcode": "SW1A 2AA",
  "localAuthority": "Westminster",
  "latitude": 51.503396,
  "longitude": -0.127625,
  "propertyType": "Terraced",
  "bedrooms": 5,
  "totalFloorAreaSqft": 2760,
  "estimatedPrice": 4250000,
  "rentalEstimate": 9800,
  "grossYieldPercent": 2.77,
  "lastSoldPrice": 3150000,
  "lastSoldDate": "2014-06-27",
  "tenure": "Freehold",
  "councilTaxBand": "H",
  "councilTaxAnnual": 1828.47,
  "epcCurrentRating": "D",
  "epcCurrentScore": 61,
  "epcPotentialRating": "B",
  "epcPotentialScore": 84,
  "floodRiskRiversSeas": "Low",
  "floodRiskSurfaceWater": "Very Low",
  "conservationArea": "Yes",
  "plotAreaSqm": 214.8,
  "titleNumber": "NGL123456",
  "broadbandUltrafastDownMbps": 1000,
  "mobileCoverageEeIndoor": 4
}
```

### FAQ

#### What is a UPRN and why does this API need one?

The UPRN is the Unique Property Reference Number, a stable identifier assigned by Ordnance Survey to every addressable location in Great Britain. It is the join key that UK property datasets align to, which is why it identifies a property here rather than an address string. Use address search mode to turn an address or postcode into a UPRN, then feed that UPRN into the property, material information and comparable modes.

#### What UK property data does this actor return?

For a single property: model price and rent estimates with gross yield, land registry value and last recorded sale, tenure and title number, current and potential EPC rating and score, council tax band with monthly and annual cost, flood risk from rivers and seas and from surface water, coastal erosion and conservation area status, plot area, bedrooms, bathrooms, receptions and floor area, property classification, broadband speeds at three tiers, and indoor mobile coverage for four networks. Optional extra rows cover nearby schools with Ofsted ratings, transport links, tree preservation orders and listed buildings.

#### What is the difference between comparables and sold comparables?

Comparables come from property portal listings and carry a marketing status: available, sold subject to contract, withdrawn, to let, let agreed and so on. Sold comparables come from the land registry and are completed transactions with a registered price and date. The two disagree by design, because an asking price is not a sale price, so they are returned as separate row types and never merged into one price history.

#### Can I get sold prices for a UK property?

Yes, in two places, and they mean different things. The property row carries the last recorded sale price and transfer date from the land registry for that exact property. Sold comparables mode returns registered sales near a property, filtered by bedroom count, property type, price range, radius and recency, which is what you want for a valuation rather than a single data point.

#### How is flood risk reported?

As depth bands rather than a single rating. The material information pack carries surface water and rivers-and-seas risk at six depths, for both the present day and a future horizon, plus reservoir risk on wet and dry days, whether the property flooded in the last five years, and coastal erosion flags. The property record also carries the headline rivers-and-seas and surface-water ratings. Only the bands that actually carry a value are reported, so an absent band reads as absent rather than as a zero.

#### What is the material information pack?

It is the disclosure set UK sellers and agents have to provide under National Trading Standards guidance, grouped into part A for financial and tenure matters, part B for physical characteristics and part C for environmental and planning matters. This actor keeps that grouping in the column names, covering lease term, ground rent, service charge, construction type, radon, covenants, greenbelt and conservation status, building safety and accessibility features.

#### Do I need my own account with the data provider?

Yes. This is a bring-your-own-key actor: it calls the API with your own account key and never with anyone else's. Note that the endpoints are licensed in bundles, so a key that reads properties fine can still be refused the insider feed or the material information pack. When that happens the actor reports it as an entitlement problem rather than as a bad key, so you are not sent off to re-copy a key that was correct.

#### Does it cover Scotland and Northern Ireland?

Coverage is strongest across England and Wales, where the land registry, EPC and council tax feeds are most complete, and the material information pack carries Scotland-specific covenant fields. The property record returns a country code so you can see which regime a given result falls under.

### Keyword map

UK property data API, UPRN lookup API, UK property API, EPC data API, energy performance certificate API, council tax band API, land registry API, sold house prices API, property comparables API, UK house price data, rental estimate UK, gross yield calculator data, flood risk data API, material information pack API, National Trading Standards part A B C, title number lookup, leasehold ground rent data, conservation area data, listed building data, tree preservation order data, UK postcode address lookup, Ordnance Survey UPRN, property due diligence data, estate agent data API, UK property investment data

# Actor input Schema

## `mode` (type: `string`):

Address search matches free text to UK properties and their UPRNs, which every other mode needs. Postcode search lists every address in a postcode. Property report reads one property in full. Material information reads the disclosure pack a UK seller has to provide. Comparables lists nearby listings in one portal status, sold comparables lists registered land registry sales, and insider lists what has been on the market across a whole outcode.

## `searchPhrases` (type: `array`):

One address, part of an address or postcode per line, for address search mode. The provider returns a match score per result, so a partial address gives you candidates rather than a guess.

## `postcodes` (type: `array`):

One full UK postcode per line, for postcode search mode. For example SW1A 2AA.

## `uprns` (type: `array`):

One Unique Property Reference Number per line, for the property, material information and comparable modes. The UPRN is the national identifier that every UK property dataset aligns to, and address search mode is how to find it from an address.

## `outcodes` (type: `array`):

One postcode outcode per line for insider mode, meaning the part before the space. For example SW1A or M1.

## `includeCommercial` (type: `boolean`):

Include commercial premises in a postcode search rather than residential addresses only.

## `includeSchools` (type: `boolean`):

Emit one row per nearby school with its Ofsted rating, pupil count, phases and distance. Read from the property record where it is already present, so it usually costs no extra call.

## `includeTransport` (type: `boolean`):

Emit one row per nearby transport link, flattened out of the provider's six groups so rail, bus, ferry, road and air links can be sorted together by distance.

## `includeTreeOrders` (type: `boolean`):

Emit one row per tree preservation order around the property, with its order reference and species. Relevant to anyone planning work on or near the plot.

## `includeListedBuildings` (type: `boolean`):

Emit one row per listed building around the property, with its grade and listing date.

## `includeTelevision` (type: `boolean`):

Add which television services reach the property to the property row.

## `includeImages` (type: `boolean`):

Add the map with plot polygon, satellite image and street view image links to the property row.

## `listingStatuses` (type: `array`):

Which listing populations to pull comparables from. These are not filters over one list: an asking price, an agreed sale and a letting are different numbers, and the status that produced each row travels with it so they can be told apart.

## `bedrooms` (type: `string`):

Restrict comparables to this bedroom count. Leave empty for any.

## `propertyTypes` (type: `array`):

Restrict comparables to these property types. Leave empty for any.

## `priceFrom` (type: `integer`):

Lowest price to include in comparables, in pounds. Applies to the sale price for sales statuses and to the monthly rent for letting statuses.

## `priceTo` (type: `integer`):

Highest price to include in comparables, in pounds.

## `searchDistanceMiles` (type: `number`):

How far around the subject property to look for comparables. The provider defaults to half a mile.

## `listedWithinDays` (type: `integer`):

Only return comparables listed in the last this many days. Leave empty for all time.

## `newBuildOnly` (type: `boolean`):

Restrict comparables to new build properties.

## `bungalowsOnly` (type: `boolean`):

Restrict comparables to bungalows.

## `sort` (type: `string`):

How the provider orders comparables before the limit is applied, which decides which ones you get when there are more than the limit.

## `comparableLimit` (type: `integer`):

How many comparables the provider returns per property, between 1 and 100. Larger values are clamped to 100 rather than silently ignored.

## `insiderWithdrawn` (type: `boolean`):

Read the withdrawn feed instead of the active one in insider mode. A withdrawn listing is a property that came off the market without selling, which is a different signal from one that sold.

## `insiderGroup` (type: `string`):

Optional grouping value the insider feed accepts. Leave empty unless the provider has told you which group to use.

## `apiKey` (type: `string`):

Your own account key, sent in the provider's own named header rather than as a bearer token. Stored encrypted and never written into the dataset. Can also be supplied as the DATA\_API\_KEY environment secret.

## `basicAuthUsername` (type: `string`):

Only if your account also requires HTTP basic authentication alongside the key. Leave empty otherwise: a half-filled basic header turns a working key into a refusal.

## `basicAuthPassword` (type: `string`):

Password paired with the basic auth username. Sent only when both are filled in. Stored encrypted and never written into the dataset.

## `maxResults` (type: `integer`):

Stop after this many rows. Counts every row: addresses, properties, comparables, schools and transport links alike.

## `requestsPerMinute` (type: `integer`):

How fast to call the provider. Lower this if your plan's rate limit is being hit.

## `baseUrl` (type: `string`):

Overrides the host the API is called on. Leave empty unless the provider has given you a different one.

## Actor input object example

```json
{
  "mode": "propertyReport",
  "includeCommercial": false,
  "includeSchools": false,
  "includeTransport": false,
  "includeTreeOrders": false,
  "includeListedBuildings": false,
  "includeTelevision": false,
  "includeImages": false,
  "newBuildOnly": false,
  "bungalowsOnly": false,
  "sort": "",
  "comparableLimit": 30,
  "insiderWithdrawn": false,
  "maxResults": 100,
  "requestsPerMinute": 60
}
```

# Actor output Schema

## `records` (type: `string`):

One row per record, alongside the UPRN, postcode or outcode that produced it.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {};

// Run the Actor and wait for it to finish
const run = await client.actor("nabeelbaghoor/uk-property-data-api").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {}

# Run the Actor and wait for it to finish
run = client.actor("nabeelbaghoor/uk-property-data-api").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{}' |
apify call nabeelbaghoor/uk-property-data-api --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,nabeelbaghoor/uk-property-data-api"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/8ovwMc30ZqJgX6z8o/builds/ZC6pm9OWCZh9woIqU/openapi.json
