Developers · API v1
Screen a location from your own systems
One endpoint. Post a point in England, Wales or Scotland, get back the same constraint survey the app shows: every layer checked, every source that failed, every council gap, the vintages, the attribution and the site score. Built for acquisition pipelines, CRMs and dashboards that would rather not have a person retyping results.
A layer missing from results while its source is listed in failures was not checked. It is not clear. Upstream services go down; when one does, we still answer 200, we still return everything we did check, and we tell you exactly what we could not. failures is always present (an empty array on a clean survey) and status is "partial" whenever it is not empty.
If your integration renders a verdict, render the partial state too. Treating silence as absence is the one failure mode of this API that produces a confidently wrong answer about a real site.
A second kind of silence has its own field: authority_gaps. Some English councils do not publish every dataset (tree preservation orders, Article 4 directions and conservation areas are the common ones), and Scottish councils supply several layers to the Improvement Service voluntarily. Where the council at the point publishes nothing for a layer, we do not return that layer in results as clear. It appears in authority_gaps instead, and it was not checked. A council can publish a dataset and still have nothing designated at your site, so count: 0 in results remains a real answer. Ask the council where it matters.
status is "partial" when a source failed to answer or when the council at the point publishes nothing for a layer (see authority_gaps). Only "complete" means every layer was checked.
This is also a condition of use: section 04A of the Terms requires that a layer listed in failures is not presented as a clear result, and sets out the allowances, key handling and attribution conditions that go with an API key.
Keys belong to an organisation on the Practice plan. The practice owner creates them under Account → Organisation → API. A key is shown once, at creation. We store a hash of it and cannot show it to you again. Lost it? Rotate it.
Send it as Authorization: Bearer pc_live_…, or as X-API-Key: pc_live_… if your client is happier with that. There is no session, no cookie and no OAuth flow. Tier and subscription state are re-checked on every single request, so revoking a key, or a subscription lapsing, takes effect on the next call.
Treat a key like a password: server-side only, never in a browser bundle or a mobile app. Name each key after the system that uses it, so revoking one when somebody leaves does not take the rest down with it.
Send either a coordinate pair or a postcode. A coordinate wins if you send both, because a postcode resolves to the centroid of the postcode unit, which can sit in a different field, flood cell or parcel than the asset you mean.
lat,lng: WGS84 decimal degrees. Both or neither.postcode: a UK postcode, e.g."CB2 1TN".radius: metres. Default 100, maximum 500.
England, Wales and Scotland. Each nation is screened against its own publishers: planning.data.gov.uk, the Environment Agency (Flood Map for Planning) and Historic England in England; DataMapWales in Wales; and Historic Environment Scotland, NatureScot, SEPA and the Improvement Service in Scotland. The response names any layer that does not apply. Scottish green belt, tree preservation order, Article 4, vacant and derelict land and air quality management area layers come from voluntary council submissions to the Improvement Service Spatial Hub. Where a council has supplied nothing, the layer appears in authority_gaps; where the Hub cannot be reached, it appears in failures. Neither is clear. Northern Ireland is out of scope, not a coverage gap: a point or postcode there is refused with a 422 rather than screened against datasets that do not describe it.
curl -X POST https://planningconstraints.com/api/v1/screen \
-H "Authorization: Bearer pc_live_…" \
-H "Content-Type: application/json" \
-d '{ "lat": 52.2053, "lng": 0.1218, "radius": 100 }'Response (abridged)
{
"request_id": "0f1a…",
"api_version": "2026-07",
"surveyed_at": "2026-07-30T09:14:22.183Z",
"status": "partial",
"notice": "One or more sources did not answer. Every layer listed in `failures` was NOT checked. Treat it as unknown, never as clear.",
"query": { "lat": 52.2053, "lng": 0.1218, "radius_m": 100, "postcode": null },
"summary": {
"constraints": 3,
"blockers": 1,
"material": 2,
"findings": ["Planning framework", "Heritage"],
"clear_domains": ["Flood & water", "Ground & subsurface"],
"not_covered": 1,
"layers_checked": 41
},
"results": [
{
"definition_id": "green-belt",
"name": "Green Belt",
"domain": "planning-framework",
"subgroup": "designation",
"band": "constraint",
"weight": "blocker",
"count": 1,
"entities": [{ "name": "Cambridge Green Belt", "reference": "…" }],
"vintage": "2025-11"
},
{
"definition_id": "flood-risk-zone",
"name": "Flood risk zone",
"domain": "flood-water",
"subgroup": "fluvial",
"band": "constraint",
"weight": "material",
"count": 0
}
],
"failures": [
{
"source_id": "historic-england",
"label": "Listed buildings (Historic England)",
"definition_ids": ["listed-building"]
}
],
"authority_gaps": [
{
"definition_id": "article-4-direction-area",
"layer": "Article 4 Direction Area",
"authority": "Bath and North East Somerset Council",
"note": "Bath and North East Somerset Council doesn't publish this data to planning.data.gov.uk, the national source we check. This site couldn't be checked against it, which is not the same as being clear of it. Ask the council directly."
}
],
"score": { "version": "v1", "total": 62, "partial": true, "contributions": [] },
"meta": {
"api_version": "2026-07",
"data_vintages": { "green-belt": "2025-11" },
"attribution": ["Contains public sector information licensed under the Open Government Licence v3.0"],
"quota": { "included": 1000, "used": 143, "remaining": 857, "resets_at": "2026-08-01T00:00:00.000Z", "month": "2026-07" }
}
}Reading the response
results[]: every layer that was checked, including the ones that found nothing.count: 0means checked and clear. A layer that is absent entirely was not checked.failures[]: sources that did not answer, each with thedefinition_idsits silence covers. Always present.authority_gaps[]: layers where the site’s planning authority publishes nothing to the national source, so the site could not be checked against them. Each hasdefinition_id,layer(the layer’s name),authorityand a plain-Englishnoteyou can show to your users as written. Added 19 October 2026 as an additive field: always an array,[]when there are none. A layer here is absent fromresultsand fromfailures, and is unknown, never clear.summary: the panel’s own headline counts, computed by the same code the app and the bulk CSV use, so the three never disagree.score: the Site Score at this point, with its contributions. Point mode: no redline, so every layer counts at full presence weight.meta.data_vintages: the source’s own edition or publication date for a layer, where it publishes one. A layer with no entry has no stated edition, so the only date issurveyed_at, when we checked it. That includes most layers read through planning.data.gov.uk, which does not publish a last-updated date for the dataset. Treat a vintage as a label, not a freshness guarantee.meta.attribution: see below. Not optional.
Errors
| Status | reason | Meaning |
|---|---|---|
| 401 | invalid-key | No credential, malformed, unknown, or revoked. All four are deliberately indistinguishable. |
| 403 | practice-required | The key’s organisation is not on the Practice plan. |
| 403 | subscription-lapsed | The organisation’s subscription has lapsed. Keys are not revoked: reactivate and the same keys work again. |
| 422 | invalid-request | Missing or malformed location, radius over the maximum, or a point outside the nations we screen. Nothing is fetched and nothing is charged. |
| 429 | rate-limited | Burst limit (60 requests a minute, per key). `Retry-After` is in seconds. |
| 402 | quota-exhausted | The monthly screen allowance is spent. `Retry-After` and `X-Quota-Reset` both point at the reset instant. |
| 503 | capacity | A product-wide daily ceiling was hit. Nothing was charged against your quota. |
| 500 | server-error | The survey could not be run at all. Nothing is charged. Quote the `request_id`. |
Every error carries error (prose), reason (machine-readable, switch on this) and request_id. Quote the request id and we can find the exact call.
Limits, quota and latency
- Burst: 60 requests a minute, per key. Over it: 429 with
Retry-After. - Monthly: 1,000 screens included per organisation per calendar month (UTC), shared across all of its keys. Over it: 402.
- Headers on every response:
X-Quota-Limit,X-Quota-Remaining,X-Quota-Reset. Pace yourself from these rather than from the refusal.
Expect seconds, not milliseconds. A screen fans out to a dozen or more live government sources for that nation (more still in Scotland, which is served by several separate agency APIs) and waits for them. That is what makes the answer current rather than a cached guess. Budget 30 seconds, call it from a queue or a background job rather than inside a user-facing request, and never inside a page render.
The data in every response is public-sector information licensed to us, and the display obligations travel with it. meta.attribution carries the exact strings owed by the layers in that response. If you display the data to anyone, you must reproduce them. See Licences & attribution.
No ownership data crosses this API. HM Land Registry CCOD/OCOD content (proprietor names, title numbers, addresses) is deliberately out of scope for v1 and appears in no response. It is licensed to us under bespoke terms rather than the Open Government Licence, and a machine-consumable surface for it needs designing rather than defaulting.
Versioning
Every response is stamped api_version: "2026-07". Changes to v1 are additive: new fields may appear, existing ones keep their meaning. Anything breaking mints /api/v2; v1 does not change under you.
Not in v1, and each for a stated reason: redline polygons (point and radius only), ownership data, batch endpoints (use bulk CSV screening in the app), webhooks, OAuth and IP allowlists. Tell us which one you need. The order they arrive in is decided by who asks.