API Reference
Base URL: https://api.planwire.io · Version: v1
Authentication
Pass your API key in the X-API-Key header. You can also use Authorization: Bearer YOUR_KEY.
X-API-Key: pw_live_a1b2c3d4e5f6...
Errors
All errors return JSON with an error field and an appropriate HTTP status code.
| Status | Meaning |
|---|---|
| 400 | Bad request — missing or invalid parameters |
| 401 | Unauthorised — missing or invalid API key |
| 402 | Payment required — fresh data or broad coverage requires a paid plan |
| 404 | Not found — resource does not exist |
| 429 | Rate limit exceeded — upgrade your plan |
| 500 | Server error — contact support |
Pagination
All list endpoints return a meta object. Use ?page=2&limit=50 to paginate. Paid plans can request up to 100 records per page. Free keys are capped at 10 records per page and older sample data.
"meta": { "total": 4821, // total matching records "page": 1, // current page (1-indexed) "limit": 25, // records per page "pages": 242 // total pages }
Freshness & dates
PlanWire exposes source-check freshness and application-date freshness separately. lastScrapedAt means the source was checked or refreshed. It should not be treated on its own as proof that new application records were ingested.
For data recency, use latestApplicationDate on councils and dates.applicationDate on applications. Application lists are sorted by applicationDate desc, firstSeenAt desc by default.
"freshness": { "sourceCheckStatus": "fresh", "dataStatus": "stale", "lastScrapedAt": "2026-05-01T09:20:00.000Z", "latestApplicationDate": "2026-03-12" }
Rate limits
Limits reset at midnight UTC. Exceeding the limit returns 429. Free access is a sandbox: 25 calls/day, older sample records, limited council preview, no webhooks, and no fresh lead data.
| Plan | Requests/day | Price |
|---|---|---|
| Free | 25 | £0 |
| Lite | 300 | £9/mo |
| Starter | 1,000 | £29/mo |
| Growth | 10,000 | £99/mo |
| Enterprise | Unlimited | £299/mo |
/v1/applications
List planning applications. All parameters are optional and can be combined. Each application includes council, source, and freshness metadata so you can verify where the record came from and when that source was last checked.
Query Parameters
oxf, cam)Approved, Refused)curl "https://api.planwire.io/v1/applications?council=adu&q=extension&status=Approved&limit=5" \ -H "X-API-Key: YOUR_KEY"
/v1/applications/nearby
Find applications within a radius using PostGIS spatial indexing. Extremely fast even over millions of records.
curl "https://api.planwire.io/v1/applications/nearby?lat=51.4975&lng=-0.1278&radius_km=1" \ -H "X-API-Key: YOUR_KEY"
/v1/applications/:id
Fetch a single planning application by its UUID. Includes property enrichment — EA flood zone and Land Registry sold prices for the postcode.
curl "https://api.planwire.io/v1/applications/8f6fac3a-d93a-453e-b5db-6c5ecc631bf0" \ -H "X-API-Key: YOUR_KEY"
Property Enrichment Fields
floodRisk
object | null
EA Flood Map for Planning zone. zone is one of low, 2, 3a, 3b. Cached 30 days. May be null on the first call while enrichment is fetched.
nearbyPrices
object | null
Land Registry price paid data for the exact application postcode over the last 2 years. Returns avgPrice (whole GBP), count, postcode. May be null on the first call, if the application has no full postcode, or if Land Registry has no exact-postcode sales in the period.
/v1/applications/ref/:council/:reference
Fetch an application using the council's own reference number (e.g. 24/01234/FUL). Also includes flood risk and nearby sold prices.
curl "https://api.planwire.io/v1/applications/ref/adu/AWDM%2F0158%2F25" \ -H "X-API-Key: YOUR_KEY"
/v1/property/market-evidence
Fetch concise HM Land Registry price-paid evidence for an arbitrary subject-site postcode. Returns exact-postcode evidence plus postcode-sector evidence when imported Land Registry transactions are available. Add include_rental=true to include official ONS PIPR local-area rental benchmark evidence where imported.
curl "https://api.planwire.io/v1/property/market-evidence?postcode=M1%205QA&include_rental=true&bedrooms=2" \ -H "X-API-Key: YOUR_KEY"
Response notes
Returns exactPostcode, sectorFallback, and recommended where evidence exists. Prices are whole GBP. The default period is 2 years; pass period_years=1..5 to adjust. recommended uses exact-postcode evidence when the sample is adequate, otherwise sector evidence when available. Each bucket includes confidence: strong_sample, moderate_sample, or thin_sample.
Rental evidence is returned as rentalEvidence.officialBenchmark. It is an official ONS PIPR local-area benchmark mapped from the subject postcode using the imported official postcode directory. It is not live postcode-sector listing evidence.
/v1/councils
List council source records and freshness metadata. Paid plans receive full coverage; free keys receive a limited preview.
{ "data": [{ "id": "adu", "name": "Adur District Council", "portalType": "official", "applicationCount": 874 }] }
/v1/councils/:id
Get a single council's details including the date of the most recent application.
curl "https://api.planwire.io/v1/councils/adu" \ -H "X-API-Key: YOUR_KEY"
/v1/webhooks
List all webhooks registered to your API key.
/v1/webhooks
Subscribe to planning application events. You'll receive an HTTP POST to your URL whenever a matching application is new or updated.
Request Body
SW1)curl -X POST "https://api.planwire.io/v1/webhooks" \ -H "X-API-Key: YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://your-app.com/hooks/planning", "filters": { "postcodePrefix": "SW1", "status": "Approved" } }'
Webhook Payload
Requests are signed with HMAC-SHA256 — verify using the X-PlanWire-Signature header.
{ "event": "application.new", // or "application.updated" "timestamp": "2025-03-19T14:23:01Z", "application": { /* full application object */ } }
Verifying signatures
Compute HMAC-SHA256(secret, rawBody) and compare to the X-PlanWire-Signature header value (after stripping the sha256= prefix).
import { createHmac } from 'crypto'; function verify(secret, rawBody, sigHeader) { const expected = 'sha256=' + createHmac('sha256', secret) .update(rawBody).digest('hex'); return sigHeader === expected; }
import hmac, hashlib def verify(secret, raw_body, sig_header): expected = 'sha256=' + hmac.new( secret.encode(), raw_body, hashlib.sha256 ).hexdigest() return hmac.compare_digest(sig_header, expected)
/v1/webhooks/:id
Remove a webhook by its UUID.
/v1/webhooks/:id/test
Send a test event to verify your endpoint is reachable and your signature verification works.
curl -X POST "https://api.planwire.io/v1/webhooks/YOUR_WEBHOOK_ID/test" \ -H "X-API-Key: YOUR_KEY"
/v1/webhooks/:id/deliveries
View the last 50 delivery attempts for a webhook — useful for debugging failed deliveries.
{ "data": [{ "id": "a3f2...", "event": "application.new", "statusCode": 200, "success": true, "createdAt": "2025-03-19T14:23:01Z" }] }
Ready to build?
Get an API key →