Pagination
Lists use cursors:
?limit=sets the page size: 1–100, default 25.- The response is
{ "data": [ … ], "meta": { "nextCursor": "…" } }. Passmeta.nextCursorback as?cursor=for the next page, with the same filters. It isnullon the last page. - Order is stable: newest first unless the operation says otherwise.
- Lists have no total count.
Cursors are opaque and signed. They are bound to your organization and to the filters of the first request: a cursor from another organization, with other filters, or edited, answers 400 VALIDATION_ERROR. Don’t build or store them for later; follow them.
curl -s -H "Authorization: Bearer $TOKEN" "https://api.tradecrafthub.ai/v1/assessments?status=finalized&limit=100"# next page:curl -s -H "Authorization: Bearer $TOKEN" "https://api.tradecrafthub.ai/v1/assessments?status=finalized&limit=100&cursor=$NEXT"async function* listAll<T>(path: string, token: string): AsyncGenerator<T> { let cursor: string | null = null do { const url = new URL(`https://api.tradecrafthub.ai/v1${path}`) url.searchParams.set("limit", "100") if (cursor) url.searchParams.set("cursor", cursor) const res = await fetch(url, { headers: { Authorization: `Bearer ${token}` } }) if (!res.ok) throw new Error(`${res.status} ${(await res.json()).error?.code}`) const page = (await res.json()) as { data: T[]; meta: { nextCursor: string | null } } yield* page.data cursor = page.meta.nextCursor } while (cursor)}
for await (const assessment of listAll<{ id: string; status: string }>("/assessments?status=finalized", token)) { console.log(assessment.id, assessment.status)}