PlanningTrack API · Free beta

Pagination and updates

Page through search results, then use ingestion checkpoints for continuing updates.

Ordinary lists

Responses contain items and nextCursor. Pass nextCursor unchanged as cursor until it is null. The default page size is 50; the maximum is 100. Keep endpoint and filters unchanged. You may change page size.

Application and authority lists use ascending ID order. Search pagination is not a frozen dataset: concurrent imports can appear earlier in the order. Document and timeline pagination are pinned to an immutable snapshot.

Initial import and ongoing changes

  1. Call /v1/changes?authority=local-authority-oxo&start=now and durably save its checkpoint.
  2. Page through applications, upserting by application ID.
  3. Poll changes from the saved checkpoint, refreshing affected application IDs.
  4. Commit each page before saving its new checkpoint, including empty pages.

Each event has a stable ID. Deduplicate replays. The feed orders by per-shard ingestion sequence, so old observations imported today still appear. observedAt is evidence time and must not be used as a pagination cursor.

Database integration template · implement the named persistence functions
// Call request() from the downloadable JavaScript example.
// Save checkpoints in your own durable database, never only in memory.
const filters = new URLSearchParams({ authority: 'local-authority-oxo', limit: '100' });

// On first setup, BEFORE importing application pages:
const initial = await request('/v1/changes?' + filters + '&start=now');
await saveCheckpoint(initial.checkpoint);
// Import application pages, upserting by application ID.
// Afterwards, run this polling loop daily:
let cursor = await loadCheckpoint();
for (;;) {
  const params = new URLSearchParams(filters);
  params.set('cursor', cursor);
  const page = await request('/v1/changes?' + params);
  for (const change of page.items) {
    if (await alreadyProcessed(change.id)) continue;
    const app = await request('/v1/applications/' + encodeURIComponent(change.applicationId));
    // Commit the upsert and processed event ID together in your database.
    await upsertApplicationAndEvent(app, change.id);
  }
  await saveCheckpoint(page.checkpoint); // Only after the entire page succeeds.
  cursor = page.checkpoint;
  if (page.nextCursor === null) break;
}
// Implement saveCheckpoint, loadCheckpoint, alreadyProcessed,
// and upsertApplicationAndEvent using your own database.
// If a process crashes, replaying the page is safe with event-ID deduplication.

The feed reports material source, parser, availability, and registry changes. It does not emit an event for every unchanged check. Historical discoveries are included even when notifyEligible is false. Checkpoints have no scheduled expiry in v1; retain the same filters.

Plan around 500 requests

A daily poll uses roughly 30 requests per month before pagination and detail retrieval. Fetch each affected application once per batch where possible. Stop on quota exhaustion and resume from your durable checkpoint after the monthly reset.