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.

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.
| Page | Statement |
|---|---|
| Image API | Completed generations are fully billed; failed or cancelled generations are not billed. |
| Image API | Client disconnects: requests ending early are billed as failed generations (not at all). |
| Jobs and results | A client-side timeout does not cancel the job. The job keeps running and still bills. |
| Jobs and results | Not 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
- Instagram Login or Facebook Login for a Reels publishing app?
Both logins can publish Reels. They differ in host, token and scopes, and resumable upload plus some metrics are Facebook Login only. Pick before you build.
- Instagram API Reels total_interactions: how it is calculated
Instagram's insights reference defines total_interactions as likes, saves, comments and shares minus unlikes and deletions, and marks it in development.
- Instagram content_publishing_limit: read quota_usage before a bulk run
Read GET /<IG_USER_ID>/content_publishing_limit before queuing Reels. Meta's pages cite 100 posts per 24 hours and show quota_total 50, so do not hard-code it.
- Instagram media_audio_type: MUSIC vs ORIGINAL_SOUND on Reels
Instagram added a media_audio_type field on June 1, 2026 that tells licensed MUSIC from ORIGINAL_SOUND. What it means for a Reel you build with Sume.
Written by Sume