# UK Planning Constraints Checker (`zinin/uk-planning-constraints-checker`) Actor

Screen buyer-supplied sites against six fixed Planning Data datasets and return attributed factual entities with explicit caveats.

- **URL**: https://apify.com/zinin/uk-planning-constraints-checker.md
- **Developed by:** [Tim Zinin](https://apify.com/zinin) (community)
- **Categories:** Real estate
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.55 / 1,000 planning constraint screen completeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#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 Planning Constraints Checker

Screen one to ten declared sites in England against six fixed official Planning Data datasets. The Actor returns attributed factual constraint rows and one explicit, caveated summary for every completed site. It does not claim that a site is clear, buildable, complete, valuable, or suitable for development.

![Coordinate or UPRN to six fixed official datasets and attributed output](https://api.apify.com/v2/key-value-stores/AbTyBkPsKO9TPAqSe/records/store-six-dataset-screening.png)

### What you get

- **Six fixed official datasets:** conservation areas, listed buildings, green belt, flood-risk zones, Article 4 direction areas, and tree-preservation zones.
- **Two precise selector modes:** a WGS84 latitude/longitude pair or a numeric UPRN. Buyer-controlled URLs, dataset names, fields, query fragments, and geometries are not accepted.
- **Attributed factual rows:** each `CONSTRAINT` row carries the dataset-specific official rights notice, source endpoint, entity identity, name/reference, position, and deterministic fingerprint.
- **One summary per completed site:** `SITE_SUMMARY` separates returned, retained, and delivered counts; records truncation and `noneReturned`; lists all six dataset attributions; and repeats the non-advice and coverage caveats.
- **Delivery-first billing:** the complete ordered Dataset batch is stored before one `result-found` is confirmed for each completed site.

Use the Actor for bounded property-research context, campaign fact-checking, regeneration briefs, and routing sites to specialist review. It is not a planning search, professional opinion, planning decision, or substitute for local-authority and specialist due diligence.

### Who it is for

- Property marketers who need source-labelled facts before drafting site or area copy.
- Development and regeneration teams triaging declared sites for further review.
- Analysts adding repeatable official-source context to a CRM, spreadsheet, or warehouse.
- Automation builders sending caveated screening results to Make, n8n, or an internal workflow.

### Quick start

The release Task is named `screen-uk-planning-constraints`. Its one-site example screens a Westminster coordinate and has a FREE-tier maximum of `$0.012000`: one start plus one completed-site result.

```json
{
  "sites": [
    {
      "siteId": "westminster-1",
      "latitude": 51.5074,
      "longitude": -0.1278
    }
  ],
  "maxConstraintsPerSite": 100,
  "includeConstraintDetails": true,
  "inputLabel": "London planning screen",
  "acknowledgeScreeningOnly": true
}
```

Replace the example site, retain `acknowledgeScreeningOnly: true`, choose a maximum charge, and consume the Dataset only after the Run reports `SUCCEEDED`.

For a UPRN, replace the coordinate pair with one non-zero-padded string:

```json
{
  "sites": [
    {
      "siteId": "uprn-site-1",
      "uprn": "100023336956"
    }
  ],
  "maxConstraintsPerSite": 100,
  "includeConstraintDetails": true,
  "inputLabel": "Authorized UPRN screen",
  "acknowledgeScreeningOnly": true
}
```

### Input reference

| Field | Required | Limits | Meaning |
| --- | --- | --- | --- |
| `sites` | Yes | 1–10 unique closed objects | Each site has a unique `siteId` and exactly one selector mode: coordinate pair or UPRN. |
| `sites[].siteId` | Yes | 1–40 ASCII characters | Correlation ID matching `[A-Za-z0-9][A-Za-z0-9._-]*`; returned in output. |
| `sites[].latitude` | Coordinate mode | `49.8` to `60.9`, at most 6 decimals | WGS84 latitude sent only to the fixed Planning Data endpoint. |
| `sites[].longitude` | Coordinate mode | `-8.7` to `2.1`, at most 6 decimals | WGS84 longitude supplied together with latitude. |
| `sites[].uprn` | UPRN mode | 1–12 digits, no leading zero | Numeric UPRN string supplied instead of coordinates. |
| `maxConstraintsPerSite` | Yes | Integer 1–100 | Deterministic retained detail-row ceiling for each site. |
| `includeConstraintDetails` | Yes | Boolean | Include retained `CONSTRAINT` rows; the summary is always delivered. |
| `inputLabel` | Yes | 0–80 safe characters | Non-sensitive correlation label. It is neither sent to Planning Data nor copied to output. |
| `acknowledgeScreeningOnly` | Yes | Must be `true` | Confirms the early-screening and coverage boundary. |

Unknown fields, mixed selectors, duplicate site IDs, formula-leading or control-containing labels, over-precise coordinates, and malformed UPRNs reject before source access or Dataset delivery.

### How it works

1. The Actor validates the complete closed Input and its 65,536-byte ceiling.
2. It checks pricing, the existing `result-found` counter, and capacity for every requested site before making a source request.
3. For each site, it calls only `https://www.planning.data.gov.uk/entity.json` with the six fixed dataset slugs and the four fixed factual fields. Redirects and compressed responses are rejected.
4. Every source response is byte-bounded, parsed as strict UTF-8 JSON, shape-validated, deduplicated, normalized, and sorted in the fixed dataset/entity order.
5. All sites must validate before the first write. The Actor then creates zero to 100 retained `CONSTRAINT` rows plus one `SITE_SUMMARY` per site and stores one complete ordered Dataset batch.
6. Only after that write succeeds does it confirm exactly one `result-found` for each completed site. A failed source or Dataset write creates no named result charge.

There is no browser, proxy, crawler, geocoder, LLM, arbitrary URL fetch, owner/contact inference, or planning submission.

![Dataset-specific rights remain attached and billing follows successful Dataset delivery](https://api.apify.com/v2/key-value-stores/AbTyBkPsKO9TPAqSe/records/store-rights-delivery-contract.png)

### Output Dataset

The default Dataset contains each site's optional detail rows followed by its mandatory summary. This factual row comes from the production Westminster example:

```json
{
  "rowType": "CONSTRAINT",
  "schemaVersion": "1.0",
  "siteId": "westminster-1",
  "source": "planning-data-gov-uk",
  "sourceEndpoint": "https://www.planning.data.gov.uk/entity.json",
  "sourceAttribution": "© Historic England 2026. Contains Ordnance Survey data © Crown copyright and database right 2026. The Historic England GIS Data contained in this material was obtained on 2026-09-01. The most publicly available up to date Historic England GIS Data can be obtained from HistoricEngland.org.uk. Licensed under the Open Government Licence v3.0.",
  "coverageCaveat": "Planning Data coverage varies by dataset and area; no returned entity does not prove that no constraint exists.",
  "dataset": "conservation-area",
  "entityId": 44002870,
  "name": "Trafalgar Square",
  "reference": "CONARA/1300",
  "constraintPosition": 1,
  "sourceEntityCount": 3,
  "siteTruncated": false,
  "rowFingerprint": "2b2a0fd9bc861d59681737726357df3830f321b48f4ef726217dcd4540f8e423"
}
```

The final `SITE_SUMMARY` includes `queryType`, `checkedDatasets`, all six `sourceAttributions`, `sourceEntityCount`, `retainedConstraintCount`, `deliveredDetailCount`, `includeConstraintDetails`, `noneReturned`, `truncated`, `checkedAt`, `adviceCaveat`, and a deterministic `reportId`. Coordinates, UPRNs, and `inputLabel` are never copied to Dataset rows.

### Row interpretation

- `sourceEntityCount` is the validated count returned in the bounded source response, up to 101.
- `retainedConstraintCount` applies `maxConstraintsPerSite`; `deliveredDetailCount` is zero when details are disabled.
- `siteTruncated`/`truncated` becomes true when the source reaches its 101-row detection boundary or the retained ceiling removes facts.
- `noneReturned: true` means only that this exact bounded request returned no matching entity. It does not mean clear, unconstrained, complete, or safe.
- `rowFingerprint` identifies normalized entity facts. `reportId` identifies the normalized factual summary scope; neither is a professional conclusion.
- `checkedAt` and Historic England's “obtained on” date describe this run's source acquisition, not a guarantee that every provider dataset was refreshed that day.

### Pricing

Pay Per Event uses the automatic Actor Start event and one primary `result-found` per completed site summary. Constraint detail rows are included in that site unit and are not charged separately. A completed `noneReturned` summary is still a paid screen because the official request, validation, attribution, and summary were delivered.

| Tier | Actor Start | Completed site | One-site Task total | Ten-site maximum |
| --- | ---: | ---: | ---: | ---: |
| FREE | $0.009000 | $0.003000 | $0.012000 | $0.039000 |
| BRONZE | $0.008550 | $0.002850 | $0.011400 | $0.037050 |
| SILVER | $0.008100 | $0.002700 | $0.010800 | $0.035100 |
| GOLD | $0.007650 | $0.002550 | $0.010200 | $0.033150 |
| PLATINUM | $0.007380 | $0.002460 | $0.009840 | $0.031980 |
| DIAMOND | $0.007200 | $0.002400 | $0.009600 | $0.031200 |

Invalid Input, a pricing/budget mismatch, source failure, or failed Dataset delivery creates no `result-found`. The platform's automatic Actor Start charge can still apply.

### API and automation

Run the Actor directly and wait for a terminal status:

```bash
curl -X POST \
  'https://api.apify.com/v2/acts/zinin~uk-planning-constraints-checker/runs?token=APIFY_TOKEN&waitForFinish=180' \
  -H 'Content-Type: application/json' \
  --data-binary @input.json
```

Or run the release Task with its validated prefill:

```bash
curl -X POST \
  'https://api.apify.com/v2/actor-tasks/zinin~screen-uk-planning-constraints/runs?token=APIFY_TOKEN&waitForFinish=180'
```

Read `defaultDatasetId` only from a `SUCCEEDED` Run, then request `https://api.apify.com/v2/datasets/DATASET_ID/items?clean=true`. Before retrying a timed-out client request, inspect the original Run so a completed paid screen is not duplicated.

**Make:** use Apify's “Run an Actor or Task” module, map the five top-level fields, wait for completion, and retrieve Dataset items. Route `noneReturned: true`, `truncated: true`, and failed Runs to human review instead of turning them into site claims.

**n8n:** use an HTTP Request node to start the Task with `waitForFinish=180`, an IF node requiring `status === "SUCCEEDED"`, and a second HTTP Request node for the default Dataset. Preserve attribution and caveat fields in every downstream record.

### Source rights and attribution

The Actor uses the official Planning Data service for England. The current dataset pages are:

- [Conservation area](https://www.planning.data.gov.uk/dataset/conservation-area)
- [Listed building](https://www.planning.data.gov.uk/dataset/listed-building)
- [Green belt](https://www.planning.data.gov.uk/dataset/green-belt)
- [Flood-risk zone](https://www.planning.data.gov.uk/dataset/flood-risk-zone)
- [Article 4 direction area](https://www.planning.data.gov.uk/dataset/article-4-direction-area)
- [Tree-preservation zone](https://www.planning.data.gov.uk/dataset/tree-preservation-zone)

Every delivered detail row carries the notice required for its dataset. Conservation-area and listed-building rows include Historic England, Ordnance Survey, acquisition-date, freshness-location, and Open Government Licence v3.0 text. Green-belt rows include the [OS OpenData licence](http://os.uk/opendata/licence), Ordnance Survey Crown/database notice, and OGL text. Flood-risk, Article 4, and tree-preservation rows include the Crown/database notice and OGL text. Each site summary preserves all six dataset-to-attribution mappings.

The Actor does not grant rights beyond the cited source terms. Source availability, provider coverage, update timing, and completeness vary by dataset and area.

### Security and data handling

- The Actor runs with `LIMITED_PERMISSIONS` and accepts no source URL, credential, cookie, proxy, custom dataset, query fragment, or executable content.
- Coordinates or UPRNs are sent only to the fixed Planning Data endpoint. `inputLabel` is not sent. Redirects, HTML, compression ambiguity, oversized bodies, unknown datasets, and unsafe source strings fail closed.
- Apify persists original Input—including selectors and label—in the run's default Key-Value Store under your account permissions and retention settings. Delivered rows remain in the default Dataset.
- Do not submit personal data, contacts, credentials, confidential prose, or sites you are not authorized to screen. Delete run storage or apply your account retention policy when the record is no longer required.
- Runtime logs contain only fixed counters and error codes, not coordinates, UPRNs, labels, source bodies, or returned entities.

### Hard limits

- 1–10 sites per Run; one fixed source request per site.
- 100 retained detail rows plus one summary per site; at most 1,010 Dataset rows.
- 65,536-byte Input ceiling; 2,097,152 source bytes per site; 20,971,520 source bytes per Run; 4,194,304-byte complete output ceiling.
- Six-second source timeout, zero redirects, zero retries, identity encoding only.
- 128 MB memory and 180-second Run timeout.
- England only, through the six exact Planning Data datasets listed above.

### Limits and interpretation

- The source can omit, duplicate, lag, or vary by provider and geography. No returned entity is not proof that no constraint exists.
- Results do not establish legal status, planning permission, buildability, compliance, environmental safety, insurability, value, ownership, or investment suitability.
- A coordinate or UPRN lookup is not a title search, local land charge search, flood assessment, survey, valuation, or professional planning review.
- The Actor does not determine whether a returned entity intersects a legal parcel boundary beyond what the official endpoint returns for the declared selector.
- Names and references are source facts, not Actor-generated advice. Formula-leading source text is neutralized for safer tabular export.
- Live official data may change between Runs, so compare timestamps and preserve the delivered attribution when retaining results.

### Troubleshooting

- **Input is rejected:** provide every top-level field, use exactly one selector mode per site, remove extra fields, and keep `acknowledgeScreeningOnly` true.
- **A coordinate is rejected:** use JSON numbers within the documented bounds and no more than six decimal places; do not use strings, exponents, or negative zero.
- **A UPRN is rejected:** use a 1–12 digit string without spaces or leading zeroes.
- **The Run reports a source error:** inspect Planning Data availability and start a new Run later. The Actor does not silently redirect or retry.
- **Only a summary appears:** either no source entity was returned or `includeConstraintDetails` was false; inspect the summary counts and flags.
- **A site is truncated:** reduce scope outside this Actor or perform authoritative specialist checks; never interpret the retained subset as complete.

### FAQ

**Does the Actor need a Planning Data API key?** No. It uses the public fixed endpoint.

**Does `noneReturned` mean there are no constraints?** No. It means only that this bounded six-dataset request returned no matching entity.

**Why is a zero-entity site charged?** One paid unit is the complete site screen and its attributed summary, not an individual constraint row.

**Can I choose other datasets or send a polygon?** No. The source host, path, fields, six datasets, and selector modes are fixed.

**Does it cover Scotland, Wales, or Northern Ireland?** No. V1 is limited to the Planning Data service for England.

**Can it replace a planning consultant or local search?** No. It is factual early-screening context only.

# Actor input Schema

## `sites` (type: `array`):

One to ten unique sites, each identified by a site ID plus either WGS84 coordinates or a numeric UPRN.

## `maxConstraintsPerSite` (type: `integer`):

Retain the deterministic first 1–100 factual entities for each site.

## `includeConstraintDetails` (type: `boolean`):

Return retained factual entity rows in addition to the mandatory per-site summary.

## `inputLabel` (type: `string`):

Optional non-sensitive buyer label used only for input correlation and never sent to Planning Data.

## `acknowledgeScreeningOnly` (type: `boolean`):

Required: returned facts are early screening only and absence of returned entities does not prove absence of constraints.

## Actor input object example

```json
{
  "sites": [
    {
      "siteId": "westminster-1",
      "latitude": 51.5074,
      "longitude": -0.1278
    }
  ],
  "maxConstraintsPerSite": 100,
  "includeConstraintDetails": true,
  "inputLabel": "London planning screen",
  "acknowledgeScreeningOnly": true
}
```

# Actor output Schema

## `screeningRows` (type: `string`):

Attributed factual rows; noneReturned never means clear or constraint-free.

# 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 = {
    "sites": [
        {
            "siteId": "westminster-1",
            "latitude": 51.5074,
            "longitude": -0.1278
        }
    ],
    "maxConstraintsPerSite": 100,
    "includeConstraintDetails": true,
    "inputLabel": "London planning screen",
    "acknowledgeScreeningOnly": true
};

// Run the Actor and wait for it to finish
const run = await client.actor("zinin/uk-planning-constraints-checker").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 = {
    "sites": [{
            "siteId": "westminster-1",
            "latitude": 51.5074,
            "longitude": -0.1278,
        }],
    "maxConstraintsPerSite": 100,
    "includeConstraintDetails": True,
    "inputLabel": "London planning screen",
    "acknowledgeScreeningOnly": True,
}

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

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

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

```

## CLI example

```bash
echo '{
  "sites": [
    {
      "siteId": "westminster-1",
      "latitude": 51.5074,
      "longitude": -0.1278
    }
  ],
  "maxConstraintsPerSite": 100,
  "includeConstraintDetails": true,
  "inputLabel": "London planning screen",
  "acknowledgeScreeningOnly": true
}' |
apify call zinin/uk-planning-constraints-checker --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,zinin/uk-planning-constraints-checker"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/QLsodZxptc1dhSTo9/builds/juuqc4Y9sshe6IzYu/openapi.json
