# UK EPC Register Scraper · Energy Certificates + Assessors (`automation_craft/uk-energy-certificate-scraper`) Actor

Search the official UK Energy Certificate Register (England, Wales & Northern Ireland) by postcode, street or certificate number. Get EPC/CEPC/DEC/AC certificates with ratings, floor areas, energy use, CO2, improvement steps and assessor contacts. Pay only for results.

- **URL**: https://apify.com/automation\_craft/uk-energy-certificate-scraper.md
- **Developed by:** [Automation Craft](https://apify.com/automation_craft) (community)
- **Categories:** Real estate, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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/platform/actors/running/actors-in-store#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 EPC Register Scraper — Energy Certificates + Assessors

Turn the official **UK Energy Certificate Register** ([find-energy-certificate.service.gov.uk](https://find-energy-certificate.service.gov.uk)) into clean, structured JSON. Search whole postcodes or streets, pull full certificate details — ratings, floor areas, energy use, CO₂, recommended improvements with costs, and the assessor who signed the certificate — for **England, Wales and Northern Ireland**, covering both homes (EPC) and commercial buildings (CEPC, DEC, air-conditioning certificates).

No browser, no logins, no guesswork: this is the public government register, delivered as data you can actually work with.

### Who uses this

- **Retrofit, solar and heat-pump installers** — find every D/E/F/G-rated home in your service area, with the register's own improvement steps ("cavity wall insulation, £500–£1,500, saves £27/yr") as a ready-made talking point.
- **Landlords and letting agents (MEES compliance)** — properties rated **F or G cannot legally be let** in England and Wales. Filter a portfolio's postcodes by rating and see exactly which certificates are expired or failing.
- **Proptech and data teams** — enrich address data with ratings, floor areas, property types, energy costs and CO₂ figures, keyed by the official 20-digit certificate number.
- **Assessor lead lists** — every full certificate carries the assessor's name, accreditation scheme and (on most domestic EPCs) phone and email.

### ✅ What it CAN do

- Search **by postcode** — every certificate registered at that postcode (verified live: dense postcodes return all results on one page).
- Search **by street + town** (domestic and non-domestic registers).
- Fetch **certificates directly** by URL or 20-digit certificate number.
- Cover **both registers**: domestic (EPC) and non-domestic (CEPC, CEPC-RR, DEC, DEC-RR, AC-CERT, AC-REPORT), or both at once.
- Pull **full certificate details**: current/potential rating and score, property type, floor area, primary energy use, heating/hot-water energy, yearly energy cost and potential saving, CO₂ tonnes per year, step-by-step improvement recommendations with cost ranges, assessor and accreditation-scheme contacts.
- Flag **expired certificates** (the register marks them inline; we expose a clean `expired` boolean).
- Filter results **by current rating** (e.g. only F and G).
- **Northern Ireland is served** — verified live with Belfast postcodes: BT searches return certificates and their detail pages parse identically.

### ❌ What it CANNOT do

- **No Scotland.** Scottish EPCs live on a separate register ([scottishepcregister.org.uk](https://www.scottishepcregister.org.uk)) that this actor does not cover.
- **Only what the public register shows.** No owner names, no sale prices, no data beyond the certificate itself.
- **The rating filter is applied after the search**, client-side — the register itself has no rating search. Filtered-out rows are never charged.
- **Non-domestic search rows carry no rating** (the register's table doesn't show one), so `ratingFilter` on the non-domestic register only works together with `fetchDetails: true`.
- **Very broad street searches** ("High Street, London") are refused by the register itself ("Too many results"). You get a free status item — use postcodes instead.
- Some fields are **best effort** on older or non-domestic certificates (see field notes below).

### Input

| Field             | Type             | Default                 | Description                                                                                       |
| ----------------- | ---------------- | ----------------------- | ------------------------------------------------------------------------------------------------- |
| `postcodes`       | array of strings | `["LS6 1EF"]` (prefill) | UK postcodes to search. Casing/spacing normalized automatically (`ls61ef` works).                 |
| `streetSearches`  | array of objects | `[]`                    | `[{"streetName": "Hessle Avenue", "town": "Leeds"}]` — street-level searches.                     |
| `certificateUrls` | array of strings | `[]`                    | Full certificate URLs or 20-digit certificate numbers (dashes optional).                          |
| `register`        | enum             | `domestic`              | `domestic`, `non-domestic`, or `both`.                                                            |
| `fetchDetails`    | boolean          | `false`                 | Off: cheap search-result rows. On: full certificate details for every result.                     |
| `ratingFilter`    | array            | `[]` (all)              | Keep only these current ratings, e.g. `["F","G"]`. `A+` exists only on non-domestic certificates. |
| `maxItems`        | integer          | `100`                   | Hard cap on charged items, honored exactly.                                                       |

Running the actor with a completely empty input (`{}`) demonstrates it on the default postcode instead of failing.

### Output

Three item types land in the dataset: `search-result` rows (list mode), `certificate` rows (details mode / direct lookups), and one free `summary` item at the end (plus free `status` items for empty/refused searches). Every row echoes the query that produced it (`searchedPostcode`, `searchedStreet`, or `requestedCertificate`). All certificate numbers are strings.

Real output from a live run (one direct certificate + one search row + the summary):

```json
[
    {
        "type": "certificate",
        "certificateNumber": "0260-2870-6219-9191-6111",
        "certificateUrl": "https://find-energy-certificate.service.gov.uk/energy-certificate/0260-2870-6219-9191-6111",
        "address": "First Floor Flat, 103 Iffley Road, OXFORD, OX4 1EH",
        "postcode": "OX4 1EH",
        "validUntil": "2029-09-09",
        "expired": false,
        "propertyType": "Top-floor flat",
        "totalFloorAreaSqm": 89,
        "dateOfAssessment": "2019-09-10",
        "dateOfCertificate": "2019-09-10",
        "assessorName": "Kieran Foggett",
        "assessorPhone": "01993773258",
        "assessorEmail": "cis@pinkconnect.co.uk",
        "assessorId": "EES/020660",
        "accreditationScheme": "Elmhurst Energy Systems Ltd",
        "employer": null,
        "register": "domestic",
        "certificateType": "EPC",
        "currentRating": "E",
        "currentScore": 54,
        "potentialRating": "D",
        "potentialScore": 63,
        "primaryEnergyUseKwhM2Year": 335,
        "heatingKwhPerYear": 18496,
        "hotWaterKwhPerYear": 2196,
        "annualEnergyCostGbp": 1096,
        "potentialAnnualSavingGbp": 224,
        "co2TonnesPerYear": 5.3,
        "co2PotentialTonnesPerYear": 4.1,
        "recommendations": [
            {
                "step": 1,
                "measure": "Cavity wall insulation",
                "costLow": 500,
                "costHigh": 1500,
                "yearlySaving": 27
            },
            {
                "step": 2,
                "measure": "Internal wall insulation",
                "costLow": 4000,
                "costHigh": 14000,
                "yearlySaving": 161
            },
            {
                "step": 3,
                "measure": "Heating controls (thermostatic radiator valves)",
                "costLow": 350,
                "costHigh": 450,
                "yearlySaving": 37
            }
        ],
        "requestedCertificate": "0260-2870-6219-9191-6111"
    },
    {
        "type": "search-result",
        "register": "domestic",
        "certificateNumber": "8788-6022-5319-2795-6906",
        "certificateUrl": "https://find-energy-certificate.service.gov.uk/energy-certificate/8788-6022-5319-2795-6906",
        "address": "2, Hessle Avenue, LEEDS, LS6 1EF",
        "postcode": "LS6 1EF",
        "certificateType": "EPC",
        "currentRating": "D",
        "validUntil": "2029-01-07",
        "expired": false,
        "searchedPostcode": "LS6 1EF"
    },
    {
        "type": "summary",
        "searchesRun": 1,
        "searchResultsEmitted": 1,
        "certificatesEmitted": 1,
        "itemsCharged": 2,
        "maxItemsReached": true
    }
]
```

#### Field reliability notes (measured on live pages)

- **Domestic EPCs**: ratings, scores, property type, floor area, costs, CO₂, recommendations and assessor contacts populated on every page tested. `heatingKwhPerYear` / `hotWaterKwhPerYear` and `validUntil` are **best effort** — very old (pre-2012, often expired) certificates can omit them. `employer` is rarely shown on domestic pages.
- **Non-domestic certificates**: `assessorName`, `assessorEmail`, `accreditationScheme`, `employer`, floor area and `validUntil` are reliable. `assessorPhone`, `propertyType`, ratings/scores and energy figures are **best effort** — they depend on the certificate type (AC certificates have no rating at all; DECs use operational ratings).

### Pricing

Pay-per-event, and **only for real data**: one small event per search-result row, one larger event per full certificate. Empty postcodes, "too many results" refusals, unknown certificate numbers, status items and the summary are always **free** — a run that finds nothing costs you nothing beyond the actor start. `maxItems` is a hard spend cap, honored exactly.

### Data license

The register's content is published under the **[Open Government Licence v3.0](https://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/)**. You are free to use it, including commercially, with attribution to the source. This actor only reads public pages, politely (low concurrency, no bypassing of any protection — there is none to bypass).

# Actor input Schema

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

UK postcodes to search, e.g. <code>LS6 1EF</code>. Every certificate registered at each postcode is returned (England, Wales and Northern Ireland; Scotland has a separate register that this actor does not cover). Casing and spacing are normalized automatically.

## `streetSearches` (type: `array`):

Optional street-level searches as objects: <code>\[{"streetName": "Hessle Avenue", "town": "Leeds"}]</code>. Broad streets in big towns can come back as 'too many results' — the register refuses to list them; use postcodes instead in that case (a free status item is emitted, nothing is charged).

## `certificateUrls` (type: `array`):

Optional direct certificate lookups: full URLs like <code>https://find-energy-certificate.service.gov.uk/energy-certificate/0260-2870-6219-9191-6111</code> or 20-digit certificate numbers (dashes optional). Each is fetched as a full certificate item.

## `register` (type: `string`):

Which register to search: <b>domestic</b> (homes — EPCs), <b>non-domestic</b> (commercial buildings — CEPC, DEC, air-conditioning certificates) or <b>both</b>. Direct certificate URLs work regardless of this setting.

## `fetchDetails` (type: `boolean`):

Off (default): emit cheap search-result rows only (address, rating, valid-until, certificate link). On: also open every certificate and emit the full detail item — scores, floor area, energy use, CO2, improvement steps and assessor contact details. Full details cost more per item, so this is off unless you switch it on.

## `ratingFilter` (type: `array`):

Only keep certificates whose CURRENT rating is in this list (e.g. <code>\["F","G"]</code> for MEES-non-compliant properties). Empty = all ratings. Applied client-side after the search. Note: non-domestic search rows carry no rating, so combine this filter with <b>Fetch full certificate details</b> when searching the non-domestic register.

## `maxItems` (type: `integer`):

Hard cap on the number of charged result items (search-result rows + certificate details) for this run, honored exactly. Status items and the run summary are always free.

## Actor input object example

```json
{
  "postcodes": [
    "LS6 1EF"
  ],
  "streetSearches": [],
  "certificateUrls": [],
  "register": "domestic",
  "ratingFilter": [],
  "maxItems": 100
}
```

# Actor output Schema

## `results` (type: `string`):

No description

## `searchResults` (type: `string`):

No description

## `certificates` (type: `string`):

No description

## `resultsCsv` (type: `string`):

No description

# 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 = {
    "postcodes": [
        "LS6 1EF"
    ],
    "streetSearches": [],
    "certificateUrls": [],
    "register": "domestic",
    "fetchDetails": false,
    "ratingFilter": [],
    "maxItems": 100
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation_craft/uk-energy-certificate-scraper").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 = {
    "postcodes": ["LS6 1EF"],
    "streetSearches": [],
    "certificateUrls": [],
    "register": "domestic",
    "fetchDetails": False,
    "ratingFilter": [],
    "maxItems": 100,
}

# Run the Actor and wait for it to finish
run = client.actor("automation_craft/uk-energy-certificate-scraper").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 '{
  "postcodes": [
    "LS6 1EF"
  ],
  "streetSearches": [],
  "certificateUrls": [],
  "register": "domestic",
  "fetchDetails": false,
  "ratingFilter": [],
  "maxItems": 100
}' |
apify call automation_craft/uk-energy-certificate-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automation_craft/uk-energy-certificate-scraper"
        }
    }
}

```

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/IWQJBOlBpzS9UVspV/builds/wcvKhlMKhb9JhLT2n/openapi.json
