Pagination
Read long lists page by page with `limit` and an opaque `cursor`.
Lists that can grow large use keyset pagination: you ask for a page with limit, and the answer tells you
where the next page starts.
Which operations page
| Operation | Order |
|---|---|
listBatteryModels | By name, then id |
listBatteries | By serial number, then id |
listEvidence | Newest first |
listFacilities | By name, then id |
listLifecycleEvents | Newest first |
listPassportVersions | Newest first |
Other lists, such as values, value histories and model versions, return all items in one response.
Request
limitintegerquerydefault: 50Number of items per page, from 1 to 200.
cursorstringqueryThe next_cursor of the previous page. Leave it out for the first page.
Send the operation's filters (for example battery_model_id or passport_status of listBatteries)
unchanged with every page.
Response
itemsobject[]requiredThe items of this page, at most limit.
next_cursorstring | nullrequiredThe cursor for the next page, or null when this is the last page.
{
"items": [
{ "id": "0192f0a3-1b7c-7e2a-8f45-6a9d0c3e5b12", "serial_number": "SV2027-000001" },
{ "id": "0192f0a3-1b7c-7e2a-8f45-6a9d0c3e5b13", "serial_number": "SV2027-000002" }
],
"next_cursor": "eyJzIjoiU1YyMDI3LTAwMDAwMiJ9"
}The sample shows only two members of each unit; real items carry the full schema of the operation.
Rules for cursors
- A cursor is opaque. Do not parse it, build it or change it; its content may change at any time.
- A cursor belongs to the query that produced it: the same operation, the same path parameters and the same filters. Use it right away for the next page; do not keep cursors as long-term bookmarks.
next_cursor: nullmeans there are no more items.- A cursor that cannot be used is
400 validation_erroron the fieldcursor; alimitoutside 1 to 200 is a400on the fieldlimit. - Pages are not a snapshot. Items created or changed while you walk through a list may or may not appear. For a consistent copy, page through and then reconcile by id (see Synchronise units).
Read every page
The loops below read all units with a published passport, 200 per page.
type Battery = { id: string; serial_number: string };
type Page<T> = { items: T[]; next_cursor: string | null };
const base = `${process.env.BATTERYPASS_URL}/api/v1`;
const headers = { Authorization: `Bearer ${process.env.BATTERYPASS_API_KEY}` };
async function* listBatteries(filters: Record<string, string>): AsyncGenerator<Battery> {
let cursor: string | null = null;
do {
const query = new URLSearchParams({ ...filters, limit: '200' });
if (cursor) query.set('cursor', cursor);
const res = await fetch(`${base}/batteries?${query}`, { headers });
if (!res.ok) throw new Error(`listBatteries: ${res.status} ${await res.text()}`);
const page: Page<Battery> = await res.json();
yield* page.items;
cursor = page.next_cursor;
} while (cursor);
}
let count = 0;
for await (const unit of listBatteries({ passport_status: 'published' })) {
console.log(unit.serial_number, unit.id);
count++;
}
console.log(`${count} units`);Each page is one request against your organisation's limit of 600 requests per minute. Use limit=200 for
bulk reads and see Rate limits for long walks.