# PlanWatch API (v1)

Read access to PlanWatch.ie's dataset of Irish planning applications — 500,000+ applications across all 31 planning authorities, refreshed nightly from the National Planning Application Database, An Coimisiún Pleanála and local authority portals.

- **Base URL:** `https://planwatch.ie/api/v1`
- **OpenAPI spec:** `https://planwatch.ie/api/v1/openapi.json`
- **This page in HTML:** `https://planwatch.ie/api.html`

v1 is the contract: fields are added, never removed or renamed. Breaking changes mean a v2 with 90 days' deprecation notice. Best-effort service, no SLA.

## The envelope

Every response — success or error — uses the same shape:

```json
{
  "data": { },
  "meta": {
    "attribution": "Data © Irish planning authorities, via PlanWatch.ie — see https://planwatch.ie/data-sources.html",
    "docs": "https://planwatch.ie/api.html"
  },
  "error": null
}
```

On an error, `data` is `null` and `error` is an object:

```json
{
  "data": null,
  "meta": { "attribution": "…" },
  "error": { "code": "not_found", "message": "No such resource." }
}
```

Error `code` values: `bad_request`, `unauthorized`, `invalid_api_key`, `not_found`, `quota_exceeded`, `too_many_requests`, `error`.

`meta.attribution` is present on **every** response. When authenticated (or on the keyless taster), `meta.usage` reports today's quota position (`limit`, `used`, `remaining`). List endpoints also add `total`, `showing`, `limit`, `offset` to `meta`.

## Authentication

The API works with **no key** — a keyless taster of **100 calls/day per IP**. To raise the quota, send a free API key:

```
curl -H "X-API-Key: pw_live_xxx" "https://planwatch.ie/api/v1/applications?q=extension&authority=Fingal%20County%20Council"
```

You can also pass the key as a query parameter: `?api_key=pw_live_xxx`. Keys are free — create one on your [Settings](https://planwatch.ie/settings.html) page (no card needed).

## Quotas

Daily quota is derived from your plan. Quotas reset at midnight (Irish time); every `429` carries a `Retry-After` header (seconds until reset).

| Access | Daily calls | Agent intelligence |
|--------|-------------|--------------------|
| Keyless (per IP) | 100 | — |
| Free key | 1,000 | — |
| Starter | 5,000 | — |
| Pro | 25,000 | ✓ (rates, rankings, benchmarks) |
| Team | Negotiated | ✓ |

A `429` for quota tells you how to get or upgrade a key. Fetching `/api/v1/openapi.json` is unmetered.

## Privacy — applicant identity is structurally absent

Applicant name and address **do not exist** on any v1 schema. Serialization is whitelist-only, so they cannot leak through a forgotten filter. Architect, agent and planner names are professional-capacity data and are included.

## Endpoints

All endpoints are `GET`.

### `GET /applications` — search planning applications

Full-text and/or filtered search. Requires at least a query or one filter. Reference numbers (e.g. `24/449`, `F24A/0123`) are detected and matched exactly first.

| Param | Description |
|-------|-------------|
| `q` | Free-text query or a planning reference number. |
| `authority` | Authority name, e.g. `Fingal County Council` (`all` or omitted = every authority). |
| `decisions` | Comma-separated outcome buckets: `granted`, `refused`, `further_info`, `withdrawn`, `invalid`, `pending`. |
| `application_type` | e.g. `PERMISSION`, `RETENTION`, `OUTLINE PERMISSION`. Comma-separated for multiple. |
| `dev_type` | Development type, e.g. `Residential`, `Commercial`. |
| `after` / `before` | Received on/after / on/before a date (`YYYY-MM-DD`). |
| `all_time` | `1` = filter-only search with no date window. |
| `has_fi` | `true` = only applications with a further-information request. |
| `has_appeal` | `true` = only applications with an An Coimisiún Pleanála appeal. |
| `architect` | Filter by architect/agent name (fuzzy match). |
| `sort` | `newest` (default), `oldest`, or `decided`. |
| `limit` | Page size (default 50, max 200). |
| `offset` | Page offset (default 0). |

Example:

```
GET /api/v1/applications?q=house%20extension&authority=Dublin%20City%20Council&decisions=granted&limit=2
```

```json
{
  "data": [
    {
      "id": "Dublin City Council_24003",
      "application_number": "24003",
      "authority": "Dublin City Council",
      "address": "3 Elm Rd, Dublin 5",
      "description": "Demolition of shed and construction of dwelling",
      "status": "GRANTED",
      "decision": "Grant Permission",
      "lifecycle_status": "ACTIVE",
      "application_type": "PERMISSION",
      "received_date": "2024-05-20",
      "decision_date": "2024-08-20",
      "decision_due_date": "2024-08-01",
      "grant_date": null,
      "expiry_date": null,
      "fi_request_date": null,
      "fi_received_date": null,
      "floor_area": null,
      "area_of_site": null,
      "num_residential_units": null,
      "one_off_house": null,
      "appeal_ref_number": null,
      "appeal_status": null,
      "appeal_decision": null,
      "appeal_decision_date": null,
      "latitude": null,
      "longitude": null,
      "link": "https://planning.dublin.ie/24003",
      "architect_name": "Smith & Associates Architects"
    }
  ],
  "meta": {
    "attribution": "Data © Irish planning authorities, via PlanWatch.ie — see https://planwatch.ie/data-sources.html",
    "docs": "https://planwatch.ie/api.html",
    "total": 1,
    "showing": 1,
    "limit": 2,
    "offset": 0
  },
  "error": null
}
```

Every field is always present; unknown values are `null`. Dates are ISO-8601 (`YYYY-MM-DD`).

### `GET /applications/{id}` — one application

`id` is the stable PlanWatch ID from search results, e.g. `Fingal County Council_FW24A/0123`. Returns the full record: everything in the search summary plus `dev_type`, `planner_name`, `conditions_count`, `abp_link`, and Building Control lifecycle where a national commencement/completion notice matched — `commencement_date`, `completion_date`, `bcn_status`, and a `building_control` object of building facts (notice ref, units, floor area, use class). No personal data.

```
GET /api/v1/applications/Dublin%20City%20Council_24003
```

Returns `404` `not_found` if the ID is unknown.

### `GET /nearby` — applications near a point

| Param | Description |
|-------|-------------|
| `lat` | Latitude (required). |
| `lng` | Longitude (required). |
| `radius` | Radius in metres (default 1000, max 10000). |
| `months` | Only applications received in the last N months. |
| `limit` | Page size (default 20, max 100). |

```
GET /api/v1/nearby?lat=53.3498&lng=-6.2603&radius=2000&limit=20
```

Each item is a compact application record plus `distance_m` (metres from the query point). `meta` includes `total`, `center` and `radius_m`.

### `GET /authorities` — list all 31 authorities

No parameters. Each item: `authority`, `slug`, `total_applications`, `earliest_received_date`, `latest_received_date`. Use `slug` with the stats endpoint.

```json
{
  "data": [
    {
      "authority": "Dublin City Council",
      "slug": "dublin-city",
      "total_applications": 21,
      "earliest_received_date": "2024-02-01",
      "latest_received_date": "2024-12-23"
    }
  ],
  "meta": { "attribution": "…", "total": 1 },
  "error": null
}
```

### `GET /authorities/{slug}/stats` — one authority's performance

`slug` comes from `GET /authorities` (e.g. `fingal`, `dublin-city`). Returns `authority`, `slug`, `total_applications`, `pending`, `avg_decision_days`, `fi_rate_percent`, `applications_last_year`. Unknown slug → `404`.

### `GET /agents/{id}` — agent/architect profile

`id` is the agent entity ID (from agent links on planwatch.ie or search results' architect data). Basic profile — name, website, volume, coverage, activity — is available on any tier. Full intelligence (success/FI/appeal rates, processing days, quality score, benchmarks) requires a **Pro** key; without one those fields are `null` and `intelligence_locked` is `true`.

### `GET /openapi.json` — the machine-readable spec

The full OpenAPI 3.1 document. Unmetered — this is how agents discover the API.

## Attribution & licence

Upstream data is CC-BY-flavoured; consumers inherit the obligation. Carry through the `meta.attribution` string in anything you build. Individual queries and derived analysis with attribution are fine; **bulk redistribution of the dataset is not licensed** — [contact us](https://planwatch.ie/contact.html). See [data sources & licensing](https://planwatch.ie/data-sources.html).
