# UK Council Planning Applications Monitor (`illehius/uk-planning-monitor`) Actor

Monitor newly validated, decided, or updated planning applications across selected UK council portals (Idox PublicAccess, Northgate Planning Explorer). HTTP-only; designed for scheduled runs with onlyNew dedupe.

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

## Pricing

from $50.00 / 1,000 application founds

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 Council Planning Applications Monitor

Monitor newly validated, decided, or updated planning applications across selected UK council portals. Returns a normalized, alert-ready record per application.

Designed for **scheduled runs**: pair with Apify Schedules + an Apify Webhook (or Zapier/Make integration) to get notified when new applications appear. Pay-per-event pricing means you only pay for the applications surfaced.

### Supported Councils

Pass the **council ID** (left column) in the `councils` input. **21 councils** across two portal types are currently supported.

#### Idox Public Access (20)

| Council ID | Council |
|------------|---------|
| `woking` | Woking Borough Council |
| `westminster` | Westminster City Council |
| `brent` | Brent London Borough Council |
| `isle-of-wight` | Isle of Wight Council |
| `tendring` | Tendring District Council |
| `durham` | Durham County Council |
| `north-norfolk` | North Norfolk District Council |
| `north-tyneside` | North Tyneside Council |
| `leeds` | Leeds City Council |
| `bristol` | Bristol City Council |
| `cornwall` | Cornwall Council |
| `glasgow` | Glasgow City Council |
| `croydon` | London Borough of Croydon |
| `barnet` | London Borough of Barnet |
| `nottingham` | Nottingham City Council |
| `southwark` | London Borough of Southwark |
| `greenwich` | Royal Borough of Greenwich |
| `enfield` | London Borough of Enfield |
| `wakefield` | Wakefield Council |
| `aberdeen` | Aberdeen City Council |

#### Northgate Planning Explorer (1)

| Council ID | Council |
|------------|---------|
| `birmingham` | Birmingham City Council |

`failOnCouncilError` (boolean, default `false`) — when `false`, a single failing council records a `{status:'failed',error}` entry in the run summary and the run continues to remaining councils. When `true`, the run fails on the first council error.

More councils are added on request — most UK councils run Idox Public Access or Northgate Planning Explorer, both of which are already supported, so adding one is typically a small configuration change.

> **Scheduling tip for Westminster:** Westminster's weekly-list form exposes a 4-week rolling window of recent applications. If you're using `onlyNew: true` to track new applications, run at least every 4 weeks (a weekly schedule is well within bound). Brent and Woking have longer windows (52 and 16 weeks respectively).

### Input

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `councils` | string\[] | *required* | Council IDs (e.g. `"woking"`) or full portal URLs. |
| `dateMode` | enum | `validated` | One of `validated`, `decided`, `updated`. |
| `dateFrom` | string (YYYY-MM-DD) | 7 days ago | Start of search window. |
| `dateTo` | string (YYYY-MM-DD) | today | End of search window. |
| `keywords` | string\[] | `[]` | Case-insensitive match against proposal/description/address. |
| `postcodes` | string\[] | `[]` | Outward (e.g. `GU21`) or full postcode filters where the portal supports them. |
| `maxResultsPerCouncil` | integer | `100` | Cap per council per run. |
| `includeDetails` | boolean | `true` | Fetch detail pages to enrich status/ward/documents. |
| `includeDocuments` | boolean | `false` | Surface document-list metadata (title, type, date, URL). Documents are never downloaded. |
| `stateKey` | string | — | Optional key enabling monitoring mode (`onlyNew`, `firstSeenAt`, `lastSeenAt`). |
| `onlyNew` | boolean | `false` | When true, output only applications not seen in prior runs for the same `stateKey`. |

#### Example: weekly Woking watch

```json
{
  "councils": ["woking"],
  "dateMode": "validated",
  "stateKey": "weekly-woking",
  "onlyNew": true
}
```

### Output

Each application is one dataset record. Key fields:

| Field | Description |
|-------|-------------|
| `councilId` / `councilName` / `portalType` | Source identifiers. |
| `applicationRef` | Council's application reference (e.g. `PLAN/2026/0042`). |
| `address` / `postcode` | Site location. |
| `proposal` | Description of works. |
| `applicationType` / `status` / `decision` | Application category and current state. |
| `receivedDate` / `validatedDate` / `decisionDate` | Key dates. |
| `ward` / `parish` / `caseOfficer` | Local context. |
| `documents` | Document-list metadata (title, type, date, URL) — only when `includeDocuments: true`. Documents are never downloaded. |
| `firstSeenAt` / `lastSeenAt` / `isNew` | Monitoring-mode tracking (only populated when `stateKey` is set). |
| `detailUrl` / `documentsUrl` | Source-verifiable links back to the council portal. |

### Notes

- Document content is **never** downloaded. Only metadata (title, type, date, URL) is surfaced.
- Public comments are not scraped (personal-data risk).
- `applicantName` / `agentName` are returned only when the council exposes them publicly on detail pages.

### Reliability

- Transient network failures retry automatically with exponential backoff.
- Per-council error isolation: a single failing council does not fail the whole run unless `failOnCouncilError` is true. The summary records each council's status individually.

### Roadmap

- More Northgate Planning Explorer councils (the adapter is live — Birmingham is the first; Wandsworth needs detail-page enrichment for dates/decision before it ships).
- Additional Idox and Northgate councils by request.

# Actor input Schema

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

Array of council IDs or full portal URLs. Supported IDs — Idox Public Access: woking, westminster, brent, isle-of-wight, tendring, durham, north-norfolk, north-tyneside, leeds, bristol, cornwall, glasgow, croydon, barnet, nottingham, southwark, greenwich, enfield, wakefield, aberdeen; Northgate Planning Explorer: birmingham. More councils added on request.

## `dateMode` (type: `string`):

Which application date drives the search window: validated (default), decided, or updated.

## `dateFrom` (type: `string`):

Start of search window in YYYY-MM-DD. Defaults to 7 days ago when omitted.

## `dateTo` (type: `string`):

End of search window in YYYY-MM-DD. Defaults to today when omitted.

## `keywords` (type: `array`):

Optional array of keywords matched (case-insensitive) against proposal, description, or address. Empty = no keyword filter.

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

Optional outward (e.g. GU21) or full postcode filters; only applied where the portal supports postcode search.

## `maxResultsPerCouncil` (type: `integer`):

Hard cap on records returned per council per run.

## `includeDetails` (type: `boolean`):

Fetch each application's detail page to enrich status/decision/ward/document metadata. Disable for cheaper list-only runs.

## `includeDocuments` (type: `boolean`):

Surface the application's document list (title, type, date, url) without downloading the documents themselves. Copyright-safe; differentiator vs PlanIt.

## `stateKey` (type: `string`):

Optional key for monitoring/dedupe state. Stored namespaced by user. Use the same key across scheduled runs to enable onlyNew filtering and firstSeenAt/lastSeenAt tracking.

## `onlyNew` (type: `boolean`):

When true, output only applications not seen in prior runs for the same stateKey. Requires stateKey.

## `failOnCouncilError` (type: `boolean`):

When true, the run fails on the first council that errors. When false (default), per-council failures are recorded in SUMMARY but the run continues to remaining councils.

## Actor input object example

```json
{
  "councils": [
    "woking",
    "durham"
  ],
  "dateMode": "validated",
  "maxResultsPerCouncil": 100,
  "includeDetails": true,
  "includeDocuments": false,
  "onlyNew": false,
  "failOnCouncilError": false
}
```

# Actor output Schema

## `applications` (type: `string`):

Dataset of normalised planning applications. Each record carries councilId, applicationRef, address, postcode, proposal, status, validatedDate/decisionDate/receivedDate, detailUrl, documentsUrl, and (when includeDocuments=true) a documents\[] metadata array of {title,type,date,url} — documents are never downloaded.

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

Per-council status (ok/failed) and total counts (found/new/errored) 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 = {
    "councils": [
        "woking",
        "durham"
    ],
    "includeDetails": false
};

// Run the Actor and wait for it to finish
const run = await client.actor("illehius/uk-planning-monitor").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 = {
    "councils": [
        "woking",
        "durham",
    ],
    "includeDetails": False,
}

# Run the Actor and wait for it to finish
run = client.actor("illehius/uk-planning-monitor").call(run_input=run_input)

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

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

```

## CLI example

```bash
echo '{
  "councils": [
    "woking",
    "durham"
  ],
  "includeDetails": false
}' |
apify call illehius/uk-planning-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=illehius/uk-planning-monitor",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/1e65gdNsDseA5Ep9s/builds/bdCiSTTMLXmvhVuvA/openapi.json
