Blog ·Tutorial·Updated

How to Search UK Planning Applications by Postcode

Find planning records matching a postcode, a postcode district or a radius around a location. These are different searches: choose the one your application needs, then check the source evidence and coverage.

For a single planning enquiry, start with the official council planning source linked from our directory. For a property product, GIS workflow or repeatable integration, use the planning applications API.

Search an exact postcode without geocoding

Set PLANWIRE_API_KEY privately in your local or server environment. Never publish it in a URL, browser script or repository.

curl
curl --fail-with-body --max-time 15 \ 'https://api.planwire.io/v1/applications?postcode=SW1A1AA&limit=5' \ -H "X-API-Key: $PLANWIRE_API_KEY"

Read records from the response's data array. An empty array means no matches in your accessible data, not proof that a property has no planning history. Missing postcodes, sample restrictions and council coverage can affect results.

Step 1: Convert a postcode to coordinates

Radius search needs latitude and longitude. Postcodes.io's postcode lookup supplies a postcode point without an API key. This is an area reference point, not necessarily the property's exact location. Use verified property coordinates when distance from the building matters.

curl
curl --fail-with-body --max-time 15 'https://api.postcodes.io/postcodes/SW1A1AA'

Use result.latitude and result.longitude from the current lookup. The following is an illustrative, shortened response, not a live location or planning-data result:

response
{ "status": 200, "result": { "postcode": "SW1A 1AA", "latitude": 51.501009, "longitude": -0.141588 } }

Step 2: Query planning applications by radius

Use /v1/applications/nearby with lat, lng and radius_km. Radius is in kilometres. Do not add coordinates to the ordinary list endpoint and assume they apply a distance filter.

curl
curl --fail-with-body --max-time 15 \ 'https://api.planwire.io/v1/applications/nearby?lat=51.501009&lng=-0.141588&radius_km=0.5&limit=20' \ -H "X-API-Key: $PLANWIRE_API_KEY"

This asks for up to 20 accessible records within 500 metres of the example point, nearest first. It is a bounded result, not an exhaustive export. Missing coordinates and field values remain possible; consult the response field reference and council coverage evidence.

Step 3: Use postcode search for date and status filters

The nearby endpoint supports coordinates, radius and limit; it does not apply the list endpoint's date, status or keyword filters. For those filters, use a postcode or council-scoped list query. A postcode district is not a substitute for a circular boundary.

Only pending applications

curl
curl --fail-with-body --max-time 15 \ 'https://api.planwire.io/v1/applications?postcode=LE2&status=Pending&limit=5' \ -H "X-API-Key: $PLANWIRE_API_KEY"

Applications within a chosen date window

curl
curl --fail-with-body --max-time 15 \ 'https://api.planwire.io/v1/applications?postcode=LE2&date_from=2026-09-01&date_to=2026-09-07&limit=5' \ -H "X-API-Key: $PLANWIRE_API_KEY"

The fixed dates demonstrate the syntax, not "the last seven days". Check date semantics and pagination. Free keys return older samples, so a recent date window may be empty.

Descriptions mentioning householder work

curl
curl --fail-with-body --max-time 15 \ 'https://api.planwire.io/v1/applications?postcode=LE2&q=householder&limit=5' \ -H "X-API-Key: $PLANWIRE_API_KEY"

Full example in JavaScript

Run this server-side with Node.js 20 or later. It makes two bounded requests, checks HTTP errors and reads the documented data array. It does not retry rate-limit responses automatically.

JavaScript
async function getPlanningByPostcode(postcode, radiusMetres = 500) { const key = process.env.PLANWIRE_API_KEY; if (!key) throw new Error('Set PLANWIRE_API_KEY privately'); if (!Number.isFinite(radiusMetres) || radiusMetres <= 0) { throw new Error('Radius must be a positive number of metres'); } const geoResponse = await fetch( `https://api.postcodes.io/postcodes/${encodeURIComponent(postcode.trim())}`, { signal: AbortSignal.timeout(15000) } ); if (!geoResponse.ok) throw new Error(`Postcode lookup HTTP ${geoResponse.status}`); const geo = await geoResponse.json(); if (geo.status !== 200 || !geo.result) throw new Error('Postcode not found'); const { latitude, longitude } = geo.result; if (!Number.isFinite(latitude) || !Number.isFinite(longitude)) { throw new Error('Postcode lookup returned no usable coordinates'); } const url = new URL('https://api.planwire.io/v1/applications/nearby'); url.searchParams.set('lat', latitude); url.searchParams.set('lng', longitude); url.searchParams.set('radius_km', radiusMetres / 1000); url.searchParams.set('limit', 50); const res = await fetch(url, { headers: { 'X-API-Key': key }, signal: AbortSignal.timeout(15000) }); if (!res.ok) throw new Error(`PlanWire HTTP ${res.status}`); const payload = await res.json(); if (!Array.isArray(payload.data)) throw new Error('Unexpected PlanWire response'); return payload.data; }
Example call
const apps = await getPlanningByPostcode('SW1A 1AA', 500); console.log(`Returned ${apps.length} matching records`);

Python example

This version uses Python 3 and the requests package. Install it in your virtual environment with python3 -m pip install requests. For a dependency-free council search, use our tested Python planning API tutorial.

Python
import requests import os import math from urllib.parse import quote def get_planning_by_postcode(postcode, radius_m=500): key = os.environ["PLANWIRE_API_KEY"] if not key: raise ValueError("Set PLANWIRE_API_KEY privately") if not math.isfinite(radius_m) or radius_m <= 0: raise ValueError("Radius must be a positive number of metres") geo_response = requests.get( "https://api.postcodes.io/postcodes/" + quote(postcode.strip(), safe=""), timeout=15, ) geo_response.raise_for_status() geo = geo_response.json() if geo.get("status") != 200 or not geo.get("result"): raise ValueError("Postcode not found") lat = geo["result"]["latitude"] lng = geo["result"]["longitude"] if not isinstance(lat, (int, float)) or not isinstance(lng, (int, float)): raise ValueError("Postcode lookup returned no usable coordinates") if not math.isfinite(lat) or not math.isfinite(lng): raise ValueError("Postcode lookup returned no usable coordinates") # Query PlanWire response = requests.get( "https://api.planwire.io/v1/applications/nearby", params={"lat": lat, "lng": lng, "radius_km": radius_m / 1000, "limit": 50}, headers={"X-API-Key": key}, timeout=15, ) response.raise_for_status() payload = response.json() if not isinstance(payload.get("data"), list): raise ValueError("Unexpected PlanWire response") return payload["data"]
Example call
apps = get_planning_by_postcode("SW1A 1AA", radius_m=500) print(f"Returned {len(apps)} matching records")

What radius should I use?

The right radius depends on what you're building:

Monitoring: Signed planning webhooks deliver supported changes after PlanWire captures them, not the moment somebody submits an application to a council. Registered filters include council and postcode prefix, not an arbitrary radius. Review plan entitlements and recovery behavior before replacing polling.

Bulk postcode lookups

If you have a list of postcodes to query at once, postcodes.io supports bulk lookups (up to 100 postcodes per request):

curl — bulk geocode
curl -X POST "https://api.postcodes.io/postcodes" \ -H "Content-Type: application/json" \ -d '{"postcodes": ["SW1A 1AA", "EC1A 1BB", "W1A 1AA"]}'

Coverage

Check current council coverage, history and source freshness for your area. Not every record has a usable postcode or coordinates. Search results are not a complete legal planning history, and permission does not establish that construction started. Keep the original source link and reference when evaluating a result.

Free sandbox

Free keys provide 25 requests per day against limited, potentially older samples. They do not establish current local coverage. Check plan limits and live access before importing records or designing monitoring. Handle 401 authentication errors, 403 entitlement errors and 429 rate limits explicitly; an error is not an empty result.

Start querying planning data by postcode

Free sandbox, no credit card. 25 requests/day for API shape and sample postcode workflows.