Documentation menu
Concepts
Pagination
List endpoints return a page of results and a cursor for the next page:
{
"data": [ ... ],
"next_cursor": "cur_5RbJ8uYc3XeN1qGh6tZkWo"
}- Set the page size with
limit(1 to 100, default 20; portfolio sites allow up to 500). - Pass
next_cursorback unchanged ascursor, with the same other parameters, to get the next page. next_cursorisnullon the last page.- Cursors are opaque (
cur_followed by 22 characters). Do not build or edit them: an altered cursor returns400 bad_request. They do not expire. - Results are in the order records were added to Planning Signal, oldest first. The order never changes, so you will not skip or repeat records while paging, even as new ones arrive (they join the end).
/api/v1/applications/:id/conditionsreturns up to 100 conditions in one response and/api/v1/authoritiesreturns every council at once; neither is paginated.- If your allowance runs out part-way through a page, the page is shorter than
limitandnext_cursorcontinues from the last record returned.
A complete paging loop
const KEY = process.env.PLANNING_SIGNAL_API_KEY;
async function* allApplications(params) {
let cursor = null;
do {
const url = new URL("https://planningsignal.co.uk/api/v1/applications");
for (const [k, v] of Object.entries({ ...params, limit: 100 })) url.searchParams.set(k, v);
if (cursor) url.searchParams.set("cursor", cursor);
const res = await fetch(url, { headers: { Authorization: `Bearer ${KEY}` } });
const body = await res.json();
if (!res.ok) throw new Error(`${res.status} ${body.code}`);
yield* body.data;
cursor = body.next_cursor;
} while (cursor);
}
for await (const app of allApplications({ authority: "Camden", since: "2026-09-01" })) {
console.log(app.id, app.reference);
}Questions? Contact us, or see the help centre.