Pagination
Lists that can grow are read one page at a time with a cursor, newest first.
Runs, plans and reviews are listed newest first, one page at a time. Each page carries a cursor for the next one:
- Call the list endpoint, optionally with
limit. - If
nextin the answer is not null, call it again withcursorset to that value. - Stop when
nextis null.
GET /api/runs?limit=20{
"runs": [ { "run_id": "01JB8X6F4M2C9QZ3T7V5W1K0HA", "status": "completed" } ],
"next": "01JB8W2Q7N4D6R8S0T2V4X6Z8A",
"scanned": 20
}GET /api/runs?limit=20&cursor=01JB8W2Q7N4D6R8S0T2V4X6Z8ACursors are opaque: pass them back as you got them, and don't build your own. They don't expire.
| Endpoint | Items | limit | Default |
|---|---|---|---|
GET /api/runs | runs | 1 to 100 | 50 |
GET /api/plans | plans | 1 to 100 | 20 |
GET /api/reviewer/reviews | reviews | 1 to 200 | 50 |
GET /api/repos is not paged: a team's repositories come back in one answer.
Short pages when searching runs
GET /api/runs with a search (q) or filters (status, source, repo) reads a few hundred runs per request and keeps the ones that match. A page can then hold fewer than limit runs, or none, while next is still set: older runs are unread. Keep reading until next is null. scanned says how many runs the page read.
Reading every page
async function* allRuns(query: Record<string, string> = {}) {
let cursor: string | null = null;
do {
const params = new URLSearchParams({ ...query, limit: '100', ...(cursor ? { cursor } : {}) });
const res = await fetch(`https://api.coderhelm.com/api/runs?${params}`, {
headers: { Authorization: `Bearer ${process.env.CODERHELM_API_KEY}` },
});
if (!res.ok) throw new Error((await res.json()).error);
const page = await res.json();
yield* page.runs;
cursor = page.next;
} while (cursor);
}
for await (const run of allRuns({ status: 'failed' })) console.log(run.run_id, run.title);GET /api/plans also returns its cursor as next_cursor, kept for older clients; use next.