Sume jobs list: no next_cursor on the last page ends the loop
GET /v1/jobs returns up to 100 jobs newest first. Pass data.next_cursor back as starting_after, and stop when it is absent. Do not build a cursor yourself.

GET /v1/jobs returns at most 100 jobs per page, newest first. When more remain, data.next_cursor is present, and you pass it back as starting_after. On the last page it is absent, and that absence is your loop terminator. Do not build a cursor from the last job id, because the cursor is opaque. An undecodable cursor is treated as the first page.
Paging rules in one table
| Rule | Detail |
|---|---|
| Order | Newest first |
| Page size | Up to 100 |
| Next page | Send data.next_cursor as starting_after |
| Last page | next_cursor is absent |
| Undecodable cursor | Treated as the first page, not an error |
| Visibility | An API key lists its own member's jobs |
A generator that follows the cursor
It stops when the field is missing, and it never inspects job ids.
import os, requests
BASE = "https://api.sume.com/v1/jobs"
HEADERS = {"Authorization": f"Bearer {os.environ.get('SUME_API_KEY', '')}"}
def list_jobs(session, status):
cursor = None
while True:
params = {"status": status, "limit": 100}
if cursor:
params["starting_after"] = cursor
r = session.get(BASE, headers=HEADERS, params=params, timeout=30)
r.raise_for_status()
data = r.json()["data"]
yield from data["jobs"]
cursor = data.get("next_cursor")
if not cursor: # absent on the last page
return
if __name__ == "__main__":
with requests.Session() as s:
for status in ("queued", "processing"):
for job in list_jobs(s, status):
print(job["id"], status, job.get("idempotency_key"))Common mistakes
- Looping until a page is empty: that costs one extra read, which counts against your read budget.
- Assuming the order matches your submission order. Retries break that, so join on
idempotency_key. - Passing a job id as the cursor: it will not decode, and you get the first page again, which can loop forever.
Sources
Related posts
More in Developers
- Sume GET /v1/jobs: a misspelled filter returns 400, not all jobs
Sume rejects an unrecognized query parameter on GET /v1/jobs with 400 unknown_parameter, so a typo cannot return an unfiltered page. Valid filters listed.
- Sume MCP tool names: tools.list or tools_list, which one to call?
Use the underscore ids from tools_list, like generate_image. Sume also accepts dotted aliases, but tools_schema wants snake_case and clients may add a prefix.
- A Sume poll returned HTML: guard the JSON parse in Python
A proxy or edge can answer a Sume poll with an HTML page. A tested stdlib Python helper that returns None for non-JSON bodies so your loop retries, not crashes.
- Which statuses to retry when reading back a Sume job (Python)
After a Sume submit returns a job id, retry reads on 408, 425, 429, 500, 502, 503, 504 and 520 to 525. A tested Python classifier and the 409 gotcha.
Written by Sume