Gemini batch job EXPIRED after 48 hours vs Sume run expires_at

A Gemini batch job ends EXPIRED after 48 hours pending or running. A Sume Format run is finalized as failed at expires_at. Compare the two clocks.

5 min readSume
All posts

Which batch deadline should you plan around, Gemini's or Sume's? They measure different things. The Gemini Batch API guide (read 2026-10-04) targets a 24-hour turnaround and lists JOB_STATE_EXPIRED for a job that was pending or running past 48 hours. Sume's per-run deadline is expires_at on the run receipt: 90 minutes from created_at, or sooner if the run goes silent, after which the run is finalized as failed.

So a Gemini job gives you a long, vague window and a terminal state that says nothing was delivered in time. A Sume run gives a short, explicit timestamp you can read on every non-terminal receipt. Your code should treat them as separate clocks.

The clocks in one table

Deadlines, Gemini page and Sume Runs docs read 2026-10-04
Gemini batch jobSume Format run
Turnaround target24 hoursNone promised; read expires_at
Hard limit48 hours pending or running, then JOB_STATE_EXPIRED90 minutes from created_at, or sooner when the run is silent
What you see at the limitEXPIRED statestatus failed with an error
Where to read the deadlineJob state onlyexpires_at on the receipt
Results kept6 weeksMedia URLs on media.sume.com are durable

How to write the Sume handler

Use expires_at as your ceiling: stop polling at that timestamp plus a small margin, then fetch the receipt once more rather than assuming the outcome. A bulk queue does not change this, because each child is an ordinary run with its own deadline. A queue becomes completed when every item is terminal, and a child that timed out is a failed item.

A hung vendor job and a force-finalized run need different recovery. For the Gemini side you resubmit the whole job or the missing requests. For Sume you start a new run with a new idempotency key, or continue the old one with previous_run_id.

Planning a holiday job around both limits

A seasonal deadline is the real constraint. If you need 100 finished videos by a date, a Gemini job that takes the full 24-hour target leaves little room to retry, and an EXPIRED job at 48 hours leaves none. Submit early and keep the work split into jobs small enough to resubmit.

On Sume the budget is different: the per-run ceiling is short, and the queue drains up to concurrency runs at a time, so the total time is the sum of the waves, not a single wait. A queue of 100 items with a window of 16 starts a new item whenever one finishes. Read each child's expires_at from its receipt and alert on any run that goes silent before it.

Neither vendor's page gives a duration for rendering a video, and this post does not invent one. Measure your own Format on a sample of ten before you size a campaign.

A small state mapper

This mapper turns both vendors' terminal vocabularies into one internal word, so downstream code stays simple. Note Google's spelling JOB_STATE_CANCELLED with two L's, and Sume's canceled with one.

GEMINI = {
    "JOB_STATE_SUCCEEDED": "done",
    "JOB_STATE_FAILED": "retry",
    "JOB_STATE_CANCELLED": "stopped",
    "JOB_STATE_EXPIRED": "retry",
    "JOB_STATE_PENDING": "waiting",
    "JOB_STATE_RUNNING": "waiting",
}
SUME = {
    "completed": "done",
    "failed": "retry",
    "canceled": "stopped",
    "skipped": "retry",
    "queued": "waiting",
    "processing": "waiting",
}
for name, table, state in [("gemini", GEMINI, "JOB_STATE_EXPIRED"), ("sume", SUME, "failed")]:
    print(name, state, "->", table[state])

Sources

Related posts

More in Comparisons

All Comparisons posts

Written by Sume