Sume API pagination: cursor, starting_after, and page limits

How each Sume list endpoint pages: cursor and has_more on Formats and runs, starting_after on /v1/jobs, and limit-only lists that have no cursor.

5 min readSume
All posts

Sume API lists page in three ways: Formats, Actions, their run lists, and Agent Completion runs return next_cursor and has_more, and you send the cursor back as cursor, while GET /v1/jobs returns data.next_cursor, which goes back as starting_after. Avatar, event, usage, and search lists take a limit but no cursor, and return a single page.

The limits below come from the live OpenAPI reference and the docs pages for each surface, such as Runs and results and Scheduled runs, read on 2026-09-27. Paging through jobs, including crash recovery, has its own walkthrough in List jobs API.

How does each list endpoint page?

Each row links the post that covers that list. limit is the page size you ask for.

From the OpenAPI reference, API reference, Format API, Runs and results, Scheduled, Scheduled runs, Agent Completions, and Trending videos, read 2026-09-27.
EndpointlimitNext pageLast page
Formats: GET /v1/formats1–100, default 50cursor = next_cursorhas_more: false
Format runs: GET /v1/formats/{handle}/{slug}/runs1–100, default 20cursor = next_cursorhas_more: false
Actions: GET /v1/actions1–100, default 50cursor = next_cursorhas_more: false
Action runs: GET /v1/actions/{action_id}/runs1–100, default 50cursor = next_cursorhas_more: false
Agent Completion runs: GET /v1/agent-runs1–100cursor = next_cursorhas_more: false
Jobs: GET /v1/jobs1–100starting_after = data.next_cursordata.next_cursor is absent
Job events: GET /v1/jobs/{id}/events1–100No cursorOne page
Usage ledger: GET /v1/usage1–100 newest rowsNo cursorOne page
Avatars and avatar videos: GET /v1/avatars, GET /v1/avatar-videos1–100No cursorOne page
Background music: GET /v1/bgm/catalog1–200No cursor parameterOne page
Avatar catalog search: POST /v1/avatar-catalog/search1–100, default 20No cursorOne ranked page
Trending video search: POST /v1/trending-videos/search1–50, default 10 in productionNo cursorOne ranked page

How do I page with cursor and has_more?

Send the first request without cursor. While has_more is true, send next_cursor back as cursor; on the last page next_cursor is null. The same loop works for Formats, Actions, and all three run lists:

  • Treat the cursor as opaque: pass it back verbatim and never parse or build one. On Format and Action run lists, a cursor Sume did not mint is 400 invalid_request.
  • Format run pages are keyset over (created_at, id), newest first, so runs created while you page do not shift rows.
  • A Format that has never been run over the API returns an empty list, not a 404.
URL="https://api.sume.com/v1/formats/acme/product-promo/runs?limit=100"
NEXT=""
while :; do
  PAGE=$(curl -sS "$URL$NEXT" -H "Authorization: Bearer $SUME_API_KEY")
  echo "$PAGE" | jq -r '.data[] | [.id, .status] | @tsv'
  [ "$(echo "$PAGE" | jq -r '.has_more')" = "true" ] || break
  NEXT="&cursor=$(echo "$PAGE" | jq -r '.next_cursor | @uri')"
done

Why does GET /v1/jobs use starting_after instead?

Jobs page differently: rows arrive in data.jobs, newest first, data.next_cursor is present only while more jobs remain, and it goes back as starting_after. There is no has_more, and cursor is not a jobs parameter, so sending it is a 400 unknown_parameter. List jobs API walks through the loop and how to recover lost job ids.

Which lists return a single page?

These take a limit, and some take filters, but their responses carry no cursor:

  • GET /v1/avatars, GET /v1/avatar-1.0/avatars, and GET /v1/avatar-videos filter by status, where ready is an alias for completed jobs.
  • GET /v1/jobs/{id}/events returns one job's timeline.
  • GET /v1/usage lists the newest ledger rows. With run_id, thread_id, or job_id, its summary folds every row of that scope, and limit only caps the rows listed.
  • GET /v1/formats/{handle}/{slug}/contents?recursive=1 returns every file of a Format package, with its body, in one call.

Which lists do not exist?

Callers look for these and do not find them:

  • GET /v1/format-runs: there is no cross-Format run list. List per Format, or keep your own index keyed by the run id you stored at create.
  • A list of bulk queues: there is no public list-queues endpoint, so store each queue id from its create response.
  • One list of your personal and team Formats together: visibility follows the key, so a personal key lists your personal Formats, a team key lists that workspace's, and neither lists the other's.

Does paging count against my rate limit?

Yes. Every GET page is a read, and with an API key, reads get forty times the plan's write number in their own bucket, so a paging loop cannot starve your creates. The two POST searches spend the write budget instead. The per-plan budgets are in Sume API errors and rate limits.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume