List every Format run: no GET /v1/format-runs, page per Format
GET /v1/format-runs does not exist. List runs per Format with limit, next_cursor and has_more, or keep your own index of the data.id you stored at create.

You cannot list every Format run in one call, because there is no GET /v1/format-runs. The docs say so directly: list the runs of each Format, or keep your own index keyed by the data.id you stored when you created the run. The list route is GET /v1/formats/{handle}/{slug}/runs, and it pages with limit, next_cursor and has_more.
Developers often hit this the first time they build an admin screen or a reconciliation job and guess a path. This page lists the three guesses and the correct reads.
Routes people guess, and what exists
| Guess | Result | Use instead |
|---|---|---|
GET /v1/format-runs | Does not exist | List per Format, or keep your own index |
GET /v1/formats/{handle}/{slug}/runs/{run_id} | Does not exist | GET /v1/format-runs/{run_id} |
GET /v1/format-runs/{run_id}/messages | Not published | events_url for the phase timeline; output and artifacts[] for the result |
The Format path only creates runs and lists them. Reading one run always goes through /v1/format-runs/{run_id}. If your key's workspace has never started a run of that Format over the API, the list call returns an empty list, not a 404.
How the paging works
The list is newest first. limit is 1 to 100 and defaults to 20. The response is a page: send next_cursor back as cursor until has_more is false. The cursor is opaque and is a keyset over (created_at, id), so runs created while you page do not shift rows between pages. A cursor that Sume did not issue returns 400 invalid_request, so never build one by hand.
The Formats list at GET /v1/formats uses the same has_more and next_cursor fields with a data array, and the sample below reads the runs list the same way.
const key = process.env.SUME_API_KEY;
const base = process.env.SUME_API_BASE_URL ?? "https://api.sume.com/v1";
if (!key) throw new Error("set SUME_API_KEY");
// There is no GET /v1/format-runs. List the runs of one Format, newest first.
export async function* runsOf(formatPath) {
let cursor;
do {
const qs = new URLSearchParams({ limit: "100" });
if (cursor) qs.set("cursor", cursor);
const res = await fetch(`${base}/formats/${formatPath}/runs?${qs}`, {
headers: { "x-api-key": key },
});
if (!res.ok) throw new Error(`list failed: ${res.status}`);
const page = await res.json();
yield* page.data;
cursor = page.has_more ? page.next_cursor : undefined;
} while (cursor);
}
let n = 0;
for await (const run of runsOf(process.argv[2] ?? "acme/product-promo")) n += 1;
console.log("runs listed:", n);Notes on the sample
- Each page is one read, and reads draw on the read budget, which is 40 times the write budget. A full sweep over a Format with many runs is therefore safe next to your creates. Still watch
ratelimit-remaining, and on a429waitretry-afterseconds. - The key needs
formats:read. A key without it gets403 insufficient_scope. - Runs from another workspace's key are invisible. A run you cannot see gives the same
404 format_run_not_foundas one that does not exist. - Pass the Format as
handle/slug, for example the pair in the Format'svanity_invoke_url. The opaque twinGET /v1/formats/{format_id}/runstakes theskl_id instead.
Build the index at create time
Listing is for audits. For day-to-day lookups, store the run id the moment the create call returns. Keep one row per business object: your order id, the Idempotency-Key you sent, the Format, and data.id. The docs recommend this for another reason as well: a value you send in input does not come back in output unless the run repeats it, so identifiers belong on your side, keyed by data.id or by your key.
When a webhook arrives, run_id and request_id are equal and stable across retries, so they join cleanly to that table. See the Runs and results page for the full list of read routes.
Sources
Related posts
More in Developers
- List Sume image models that take references or masks with jq
New image models land weekly. One curl and one jq filter on GET /v1/images/models show which ids accept input_references, mask_url or background, and how many.
- List Sume TTS Router models and prices in Python: one GET
GET /v1/tts-router/models returns five Sonic ids with per-character price and constraints. A short Python script prints each model's price per 1,000 characters.
- How do I add a listen-to-this-page audio version with TTS?
Turn each article into an audio file with one async TTS job per page: a 9,000-character article costs 43 cents on Sume. What it does not replace.
- LTX v1 video endpoints end Oct 26: what a Sume job client changes
LTX turns off five v1 video routes on 2026-10-26. Client habits that survive a cutoff like that on Sume: catalog ids, stored job ids, one submit function.
Written by Sume