PlanningTrack API · Free beta

Quickstart

Search planning applications, retrieve council evidence, and keep your product updated through one authenticated API.

500 requests / monthNo card requiredJSON over HTTPS

Application coverage is incomplete. Start by checking your target councils. Address searches match published text; they do not establish verified property history.

1. Create your account and key

Sign in with an email code, then create a named key in API keys. Copy it when it appears; you cannot reveal it later.

Set PLANNINGTRACK_API_KEY in your server’s environment. Never put it in browser code, a mobile app, a URL, or source control.

2. Check collection coverage

Use GET /v1/coverage?authority=local-authority-oxo to inspect collection evidence. Directory entries, summary counts, and full detailed records describe different things.

3. Search applications

The example alongside this guide searches collected Oxford records. Use q to add an address, postcode, reference, or proposal. Empty results do not prove that no applications exist.

4. Retrieve a returned application

Take an id from items and URL-encode it in GET /v1/applications/{id}. Display sourceUrl, lastCheckedAt, and section availability with your results.

Run a complete example

These examples check coverage, search, and retrieve an actual returned ID. They use up to three successful requests and include bounded retries and a pagination helper.

quickstart.mjs · Node 24
// Node 24. Set PLANNINGTRACK_API_KEY in your server environment.
const base = process.env.PLANNINGTRACK_API_BASE || 'https://api.planningtrack.co.uk';
const key = process.env.PLANNINGTRACK_API_KEY;
if (!key) throw new Error('Set PLANNINGTRACK_API_KEY');
export async function request(path) {
  for (let attempt = 0; attempt < 4; attempt++) {
    let response;
    try {
      response = await fetch(base + path, {
        headers: { Authorization: 'Bearer ' + key },
        signal: AbortSignal.timeout(15000),
      });
    } catch (error) {
      if (attempt === 3) throw error;
      await new Promise(resolve => setTimeout(resolve, 1000 * 2 ** attempt));
      continue;
    }
    const body = await response.json();
    if (response.ok) return body;
    const retryable = response.status === 429 || response.status >= 500;
    const delay = Number(response.headers.get('retry-after') || 2 ** attempt);
    if (!retryable || body.error?.code === 'quota_exceeded' || attempt === 3 || !Number.isFinite(delay) || delay > 60) {
      throw new Error(JSON.stringify({ status: response.status, ...body.error,
        requestId: response.headers.get('x-request-id') }));
    }
    await new Promise(resolve => setTimeout(resolve, (Math.max(1, delay) + Math.random()) * 1000));
  }
}
export async function* applications(filters) {
  let cursor;
  do {
    const params = new URLSearchParams({ ...filters, limit: '100' });
    if (cursor) params.set('cursor', cursor);
    const page = await request('/v1/applications?' + params);
    yield* page.items;
    cursor = page.nextCursor;
  } while (cursor);
}
// Quickstart uses up to three successful requests; importing helpers makes none.
if (globalThis._importMeta_.main) {
const authority = encodeURIComponent(process.env.PLANNINGTRACK_AUTHORITY || 'local-authority-oxo');
const coverage = await request('/v1/coverage?authority=' + authority);
console.log('Collection evidence:', coverage);
const page = await request('/v1/applications?authority=' + authority + '&limit=1');
if (page.items.length) {
  const application = await request('/v1/applications/' + encodeURIComponent(page.items[0].id));
  console.log(application.reference, application.proposal, application.sourceUrl);
} else {
  console.log('No collected records matched. This does not establish no planning history.');
}
}

Download JavaScript example · Download Python example