UK Planning Applications & Site Constraints (Planning Data) avatar

UK Planning Applications & Site Constraints (Planning Data)

Pricing

from $1.40 / 1,000 planning record returneds

Go to Apify Store
UK Planning Applications & Site Constraints (Planning Data)

UK Planning Applications & Site Constraints (Planning Data)

Read the UK Planning Data register: planning applications with reference, site address, coordinates, proposal, decision and decision date, plus the designations covering a site — conservation area, article 4, listed building outline, flood risk zone. Filter by council, radius, text or date.

Pricing

from $1.40 / 1,000 planning record returneds

Rating

0.0

(0)

Developer

Samat Makatov

Samat Makatov

Maintained by Community

Actor stats

0

Bookmarked

1

Total users

1

Monthly active users

2 days ago

Last modified

Share

Read the official UK Planning Data platform (planning.data.gov.uk, run by the Ministry of Housing, Communities & Local Government) as flat rows. Two jobs in one actor: the planning-application register — council reference, site address and postcode, coordinates, the proposal text, the decision and its date, a link to the council's own file where the council publishes one — and a site constraint check: which designations cover a coordinate (conservation area, article 4 direction, listed building outline, tree preservation zone, flood risk zone, green belt and eight more layers).

Filter by council, by a circle around a coordinate, by keyword, by decision wording, by decision date or by reference. Every date is a UTC calendar date, every row links back to the record it came from. No API key, no proxy, no browser, no council portal is touched.

Application coverage is partial and the platform says so itself — read Coverage before you plan a project around it. Designation layers are national.

Use cases

  • Planning history of a site (surveyors, developers, architects): every application within 300 m–2 km of a coordinate, with the proposal, the decision and the date, as the evidence base for a new application.
  • Appeal and refusal research (planning consultants): one council filtered to decisionContains: "refus", newest decision first, to build a precedent list of what was turned down and why it was proposed.
  • Lead pipelines for trades and suppliers: approved works that mention solar, heat pump, loft conversion or basement, each with the site address and, for Adur and Worthing, a direct link to the council's application file.
  • Local reporting and analysis: one authority and one year (decisionDateFrom / decisionDateTo) as a spreadsheet — the raw material for "how many extensions were refused in 2023".
  • Site constraint checks before buying or extending (owners, conveyancers, solar installers): mode: "constraints" with radiusMeters: 0 answers "is this spot inside a conservation area, an article 4 direction or a flood risk zone", each answer with the designation's name, reference, publishing authority and a GeoJSON boundary link.
  • Heritage screening (heritage consultants, retrofit assessors): listed building outlines and conservation areas within a radius of an address, to see what sits around a site and not only on it.
  • Keeping a copy in sync: onlyNew: true in a scheduled task writes only records the task has not delivered before, so a warehouse table grows by the batch the platform publishes.

Coverage

The designation datasets are national and maintained: at the time of writing the flood-risk-zone layer alone held 780,636 rows, and an article 4 row at the test coordinate carried a register date of 2026-08-10.

The planning-application register is not national. The platform marks it "incomplete and not yet ready for use", and it is a snapshot republished in batches, not a live feed. Counted on 2026-09-19 it held 100,627 applications from four authorities:

CouncilShort codeOrganisation entityRowsSite addressCoordinatesReceived dateDecision wordingCouncil file link
London Borough of CamdenCMD9077,499yesyesnoin decisionType (Granted, Refused, …)no
Worthing Borough CouncilWOT38213,629yesyesyesdecision (Approve) + decisionType (Delegated)yes
Adur District CouncilADU267,585yesyesyesdecision + decisionTypeyes
Doncaster Metropolitan Borough CouncilDNC1091,914nonononone, only decisionDateno

Nothing in the actor hard-codes those four. Councils are resolved against the platform's live local-authority register (379 authorities), so an authority the platform adds later works without an update here. A council that resolves but has no applications in the register simply returns zero rows, and the run says so.

Input

Nothing is required. With no input at all the actor reads the register from its start; the prefills (Camden, the Primrose Hill coordinate, the six everyday designation layers) make the first run a real search.

FieldTypeDefaultAllowed values / notes
modestringapplicationsapplications = the register, constraints = designations covering a point — see Modes
councilstring(prefill Camden)Name or short code (Camden, London Borough of Camden, CMD). Resolved against the live register — see Councils. Empty = every council. Not used in constraints
latitudenumber(prefill 51.5413)WGS84, 49.5 … 61.0. Required for constraints
longitudenumber(prefill -0.1543)WGS84, −9.0 … 2.5, negative west of Greenwich. Works only together with latitude
radiusMetersinteger10000 … 20000. 0 in constraints = the exact point; 0 in applications is read as 1000
datasetsstring[](prefill: the six everyday layers)Which designation layers constraints asks about — see Designation datasets. Empty = the six defaults
textContainsstring—Case-insensitive text in the proposal, the address or the reference (solar, loft conversion, BN43). Applied by this actor over maxScanRows
decisionContainsstring—Case-insensitive text in either decision field (granted, refus, approve, withdrawn, delegated) — see Decision wording
decisionDateFromstring—YYYY-MM-DD, inclusive. Applied by this actor over maxScanRows
decisionDateTostring—YYYY-MM-DD, inclusive
referencestring—Exact council reference (2011/0724/C, AWDM/1227/21). Applied by the API, so it is the cheapest lookup
entryDateSincestring—YYYY-MM-DD. The date the platform loaded the record, not the date it was submitted or decided
onlyNewbooleanfalseOutput only entity ids no earlier run with the same input delivered (state in a named key-value store)
maxScanRowsinteger5000100 … 100000. How many register rows a run may read before the actor's own filters. Never lower than maxItems
sortBystringsourceOrdersourceOrder, decisionDateDesc, decisionDateAsc — sorted among the rows the run read, see Limits
maxItemsinteger251 … 5000 output rows
fieldsstring[]allKeep only these output fields, in this order

Filters combine with AND. The API applies council, the circle around the coordinate, reference and entryDateSince; this actor applies textContains, decisionContains, the decision-date window and the exact distance to your coordinate, over the rows the run reads. That split is a property of the source (see Limits & FAQ), and every run reports it in its SUMMARY record.

Reference

Modes

modeWhat one row isNeedsTypical input
applicationsOne planning application from the registernothing (council / coordinate / reference recommended){"council":"Camden","radiusMeters":800,"latitude":51.5413,"longitude":-0.1543}
constraintsOne designation that covers the point or the circlelatitude + longitude{"mode":"constraints","latitude":51.5413,"longitude":-0.1543,"radiusMeters":0}

In constraints mode council, decisionContains and the decision-date window have nothing to act on and are reported as unused in the run log and SUMMARY; textContains is matched against the designation name and reference.

Councils

council accepts the short code or any form of the name; the value is matched against the platform's live local-authority register in this order: short code, exact name, name without its legal suffix ("Camden" → "London Borough of Camden"), a name containing what you typed, and finally a small typo budget ("camdon" → Camden, reported in the run status). An authority that was abolished is used only when no active authority matches, so "Buckinghamshire" resolves to the unitary council and not to the county council that closed in 2020.

Two things never happen: the search is never widened silently, and an ambiguous name never picks a winner. "North" ends the run with the 18 candidates listed; an unknown name ends it with the five closest names. The four authorities whose applications the register holds today are in Coverage; every other council resolves but has no application rows yet.

Designation datasets

datasets (used by mode: "constraints"):

idLayer
conservation-areaConservation area
article-4-direction-areaArticle 4 direction area (permitted development removed)
listed-building-outlineListed building outline
tree-preservation-zoneTree preservation zone
scheduled-monumentScheduled monument
world-heritage-siteWorld heritage site
site-of-special-scientific-interestSite of special scientific interest
flood-risk-zoneFlood risk zone
green-beltGreen belt
ancient-woodlandAncient woodland
brownfield-landBrownfield land register entry
national-parkNational park
area-of-outstanding-natural-beautyNational landscape (AONB)
local-authority-districtLocal authority district

The first six are the default. A typed id is corrected when it is close enough (conservation-areas → conservation-area) and reported; an id the platform does not publish ends the run with the list above. Not every council has published every layer — a layer with no row at your point simply returns nothing, which is an answer, not an error.

Decision wording

Councils word decisions differently and the platform passes the words through, so the actor does too: decision is the council's decision, decisionType its route or its own classification, and decisionContains searches both.

decisionContains matches a substring, so refus also returns Camden's Part Granted/Refused rows — outright refusals are the rows whose wording is exactly Refused (Camden) or Refuse (Worthing, Adur), and mixed outcomes always carry Part in front.

CouncildecisiondecisionTypestatus
Camden(empty — the wording is in decisionType)Granted, Refused, Withdrawn Decision, Granted Subject to a Section 106 Legal Agreement, No Objection to Works to Tree(s) in CA, Approve Works (TPO), Part Granted/Refused, …Final Decision, Withdrawn, Appeal Decided, Appeal Lodged, Registered
Worthing, AdurApprove, Refuse, Withdrawn, Split, Prior not required - prior approval not requiredDelegated, CommitteeWithdrawn or empty
Doncaster(empty)(empty)(empty)

Use a fragment, not a whole word: refus catches both Refused (Camden) and Refuse (Worthing/Adur), grant catches Granted and Part Granted/Refused.

Examples

Planning history around a site in Camden (800 m)

{ "mode": "applications", "council": "Camden", "latitude": 51.5413, "longitude": -0.1543,
"radiusMeters": 800, "maxScanRows": 3000, "maxItems": 30 }

Refusals in one council, newest decision first

{ "mode": "applications", "council": "Camden", "decisionContains": "refus",
"sortBy": "decisionDateDesc", "maxScanRows": 4000, "maxItems": 25 }

Approved works with a link to the council's file (Worthing)

{ "mode": "applications", "council": "Worthing", "decisionContains": "approve",
"maxScanRows": 2000, "maxItems": 30 }

Solar work as a sales pipeline (keyword over a whole council)

{ "mode": "applications", "council": "Worthing", "textContains": "solar",
"maxScanRows": 15000, "maxItems": 20 }

One council, one year, as a table

{ "mode": "applications", "council": "Doncaster", "decisionDateFrom": "2023-01-01",
"decisionDateTo": "2023-12-31", "sortBy": "decisionDateAsc", "maxScanRows": 2000, "maxItems": 25 }

What binds this plot? (exact point)

{ "mode": "constraints", "latitude": 51.5413, "longitude": -0.1543, "radiusMeters": 0,
"datasets": ["conservation-area", "article-4-direction-area", "listed-building-outline",
"tree-preservation-zone", "flood-risk-zone", "green-belt"], "maxItems": 20 }

Listed buildings and conservation areas within 300 m of an address

{ "mode": "constraints", "latitude": 51.5197, "longitude": -0.127, "radiusMeters": 300,
"datasets": ["listed-building-outline", "conservation-area"], "maxItems": 25 }

One application by its reference, trimmed to four fields

{ "mode": "applications", "reference": "2011/0724/C",
"fields": ["reference", "address", "decision", "decisionDate", "url"], "maxItems": 5 }

Output

One row per application (or per designation). A real row from the cloud run of the first example above (run nzp1RvTtCtCIe8APz):

{
"entityId": 10000021615,
"dataset": "planning-application",
"reference": "2015/4843/L",
"name": null,
"council": "London Borough of Camden",
"councilEntity": 90,
"description": "Erection of a lower ground floor extension and internal alterations namely the formation of a small opening to the side wall to connect to the lower ground floor extension, the removal of non-original partitions, repairs to structural walls and the renewal of internal fixtures and fittings.",
"address": "11 Prince Albert Road London NW1 7SR",
"postcode": "NW1 7SR",
"latitude": 51.537596,
"longitude": -0.150267,
"receivedDate": null,
"decisionDate": "2015-10-09",
"decision": "Granted",
"decisionType": "Granted",
"status": "Final Decision",
"developmentClassification": null,
"documentationUrl": null,
"entryDate": "2025-09-09",
"url": "https://www.planning.data.gov.uk/entity/10000021615",
"geojsonUrl": "https://www.planning.data.gov.uk/entity/10000021615.geojson",
"fetchedAt": "2026-09-19T22:14:18.683Z"
}
FieldTypeFilled when
entityIdnumberAlways (the platform's own record id); null only in the "not found" row of a reference lookup
datasetstringAlways — planning-application or the designation layer the row came from
referencestringApplications: the council's reference. Designations: the authority's reference (CA29, LB1694)
namestringDesignations (the designation's name). Applications carry no name
councilstringThe authority that published the row, resolved from councilEntity; national layers name their body (e.g. Environment Agency)
councilEntitynumberThe publishing organisation's entity id
descriptionstringApplications: the proposal text. Camden, Worthing, Adur and Doncaster all fill it
addressstringApplications, where the council publishes a site address (not Doncaster)
postcodestringParsed out of address when it ends in a UK postcode
latitude, longitudenumberApplications: the published point (not Doncaster). Constraints: the coordinate you asked about, echoed into every row
receivedDatedateApplications: the date the council received it (Worthing and Adur). Designations: the date the designation started
decisionDatedateApplications, where the council published a decision date
decisionstringThe council's decision wording; falls back to decisionType for councils that record it there
decisionTypestringThe council's own route or classification (Delegated, Committee, Granted)
statusstringCamden's application status (Final Decision, Appeal Lodged, …); other councils leave it empty
developmentClassificationstringAdur and Worthing: the classification URI the council publishes
documentationUrlstringLink to the application file on the council's portal — Adur and Worthing only
entryDatedateWhen the platform loaded the record (the batch date, not the application date)
urlstringThe record page on the platform
geojsonUrlstringThe record's GeoJSON — a point for applications, the boundary polygon for designations
fetchedAtdate-timeUTC timestamp of the run
foundbooleanOnly on the single row a reference lookup writes when the register holds no such reference

Boundary polygons are never written into rows (one conservation area is 50 KB of WKT) — geojsonUrl serves them.

Dataset views: Planning applications (overview), Applications with coordinates (map), Designations covering the site (constraints).

The SUMMARY record in the run's key-value store holds the resolved council, the first request URL, how many rows matched server-side, how many rows were read, whether maxScanRows stopped the scan, how many rows a filter or the onlyNew state dropped, the request count, every input warning and the licence note.

Use it from code / agents

curl -X POST "https://api.apify.com/v2/acts/yadroo~uk-planning-applications/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"council":"Camden","latitude":51.5413,"longitude":-0.1543,"radiusMeters":800,"maxItems":30}'
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('yadroo/uk-planning-applications').call({
mode: 'constraints', latitude: 51.5413, longitude: -0.1543, radiusMeters: 0,
datasets: ['conservation-area', 'article-4-direction-area', 'flood-risk-zone'], maxItems: 20,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
from apify_client import ApifyClient
client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("yadroo/uk-planning-applications").call(run_input={
"council": "Worthing", "textContains": "heat pump", "maxScanRows": 15000, "maxItems": 20,
})
items = client.dataset(run["defaultDatasetId"]).list_items().items

The actor is a thin wrapper over one government API, which makes it easy to hand to an agent as a tool: one JSON input, one flat row shape for both questions, and fields to keep answers short enough for a prompt — {"council":"Camden","radiusMeters":500,"latitude":51.5413,"longitude":-0.1543,"fields":["reference","address","description","decision","decisionDate"]}.

MCP: add https://mcp.apify.com to Claude, Cursor or any MCP client and call the yadroo/uk-planning-applications tool with the same JSON input.

Pricing

Pay per event: $0.001 per run start + $0.002 per row written to the dataset. Nothing else is charged — no proxy, no browser, 256 MB of memory.

RunCost
Site check: 3 designations at one coordinate$0.007
The default 25-row search around a Camden coordinate$0.051
Keyword pipeline: 20 solar applications out of a whole council$0.041
A 500-row extract of one council$1.001

Apify tiers reduce the per-row price automatically: Bronze −10 %, Silver −20 %, Gold and above −30 %.

Limits & FAQ

  • Application coverage is partial. Four authorities, 100,627 rows, 77 % of them Camden (see Coverage). If you need a council that is not in the register, this actor cannot give it to you — it reads the national platform, not council portals.
  • A snapshot, not a live feed. The register is republished in batches; the rows loaded on 2026-09-09 (Camden) and 2026-09-07 (Worthing, Adur) carry those dates in entryDate. entryDateSince and onlyNew work on that load date, so "new" means "new in the platform's batch", not "submitted this week".
  • No text search in the API. The platform has no keyword search over proposals, so textContains, decisionContains and the decision-date window are applied to the rows the run reads. maxScanRows is that budget: 15,000 covers Worthing or Adur completely, Camden's 77,499 rows need a higher cap or a coordinate filter. When the cap stops a scan the run status and SUMMARY say so — a partial answer is never presented as a complete one.
  • No sort in the API either. Rows arrive in the order the platform ingested them, so decisionDateDesc orders what the run read, not the whole register. Over a full council scan the two are the same thing; over a capped scan they are not.
  • Doncaster rows have no address and no point. They carry reference, proposal and decision date only, so a coordinate filter can never return them, and their rows leave address, postcode, latitude, longitude empty.
  • start_date filtering is broken upstream. The platform's "received since" filter returns nothing on this dataset even for rows that carry a received date, so the actor does not offer it; filter on decisionDate instead, or read the council and filter receivedDate yourself.
  • Dates are passed through. A handful of council records carry obviously wrong decision dates (one Worthing row says 2046). The actor does not invent or correct them, it only converts to YYYY-MM-DD in UTC.
  • Radius is exact. The circle is sent to the API as a polygon drawn around your coordinate and every returned row is then re-measured, so a 300 m search returns nothing 301 m away. Distances use WGS84 great-circle metres.
  • Designation layers are not complete either. They are national where the data exists, but a council that never published, say, its tree preservation zones has none in the platform. No row at your point means "nothing published here", not "certainly unconstrained" — a formal search still belongs to the council.
  • No personal data. The register publishes proposals and site addresses, and those are what the rows carry. Applicant and agent names are not in the platform's records and are never collected.
  • Rate limits and blocks. The platform publishes no rate limit; the actor uses one request at a time with retry and backoff, and a normal run makes 2–30 requests. It calls www.planning.data.gov.uk and nothing else — the council portal links in documentationUrl are passed through as text and never fetched.
  • Licence and attribution. Planning Data is published under the Open Government Licence v3.0, © Crown copyright and database right 2026. Keep the attribution when you republish the rows. This actor is not affiliated with the Ministry of Housing, Communities & Local Government.

Made by Yadroo. Sibling actors: rightmove-uk, osm-geocode, public-holidays, world-bank-indicators, rss-to-json.