# UK Planning Applications Search — Council Portals (`muhammadafzal/planning-permission-uk-scraper-candidate`) Actor

Search UK council planning applications in Cardiff, Edinburgh, Highland, Southampton and Northern Ireland: proposal, address, status, dates. USD 10 per 1,000.

- **URL**: https://apify.com/muhammadafzal/planning-permission-uk-scraper-candidate.md
- **Developed by:** [Muhammad Afzal](https://apify.com/muhammadafzal) (community)
- **Categories:** Real estate, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $8.00 / 1,000 planning record delivereds

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 Search — Council Portals

UK Planning Applications Search finds planning applications on supported UK council planning portals and returns each application's reference, proposal, site address, council, nation, status, type, received, validated and decision dates, and a link to the public council record. It covers Cardiff, Edinburgh, Highland, Southampton and the Northern Ireland public register, plus England's national planning datasets, and costs USD 10 per 1,000 records on the FREE tier (USD 0.01 each) plus a USD 0.01 start fee per run.

It is built for solar and home-improvement installers, developers, land and property researchers, and planning consultants. It is not a search of every UK council.

### How do I search council planning applications?

Choose council presets and a search term. The term can be a keyword, an address, a postcode or an application reference. The prefilled run searches four councils for `solar`:

```json
{
  "source": "council-portals",
  "councils": ["cardiff", "edinburgh", "southampton", "northern-ireland"],
  "query": "solar",
  "maxResults": 20
}
```

The Actor takes one matching application from each council in turn, so the default run returns up to five per council. On 2026-10-26 it returned 20 applications (five each from Cardiff, Edinburgh, Southampton and Northern Ireland) in about 170 seconds.

To look up one application, select its council and use the reference as the query:

```json
{ "source": "council-portals", "councils": ["northern-ireland"], "query": "LA07/2026/1284/F", "maxResults": 1 }
```

### Which UK councils does it cover?

| Preset | Nation | Coverage |
| --- | --- | --- |
| `cardiff` | Wales | Cardiff Council planning portal |
| `edinburgh` | Scotland | City of Edinburgh Council planning portal |
| `highland` | Scotland | Highland Council planning portal |
| `southampton` | England | Southampton City Council planning portal |
| `northern-ireland` | Northern Ireland | Public planning register for ten councils and DfI (Mid Ulster is not included); `authority` names the council |

You can add up to 10 more councils that use the same Idox Public Access system: pass the portal root in `portals`, for example `[{"url":"https://wam.highland.gov.uk/wam/","nation":"Scotland","authority":"Highland Council"}]`. Custom roots must be public HTTPS `.gov.uk` addresses ending in `/`. Councils on other portal software are not supported. You can also pass up to 20 Idox `applicationDetails.do?keyVal=...` links in `startUrls` to read those applications directly.

### Can I search England's national planning data and constraints?

Yes. Set `source` to `planning-data` and clear `query` (the Console prefill `solar` is rejected in this mode). Then choose dataset slugs such as `planning-application`, `listed-building`, `conservation-area`, `green-belt`, `flood-risk-zone`, `article-4-direction-area` or `tree-preservation-zone`, and filter by one location method: `postcodeOrUprn`, a `latitude`/`longitude` pair, or `geometryWkt`.

```json
{
  "source": "planning-data",
  "query": "",
  "datasets": ["conservation-area"],
  "latitude": 51.5074,
  "longitude": -0.1278,
  "maxResults": 20
}
```

`reference` and `organisationEntity` (for example `26` for Adur) narrow national results further. National coverage varies by authority and is England only.

| Input | Default | Notes |
| --- | --- | --- |
| `source` | `council-portals` | Or `planning-data` for England national datasets |
| `councils` | four presets | Up to 5 of the presets above |
| `query` | `solar` | Keyword, address, postcode or reference. Council mode only |
| `startDate` / `endDate` | empty | Inclusive received (or validated, see below) or entity start date. Undated records are excluded when set |
| `maxResults` | 20 | 1–1,000 records per run |
| `responseFormat` | `detailed` | `concise` drops polygon geometry |
| `maxPages` / `maxRequests` / `maxRuntimeSecs` | 20 / 200 / 260 | Per-source pages, total requests and runtime budget |
| `pageSize` / `requestDelayMs` | 20 / 500 | National and NI page size; pause between requests (council portals use at least 2 seconds) |

Each response is capped at 5 MB and each run at 20 MB of downloads. When a budget runs out, the Actor keeps the records it already saved and reports the stop reason.

### What data does it return?

| Field | What it means |
| --- | --- |
| `reference` | Council or national planning reference |
| `description` | The proposal text |
| `address` | Site address |
| `authority` / `nation` | Council and UK nation |
| `applicationStatus` / `applicationType` | Status and type as the register shows them |
| `startDate` / `validatedDate` / `decisionDate` | Received (or entity start), validated and decision dates. Where a council publishes no received date, `startDate` is the validated date |
| `planningDecision` / `planningDecisionType` | Decision and route, where published |
| `recordType` / `dataset` / `entityId` / `typology` / `name` | Application, constraint or entity classification and national IDs |
| `developmentClassification` / `organisationEntity` / `endDate` / `entryDate` | National dataset fields |
| `geometryWkt` / `pointWkt` | National polygon and point (polygon omitted in `concise`) |
| `documentationUrl` / `sourceUrl` | Public council record or national source page |
| `sourceSystem` / `retrievedAt` / `warnings` | Source adapter, UTC time and data-quality notes |

Fields a source does not publish are `null`. Edinburgh and Southampton publish a validated date but no received date, so their `startDate` holds the validated date, the record's `warnings` say so, and date filters include their applications. `applicationType` was filled for Northern Ireland only.

The `OUTPUT` record gives the outcome (`SUCCEEDED`, `PARTIAL`, `EMPTY`, `BLOCKED`, `LIMIT`, `REJECTED` or `FAILED`), records saved, charged events, requests, per-council results, warnings and the stop reason. Read it after every run: a council that failed or blocked leaves the search incomplete.

### When should you use it, and when not?

Use it to:

- find new solar, extension or change-of-use applications in the covered councils for sales outreach;
- check a site's planning permission history in Cardiff, Edinburgh, Highland, Southampton or Northern Ireland;
- pull conservation areas, listed buildings or other constraints around an England location.

Do not use it for:

- councils outside the list and not on Idox: use the council's own portal;
- England national data only, without council portals: [Planning Permission UK Scraper — Applications & Constraints](https://apify.com/muhammadafzal/planning-permission-uk-scraper) is built for that source;
- house sale prices: use [UK Land Registry Prices Scraper](https://apify.com/muhammadafzal/uk-land-registry-prices-scraper);
- documents, drawings, comments or applicant contact details, which it never collects.

### How do I run it from code?

Call the Actor with the id `muhammadafzal/planning-permission-uk-scraper-candidate`.

JavaScript (`apify-client`):

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

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('muhammadafzal/planning-permission-uk-scraper-candidate').call({
  source: 'council-portals',
  councils: ['edinburgh'],
  query: 'heat pump',
  maxResults: 10,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.map((i) => [i.reference, i.applicationStatus, i.address]));
```

Python (`apify-client`):

```python
import os
from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("muhammadafzal/planning-permission-uk-scraper-candidate").call(
    run_input={"source": "council-portals", "councils": ["northern-ireland"], "query": "solar", "maxResults": 25},
)
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item["authority"], item["reference"], item["description"][:80])
```

### Use it from an AI assistant

UK Planning Applications Search works from Claude, ChatGPT, Cursor or any MCP client through Apify's MCP server. Add `https://mcp.apify.com/?tools=muhammadafzal/planning-permission-uk-scraper-candidate` as a server, then ask, for example: "Find five recent solar panel planning applications in Cardiff and give me the reference, address and status." The agent sees the input schema and the price per record before it runs.

### What does the output look like?

Two records from the default run on 2026-10-26:

```json
[
  {
    "sourceSystem": "ni-register",
    "nation": "Northern Ireland",
    "authority": "Newry, Mourne and Down District Council",
    "applicationStatus": "Valid",
    "applicationType": "Full",
    "validatedDate": null,
    "recordType": "planning-application",
    "entityId": null,
    "dataset": "planning-application",
    "reference": "LA07/2026/1284/F",
    "description": "Storage building for cars, pickup truck, tractor, and associated garden machinery and a sailing boat, with roof mounted solar panels. (amended description)",
    "address": "20 Greenpark Road, Newry BT34 3EZ",
    "startDate": "2026-10-17",
    "decisionDate": null,
    "planningDecision": null,
    "geometryWkt": null,
    "documentationUrl": "https://planningregister.planningsystemni.gov.uk/application/713943",
    "sourceUrl": "https://planningregister.planningsystemni.gov.uk/application/713943",
    "retrievedAt": "2026-10-26T07:22:56.192Z",
    "warnings": []
  },
  {
    "sourceSystem": "idox",
    "nation": "Wales",
    "authority": "Cardiff Council",
    "applicationStatus": "Registered",
    "applicationType": null,
    "validatedDate": "2026-10-12",
    "recordType": "planning-application",
    "reference": "26/02146/HSE",
    "description": "Erection of a double storey side extension with solar panels on the existing & proposed roof",
    "address": "5 Heol Dyfed Heath Cardiff CF14 4PN",
    "startDate": "2026-10-12",
    "documentationUrl": "https://www.cardiffidoxcloud.wales/publicaccess/applicationDetails.do?keyVal=TMSH8MECGZU00&activeTab=summary",
    "retrievedAt": "2026-10-26T07:21:44.411Z",
    "warnings": []
  }
]
```

Items are shortened; every record carries the full field set.

### How much does a UK planning application search cost? Pricing

You pay per planning record saved plus a start fee. Apify platform usage is included. Each record is saved first and then charged once. A search with no match pays only the start fee.

| Event (USD) | FREE | BRONZE | SILVER | GOLD, PLATINUM, DIAMOND |
| --- | --- | --- | --- | --- |
| Planning record delivered | 0.01 | 0.00975 | 0.0095 | 0.008 |
| Actor start, per started GB of allocated memory | 0.01 | 0.00975 | 0.0095 | 0.008 |

The default memory is 256 MB, so each run has one start event.

- **Default 20 records, FREE tier:** USD 0.01 + 20 × 0.01 = **USD 0.21**.
- **100 records, GOLD tier:** USD 0.008 + 100 × 0.008 = **USD 0.808**.

### FAQ

#### Does it cover every UK council?

No. It covers the five presets, any Idox Public Access council you add by URL, and England's national planning datasets.

#### Can I search by postcode?

In council mode, put the postcode in `query`; the portal decides how it matches. In national mode, use `postcodeOrUprn`.

#### Does it download planning documents or drawings?

No. It returns the application summary and a link to the council record.

#### How current is the data?

Each run reads the live council registers, so new applications appear as soon as the council publishes them.

#### What if a council portal is down or blocks access?

Other councils continue. The failed council is marked `FAILED` or `BLOCKED` in `OUTPUT`, and the run is reported as `PARTIAL` when other councils returned data.

#### Is planning data free to reuse?

Council registers are public, but reuse terms differ. National Planning Data records may be under the Open Government Licence where stated. Check each source before republishing.

### Related Actors

- [Planning Permission UK Scraper — Applications & Constraints](https://apify.com/muhammadafzal/planning-permission-uk-scraper): England national applications and constraints with spatial filters.
- [UK Land Registry Prices Scraper](https://apify.com/muhammadafzal/uk-land-registry-prices-scraper): official sale prices for England and Wales.
- [Building Permits Scraper: Contractor Leads](https://apify.com/muhammadafzal/building-permit-lead-scraper): US building permits with project values.
- [NYC Property Sales Scraper](https://apify.com/muhammadafzal/nyc-property-sales-scraper) and [NJ Property Tax Records](https://apify.com/muhammadafzal/nj-property-tax-assessment-lookup): official US property records.

### Legal and compliance note

The Actor collects public application metadata and links, not documents, drawings, comments or applicant contacts. Follow each council's terms and UK data-protection law when you store or use addresses. A search with no results does not prove a site has no planning history. The Actor does not bypass logins or CAPTCHAs and is not affiliated with any council, the UK governments or portal providers.

Last verified: 2026-10-27

# Changelog

This Actor's version history is a separate document: https://apify.com/muhammadafzal/planning-permission-uk-scraper-candidate/changelog.md

# Actor input Schema

## `source` (type: `string`):

Use council-portals for supported Idox councils and the Northern Ireland register. Use planning-data for England applications and constraints.

## `councils` (type: `array`):

Built-in presets: cardiff (Wales), edinburgh/highland (Scotland), southampton (England), northern-ireland (10 councils plus DfI; excludes Mid Ulster). Used only in council-portals search mode.

## `query` (type: `string`):

Keyword, address, postcode, or application reference for council search. Example: solar or LA04/2025. Clear this in planning-data mode. A query searches within selected portals; it does not choose councils.

## `portals` (type: `array`):

Optional public Idox portal roots, e.g. \[{"url":"https://wam.highland.gov.uk/wam/","nation":"Scotland","authority":"Highland Council"}]. HTTPS .gov.uk hosts only; include the trailing slash. Other portal systems are unsupported.

## `startUrls` (type: `array`):

Optional direct applicationDetails.do URLs with keyVal from built-in or additional Idox portals. If supplied, reads these application summaries instead of searching. Maximum 20 URLs.

## `datasets` (type: `array`):

Use this to choose official Planning Data dataset slugs. Example: \['planning-application'] for applications or \['listed-building','conservation-area'] for constraints. Defaults to planning-application and accepts up to 10 slugs. This is not a council portal name or a free-text keyword search.

## `postcodeOrUprn` (type: `string`):

Use this to search by a UK postcode or Unique Property Reference Number. Example: 'BN15 0EE' or '10008315764'. Defaults to no location filter. This is not a council name, planning reference, or latitude/longitude pair.

## `reference` (type: `string`):

Use this to match an authority planning reference such as 'AWDM/0550/25'. Example: 'AWDM/0550/25'. Defaults to no reference filter. This is not the numeric Planning Data entity ID; use postcodeOrUprn or the dataset search for that.

## `organisationEntity` (type: `integer`):

Restrict national results to a Planning Data organisation entity ID, e.g. 26 for Adur. This is not a council postcode or planning reference.

## `latitude` (type: `number`):

Use this with longitude to search entities intersecting a point. Example: 51.5074. Defaults to no coordinate filter. This is WGS84 decimal latitude, not a British National Grid easting.

## `longitude` (type: `number`):

Use this with latitude to search entities intersecting a point. Example: -0.1278. Defaults to no coordinate filter. This is WGS84 decimal longitude, not a British National Grid northing.

## `geometryWkt` (type: `string`):

Use this for a custom WGS84 Well-Known Text geometry, such as 'POLYGON((-0.15 51.50,-0.10 51.50,-0.10 51.52,-0.15 51.52,-0.15 51.50))'. Defaults to no geometry filter. This is not GeoJSON; use coordinates for a single point.

## `geometryRelation` (type: `string`):

Use this to select the spatial relation between geometryWkt and returned entities. Example: 'intersects'. Defaults to intersects. This applies only to geometryWkt, not postcode, UPRN, or coordinates.

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

Use this as the inclusive lower date for entity start dates. Example: '2025-05-01'. Defaults to no lower date filter. This is an ISO date, not a council decision date or a relative phrase like 'last week'.

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

Inclusive upper bound on application received date (validated date for councils that publish no received date) or national entity start date. Undated records are excluded when either date bound is set. This never filters entity end-date.

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

Use this to cap stored records and result-event charges. Example: 100. Defaults to 20 and accepts 1-1,000; council searches take one record from each selected council in turn. This limits output rows, not the number of matching records available from the source.

## `responseFormat` (type: `string`):

Use detailed when you need geometry WKT retained, or concise for smaller AI-agent output. Example: 'detailed'. Defaults to detailed. This changes geometry inclusion only; applications and constraints use the same record fields.

## `requestDelayMs` (type: `integer`):

Pace every source request. Default 500 ms; accepted range 100–5,000 ms. Idox council requests have a 2-second minimum; runtime and request budgets are separate.

## `maxPages` (type: `integer`):

Bounds pages of search results; requests are also capped separately.

## `maxRequests` (type: `integer`):

Run-wide request cap, including retries, redirects and application details.

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

Page size for Planning Data and Northern Ireland. Idox chooses its own page size.

## `maxRuntimeSecs` (type: `integer`):

Stop with retained partial results after this runtime budget; cloud timeout is a separate bound.

## Actor input object example

```json
{
  "source": "council-portals",
  "councils": [
    "cardiff",
    "edinburgh",
    "southampton",
    "northern-ireland"
  ],
  "query": "solar",
  "portals": [],
  "startUrls": [],
  "datasets": [
    "planning-application"
  ],
  "postcodeOrUprn": "BN15 0EE",
  "reference": "AWDM/0550/25",
  "organisationEntity": 26,
  "latitude": 51.5074,
  "longitude": -0.1278,
  "geometryWkt": "POLYGON((-0.15 51.50,-0.10 51.50,-0.10 51.52,-0.15 51.52,-0.15 51.50))",
  "geometryRelation": "intersects",
  "startDate": "2025-05-01",
  "endDate": "2025-12-31",
  "maxResults": 100,
  "responseFormat": "detailed",
  "requestDelayMs": 500,
  "maxPages": 20,
  "maxRequests": 200,
  "pageSize": 20,
  "maxRuntimeSecs": 260
}
```

# Actor output Schema

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

One typed application, constraint, or planning entity per dataset item.

## `summary` (type: `string`):

Request, pagination, result, billing, and warning summary.

# 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 = {
    "source": "council-portals",
    "councils": [
        "cardiff",
        "edinburgh",
        "southampton",
        "northern-ireland"
    ],
    "query": "solar",
    "portals": [],
    "startUrls": [],
    "datasets": [
        "planning-application"
    ],
    "postcodeOrUprn": "",
    "reference": "",
    "geometryWkt": "",
    "geometryRelation": "intersects",
    "maxResults": 20,
    "responseFormat": "detailed",
    "requestDelayMs": 500,
    "maxPages": 20,
    "maxRequests": 200,
    "pageSize": 20,
    "maxRuntimeSecs": 260
};

// Run the Actor and wait for it to finish
const run = await client.actor("muhammadafzal/planning-permission-uk-scraper-candidate").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 = {
    "source": "council-portals",
    "councils": [
        "cardiff",
        "edinburgh",
        "southampton",
        "northern-ireland",
    ],
    "query": "solar",
    "portals": [],
    "startUrls": [],
    "datasets": ["planning-application"],
    "postcodeOrUprn": "",
    "reference": "",
    "geometryWkt": "",
    "geometryRelation": "intersects",
    "maxResults": 20,
    "responseFormat": "detailed",
    "requestDelayMs": 500,
    "maxPages": 20,
    "maxRequests": 200,
    "pageSize": 20,
    "maxRuntimeSecs": 260,
}

# Run the Actor and wait for it to finish
run = client.actor("muhammadafzal/planning-permission-uk-scraper-candidate").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 '{
  "source": "council-portals",
  "councils": [
    "cardiff",
    "edinburgh",
    "southampton",
    "northern-ireland"
  ],
  "query": "solar",
  "portals": [],
  "startUrls": [],
  "datasets": [
    "planning-application"
  ],
  "postcodeOrUprn": "",
  "reference": "",
  "geometryWkt": "",
  "geometryRelation": "intersects",
  "maxResults": 20,
  "responseFormat": "detailed",
  "requestDelayMs": 500,
  "maxPages": 20,
  "maxRequests": 200,
  "pageSize": 20,
  "maxRuntimeSecs": 260
}' |
apify call muhammadafzal/planning-permission-uk-scraper-candidate --silent --output-dataset

```

## MCP server setup

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

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/YiRGfQOGeiVnP2mLz/builds/HqqiGJVgdNtUMXdjZ/openapi.json
