# UK Planning Applications (`wildorigins/uk-planning-applications`) Actor

🏷️ From $0.60 / 1K | Planning applications from every UK council in one feed, classified by work type such as solar, extension or loft conversion, with site address, applicant and agent details.

- **URL**: https://apify.com/wildorigins/uk-planning-applications.md
- **Developed by:** [Wild Origins](https://apify.com/wildorigins) (community)
- **Categories:** Real estate, Lead generation, Automation
- **Stats:** 1 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.60 / 1,000 applications

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## UK Planning Applications

Planning applications from every UK local authority in one feed, tagged by the type of work, with applicant and agent details attached.

### 🔍 What does UK Planning Applications do?

Planning data is normally scattered across more than 300 separate council portals, each with its own site and its own search. This Actor gives you the lot from a single search, as clean rows ready for a spreadsheet, a CRM or your own product.

Every application is classified by the kind of work proposed, so you can pull just the jobs you care about instead of filtering thousands of records yourself:

`solar`, `extension`, `loft-conversion`, `new-dwelling`, `change-of-use`, `demolition`, `roofing`, `windows-doors`, `driveway-landscaping`, `trees`, `signage`, `listed-building`, `commercial`

Ask for `solar` in the last 7 days within 10km of a postcode and you get exactly that.

### 🏛️ What the official route gives you, and what this adds

Planning applications are public by law, but the official register is council by council. GOV.UK's own [service for finding planning decisions](https://www.gov.uk/search-register-planning-decisions) is a postcode lookup that sends you to your local council, and each of the 300 plus local planning authorities then runs its own portal with its own search, its own reference format and its own way of publishing applicant details. Answering one question across an area that spans council boundaries means repeating the search on every portal it touches, and there is no way to search them together from the official side.

This Actor gives you that missing cross-council search in one call: a postcode radius or a list of councils, a date window, and a work-type classification the councils do not provide at all, with every result as a clean row that links back to the council's own record. The applications themselves are the councils' published data, reached through the PlanIt aggregator described below.

### 📊 What data can I extract from planning applications?

One row per planning application:

| Field | Description |
|---|---|
| 🆔 `reference` | Planning reference at the council |
| 🆔 `uid` | PlanIt unique id for the application |
| 🆔 `name` | PlanIt name for the application |
| 🏢 `council` | Council the application was made to |
| 🏠 `address` | Postal address of the site as published |
| 📍 `postcode` | UK postcode of the site |
| 📍 `latitude` | Latitude of the site |
| 📍 `longitude` | Longitude of the site |
| 📄 `description` | Full text of what is being applied for |
| 🏷️ `opportunityTags` | Work types detected, such as solar or extension |
| 🏷️ `applicationType` | Application type as recorded by the council |
| 🏷️ `applicationState` | Current state, such as Undecided or Permitted |
| 🏷️ `applicationSize` | Size band of the application |
| 📅 `startDate` | Date the application was received |
| 📅 `decidedDate` | Date of the decision, when decided |
| 🏷️ `decision` | Decision as recorded, when decided |
| 📅 `lastChanged` | When the council last changed the record |
| 🔢 `dwellings` | Number of dwellings, for residential applications |
| 📍 `ward` | Electoral ward of the site |
| 📍 `parish` | Parish of the site |
| 👤 `applicantName` | Applicant as recorded |
| 🏠 `applicantAddress` | Applicant address as recorded |
| 👤 `agentName` | Agent handling the application, when any |
| 🏢 `agentCompany` | Company of the agent |
| 🏠 `agentAddress` | Agent address as recorded |
| 👤 `caseOfficer` | Council case officer |
| 🔗 `documentsUrl` | Application documents at the council |
| 🔗 `councilUrl` | Application page at the council |
| 🔗 `planitUrl` | Application page on PlanIt |
| ⏱️ `retrievedAt` | When the row was collected, ISO 8601 |

Applicant and agent fields depend on what each council publishes, so they are not present on every record. Results are deduplicated within a run.

### 💡 Why scrape planning applications?

**Solar and heat pump installers.** Every approved or pending solar application in your area, with the property address.

**Builders, roofers, window and extension firms.** Homeowners with planning permission for the work you do, at the moment they are looking for a contractor.

**Architects and planning consultants.** Track which agents are winning work in your patch using the applicant and agent search.

**Estate agents and property investors.** Spot new dwellings, conversions and land coming forward before they hit the market.

### 🧭 Use cases

A solar installer pulling every solar application within 10km of the depot each Monday morning. `opportunityTags` set to `solar`, `recentDays` set to 7, scheduled weekly, straight into a sheet the sales team calls from.

A loft conversion firm finding homeowners who already have permission and now need a builder. `loft-conversion` with `applicationState` set to Permitted returns addresses where the decision has landed and the work has not started.

An architect checking which agents are winning applications in their patch before pitching. `developerText` with a rival practice's name shows every application they have filed across every council, with outcomes.

A house buyer checking what is proposed around a property before exchanging. The property's postcode with a 1km radius and a wide date window surfaces the extension next door and the 40 dwellings on the field behind.

A property investor watching for new dwelling applications in three specific councils. `areas` with the council names and `opportunityTags` set to `new-dwelling`, run monthly, shows land coming forward before it is marketed.

A parish councillor keeping the village informed. A weekly scheduled run on the parish postcode, delivered to a shared inbox through a webhook, replaces checking the council portal by hand.

### 🚀 How do I use UK Planning Applications?

1. Click **Try for free**.
2. Set a time window with `recentDays`, or an explicit `startDate` and `endDate`.
3. Set a place: a `postcode` with a `radiusKm` of 10 or below, or a comma separated list of council names in `areas`.
4. Narrow it with `opportunityTags` and `applicationState`, then set `maxResults` to cap the run.
5. Click **Start**, then download the results as JSON, CSV or Excel, or pull them from the API.

### ⬇️ Input

Set at least one of `recentDays`, `startDate`, `areas`, `postcode`, `searchText` or `developerText`.

```json
{
  "recentDays": 90,
  "postcode": "BS1 4DJ",
  "radiusKm": 10,
  "opportunityTags": ["solar"],
  "maxResults": 500
}
```

| Field | Type | Default | What it does |
|---|---|---|---|
| `recentDays` | integer | `7` | Applications from the last N days, 0 means today |
| `startDate` | string | | Explicit start date, YYYY-MM-DD |
| `endDate` | string | | Explicit end date, YYYY-MM-DD |
| `areas` | string | | Comma separated council names, blank for all UK |
| `postcode` | string | | Search around this postcode |
| `radiusKm` | integer | `5` | Radius around the postcode |
| `opportunityTags` | array | | Only return these work types |
| `applicationState` | string | | Undecided, Permitted, Rejected, Withdrawn or Conditions |
| `maxResults` | integer | `500` | Hard cap on billable results |

Three further filters are available: `applicationSize` takes Small, Medium or Large, `searchText` does a free text search of the description, and `developerText` searches applicant and agent names and addresses.

### ⬆️ Output

#### Table view

Results arrive as an Applications table you can sort and filter in the Console, with the council, address, description, tags and decision status lined up for scanning.

#### JSON

A typical row:

```json
{
  "reference": "26/12938/NMA",
  "council": "Bristol",
  "address": "Portwall Place, Portwall Lane, Redcliffe, Bristol BS1 6NA",
  "description": "Provision of additional floorspace at ground and roof levels.",
  "opportunityTags": ["solar"],
  "applicationState": "Undecided",
  "startDate": "2026-07-13",
  "planitUrl": "https://www.planit.org.uk/planapplic/Bristol/26/12938/NMA/"
}
```

Download it from the run as JSON, CSV or Excel, or read it straight from the API.

### Radius, and why a wide one fails

PlanIt's own data source gives up after 45 seconds and reports it as an error. A radius above roughly 10km crosses that line most of the time, whatever date range it is paired with. Measured against the same postcode:

| Radius | Result |
|--------|--------|
| 10km, 7 / 30 / 90 days | succeeds every time, around 42 seconds |
| 20km, 7 / 30 / 90 days | times out five attempts out of six |

The date range is not what costs the time, the radius is. A 90 day search at 10km returns fine. So keep the radius at 10 or below and run several searches around different postcodes rather than one wide one. The actor retries an upstream timeout, but each retry costs another 45 seconds, so it is not a substitute for a sensible radius.

### Postcode formats

Type the postcode however you have it. All of these reach the same place:

```
BS1 4DJ      bs1 4dj      BS14DJ       bs14dj
BS1-4DJ      BS1.4DJ      "BS1 4DJ"    BS1 4DJ.
```

Case, spacing and surrounding punctuation are all normalised before the search runs. PlanIt itself rejects a postcode without its space, so this matters: it answers `BS14DJ` with "No location found" rather than resolving it.

`GIR 0AA` is handled as the special case it is. An outward code on its own, such as `BS1`, is passed through untouched rather than guessed at, and a full address is left alone so it can be matched as text.

### Data source and fair use

Data comes from [PlanIt](https://www.planit.org.uk/), an independent service that aggregates UK local authority planning portals, and originates from the councils themselves. This Actor rate limits itself between requests. If you are pulling large volumes regularly, consider [supporting PlanIt](https://www.planit.org.uk/), since the service is free and volunteer run.

### ⏱️ How long does a run take?

Measured on real runs, so you know what normal looks like and can tell it apart from a run that has stalled.

| Applications returned | Typical run time |
|---|---|
| 100, one named area | about 9 seconds |
| 200, one named area | 9 to 13 seconds |
| 500, every authority, last 7 days | about 55 seconds |

A search by **named area** is the fast route, because PlanIt already indexes applications by authority and the Actor just pages the result. A search by **postcode and radius** is the slow one: the point has to be resolved and a bounding area scanned before anything can be filtered, and 40 applications within 10 km of a Bristol postcode has been measured at 56 to 107 seconds. That is the same shape as the warning elsewhere in this README that a wide radius fails rather than merely being slow, so keep `radiusKm` tight and prefer `areas` where you can name the council.

Adding `opportunityTags`, a state, a size or a text filter does not slow a run down. Those are applied to applications that were fetched anyway, so they usually shorten it by ending the paging sooner. The first few seconds of any run are the container starting rather than the work.

A run is never silently stuck. Each page of applications is logged as it is read, and if a run reaches its time limit it stops early, keeps every application already collected and says how far it got in the status message.

**Set the run timeout to suit the size of the ask.** This Actor's default is 3600 seconds, which is comfortably more than the largest run in the table above (500 applications takes about 1 minute). You are charged per delivered result rather than per minute, so a generous timeout costs you nothing and a tight one risks losing the run's work. Lower it only if you want a hard ceiling on how long a scheduled run may sit.

### 💰 How much does it cost?

You pay per application returned, after deduplication within the run. Set `maxResults` to cap what a run can cost, and use `opportunityTags` to avoid paying for records you were only going to filter out afterwards.

Runs that return nothing cost nothing. Paid Apify plans get a lower rate per application.

### 🔌 Integrations

Send results straight to Google Sheets, Slack, Airtable, Zapier, Make or your own webhook using [Apify integrations](https://docs.apify.com/platform/integrations). You can also trigger a run whenever something happens in another tool, which is the usual way to drop new applications into a CRM as leads.

UK Planning Applications is also callable by AI agents through the [Apify MCP server](https://mcp.apify.com). An agent connected to the server can discover the Actor, read its input schema and call it as a tool under the identifier `spookyweb/uk-planning-applications`, then read the dataset back in the same session. That lets an assistant asked what is being built near an address answer from the live register rather than from memory. Setup is covered in the [Apify MCP documentation](https://docs.apify.com/platform/integrations/mcp).

### 🔗 Using UK Planning Applications with the Apify API

```bash
curl -X POST "https://api.apify.com/v2/acts/spookyweb~uk-planning-applications/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"recentDays": 90, "postcode": "BS1 4DJ", "radiusKm": 10, "opportunityTags": ["solar"], "maxResults": 500}'
```

Or with the Apify client:

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

const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('spookyweb/uk-planning-applications').call({
  recentDays: 90,
  postcode: 'BS1 4DJ',
  radiusKm: 10,
  opportunityTags: ['solar'],
  maxResults: 500,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

Full detail is in the [Apify API reference](https://docs.apify.com/api/v2), and every run is also callable from the [Python](https://docs.apify.com/api/client/python) and [JavaScript](https://docs.apify.com/api/client/js) clients.

### ❓ FAQ

#### Which councils are covered?

Every UK council. The Actor reads PlanIt, which aggregates more than 300 local authority planning portals into one place. Leave `areas` blank to search the whole country, or pass a comma separated list of council names to restrict it.

#### What are opportunity tags?

A classification of the work being proposed, applied to each application from its description. The tags are `solar`, `extension`, `loft-conversion`, `new-dwelling`, `change-of-use`, `demolition`, `roofing`, `windows-doors`, `driveway-landscaping`, `trees`, `signage`, `listed-building` and `commercial`. Pass the ones you want in `opportunityTags` and everything else is filtered out before it reaches your dataset.

#### Why does a wide radius time out?

Because PlanIt's own data source gives up after 45 seconds, and a radius above roughly 10km crosses that line most of the time. Keep `radiusKm` at 10 or below and run several searches around different postcodes instead of one wide one. The radius section above has the measurements.

#### How current is it?

PlanIt collects from council portals continuously, so a run returns what has been published up to that point. There is a lag between a council receiving an application and putting it on its portal, and that varies by council. Set `recentDays` to 7 to catch the freshest window.

#### Can I monitor for new applications?

Yes. Set `recentDays` to 1 or 7, fix a `postcode` and `radiusKm`, choose your `opportunityTags`, then schedule the run daily with Apify's scheduler. Results are deduplicated within a run, so pair it with your own key on `uid` to spot genuinely new records across runs.

#### Can I search by applicant or agent?

Yes, `developerText` searches applicant and agent names and addresses, which is how you track a particular architect or developer across councils. Note that applicant and agent fields only appear where the council publishes them.

### ⚖️ Is it legal to scrape planning applications?

Planning applications are a statutory public register every council must publish so neighbours and the public can see and comment on what is proposed. This Actor reads the PlanIt aggregator, which collects those council registers, and it never logs in.

Applicant and agent details appear because the council published them as part of the planning process, so onward use is your responsibility under GDPR. Apify's [ethical scraping guide](https://blog.apify.com/is-web-scraping-legal/) covers the wider picture.

### 👍 Your feedback

Found a bug, or want a field that is not here yet? Open an issue on the Actor's Issues tab. Requests that make the data more useful get built, and problems get fixed quickly.

### 🔎 You might also like

| Actor | What it does |
|---|---|
| [UK Food Hygiene Ratings](https://apify.com/spookyweb/uk-food-hygiene-ratings) | FSA hygiene ratings for every UK food business, with council environmental health contacts |
| [UK Insolvency Register Search](https://apify.com/spookyweb/uk-insolvency-register-search) | Individual Insolvency Register search for bankruptcies, IVAs and debt relief orders |
| [UK Hansard Parliamentary Debates](https://apify.com/spookyweb/uk-hansard-parliamentary-debates) | Full text UK parliamentary debates from the official Hansard record |

# Actor input Schema

## `recentDays` (type: `integer`):

Return applications submitted in the last N days. 0 means today only. Leave blank to use a specific date range instead.

## `startDate` (type: `string`):

Earliest application date, YYYY-MM-DD. Ignored if 'Applications from the last N days' is set.

## `endDate` (type: `string`):

Latest application date to return, as YYYY-MM-DD. Leave blank to search up to today. Pair it with the start date to pull a single week as a lead list.

## `areas` (type: `string`):

Comma separated council names, for example: Cambridge, Fylde, Southwark. Leave blank to search every UK council.

## `postcode` (type: `string`):

Search around a postcode instead of by council, for example CB1 2AB.

## `radiusKm` (type: `integer`):

Search radius around the postcode or coordinates. Keep this at 10 or below. PlanIt's own data source gives up after 45 seconds, and a radius above about 10km times out far more often than it succeeds, whatever date range you pair it with. Several smaller searches are faster and far more reliable than one wide one.

## `opportunityTags` (type: `array`):

Only return applications matching these work types. Leave empty for all.

## `applicationState` (type: `string`):

Filter by status. Undecided applications are the freshest leads.

## `applicationSize` (type: `string`):

Filter by scale of development as classified by PlanIt.

## `searchText` (type: `string`):

Free text search of the application description.

## `developerText` (type: `string`):

Search applicant and agent company names and addresses. Useful for tracking a competitor or a specific developer.

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

Hard cap on results. Each application is one billable result.

## `pageSize` (type: `integer`):

Records requested per API call. Leave as default unless you have a reason.

## Actor input object example

```json
{
  "recentDays": 7,
  "radiusKm": 5,
  "opportunityTags": [],
  "applicationState": "",
  "applicationSize": "",
  "maxResults": 500,
  "pageSize": 200
}
```

# Actor output Schema

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

One row per item: planning applications with applicant details.

## `runSummary` (type: `string`):

Counts and stop reason for the run.

# 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 = {
    "recentDays": 7
};

// Run the Actor and wait for it to finish
const run = await client.actor("wildorigins/uk-planning-applications").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 = { "recentDays": 7 }

# Run the Actor and wait for it to finish
run = client.actor("wildorigins/uk-planning-applications").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 '{
  "recentDays": 7
}' |
apify call wildorigins/uk-planning-applications --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,wildorigins/uk-planning-applications"
        }
    }
}
```

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/cefP5nrfbIEUc4LHf/builds/0ndtBdLDsmZ0Mhigf/openapi.json
