Image request timed out: do you pay for it on Sume?

Sume bills image jobs by outcome. A client timeout does not cancel the job, so a finished image can still bill. Use async mode and poll; do not resubmit.

5 min readSume
All posts

If your own client times out on a Sume image call, do not assume you are not charged. The Jobs docs say a client-side timeout does not cancel the job: it keeps running and still bills, and you have only stopped watching. The Image API docs also say generation billing is all-or-nothing, and that a failed or cancelled generation is not billed. So the outcome of the job decides the bill, not the state of your connection. Do not resubmit; look the job up.

What each page says

The two pages read differently at first glance, so here they are side by side.

Billing statements, read 2026-10-02
PageStatement
Image APICompleted generations are fully billed; failed or cancelled generations are not billed.
Image APIClient disconnects: requests ending early are billed as failed generations (not at all).
Jobs and resultsA client-side timeout does not cancel the job. The job keeps running and still bills.
Jobs and resultsNot terminal? Poll. Do not resubmit.

How to read them together

The Image API line covers a request that ends before generation completes, which is then a failed generation. The Jobs line covers the case where the job continues after you stop watching, which can finish and bill. Because you cannot tell from your side which happened, treat the job record as the source of truth.

Do this instead

Submit with mode: "async" and an Idempotency-Key, then poll the job status until it is terminal.

import os, time, uuid, requests

H = {"Authorization": f"Bearer {os.environ['SUME_API_KEY']}"}
sub = requests.post("https://api.sume.com/v1/images", timeout=60,
    headers={**H, "Idempotency-Key": str(uuid.uuid4())},
    json={"model": "openai/gpt-image-2.5", "prompt": "a red kite", "mode": "async"})
job_id = sub.json()["job"]["id"]
while True:
    s = requests.get(f"https://api.sume.com/v1/jobs/{job_id}/status", headers=H, timeout=30).json()
    if s["terminal"]:
        break
    time.sleep(s.get("next_poll_after_seconds") or 3)
print(s["sume_status"])

Check your spend

GET /v1/usage can be filtered by job id to confirm what a job cost. Send Idempotency-Key on submits you retry, so a retry does not create a second job.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume