Skip to content

Pagination

The list-returning endpoints — search, category, and the paid change feed — page with an opaque cursor. The single-title lookup returns one object and never paginates.

Every paged response wraps its rows in data and carries a pagination block:

{
"data": [ /* SearchResult[] or Verdict[] or ChangeRecord[] */ ],
"pagination": {
"nextCursor": "eyJ2IjoyfQ",
"hasMore": true
}
}
  • nextCursor — an opaque token. Pass it back as ?cursor= to get the next page. It is null on the last page. Never parse or construct it; its format can change.
  • hasMoretrue when another page exists.

Use limit (default 50, max 100). Larger limit means fewer round trips; the cap keeps any single response bounded.

Terminal window
curl "https://api.doesitarm.com/v1/categories/productivity/verdicts?limit=100"

Walk every page until nextCursor is null:

async function* allVerdicts(category) {
let cursor = null
do {
const url = new URL(
`https://api.doesitarm.com/v1/categories/${category}/verdicts`
)
url.searchParams.set("limit", "100")
if (cursor) url.searchParams.set("cursor", cursor)
const { data, pagination } = await (await fetch(url)).json()
yield* data
cursor = pagination.nextCursor
} while (cursor !== null)
}
for await (const verdict of allVerdicts("productivity")) {
console.log(verdict.slug, verdict.status)
}

Each page still carries the rate-limit headers, so honor RateLimit-Remaining while looping. For keeping a large local copy current, prefer the change feed over re-walking every page.