Ideogram is_image_safe in the response: what Sume returns instead
Ideogram's precise edit returns an is_image_safe field per image. Sume has no such field: a refused input ends as a failed job with generation_rejected.

Ideogram's Precise Edit reference (read 2026-10-06) lists is_image_safe among the fields returned for each generated image. Sume's /v1/images response has no equivalent per-image flag. Its documented response is data[].url, media_type and a usage.cost. When the input is refused, you see it on the job.
So the port is not a field rename. You move a check from the image object to the job status.
Where Sume reports a refusal
Sume's errors page lists generation_rejected as a job error category, and the suggested action is to examine the events and correct the unsupported input. Failed jobs carry public error metadata: category, stage, retryability, retry-after seconds, a public reason and a next action. The Image API page adds that failed or cancelled generations are not charged.
Field mapping
| Ideogram signal | Sume signal | What to do |
|---|---|---|
is_image_safe on each image | Job status failed, category generation_rejected | Poll status, then read the error |
| HTTP error on a bad request | 400 with a code such as unsupported_parameter | Fix the request, same idempotency key only if the payload is unchanged |
| Charge when refused | Failed or cancelled: no charge | Check usage.cost on completed jobs only |
A status check that prints the error
This submits a cheap async generation and prints the whole status object, so you can see the category field names for your own account instead of guessing them.
import json, os, time, requests
key = os.environ.get("SUME_API_KEY")
if not key:
raise SystemExit("set SUME_API_KEY")
headers = {"Authorization": f"Bearer {key}", "Idempotency-Key": "safety-probe-001"}
body = {"model": "ideogram/ideogram-v4.5", "quality": "low", "mode": "async",
"prompt": "A plain teal poster that reads OPEN"}
r = requests.post("https://api.sume.com/v1/images", headers=headers, json=body, timeout=60)
data = r.json()["data"]
while True:
s = requests.get(data["status_url"], headers=headers, timeout=30).json()
s = s.get("data", s)
if s.get("terminal"):
print(json.dumps(s, indent=2))
break
time.sleep(2)Handling it in a batch
Batch edits make the difference matter. With a per-image flag you can keep nine good frames and drop one. With a job-level outcome you decide per job, so submit one edit per job and keep your own table that maps each source image to its job id and final status.
Do not resubmit a failed edit unchanged. The refusal is about the input, so a retry spends a request and gets the same answer. Change the source or the instruction, then submit with a new Idempotency-Key.
Sources
Related posts
More in Developers
- SSE or WebSocket for AI video progress on Sume? Poll or webhook
Sume has no SSE or WebSocket. mode subscribe is the same 30 s wait as sync. Use async with status polling, the events snapshot, or a webhook. Python example.
- Keep a series voice consistent: pin model, voice, speed and volume
A series sounds the same only if every episode sends the same TTS settings. Keep one profile in code, pin a model id, and send it with each Sume request.
- Same volume every Shorts episode: gain_db, duck_db, one music bed
Sume does not measure loudness. To keep a series consistent, pin audio.gain_db, soundtrack gain_db and duck_db in one function with one bed. Python plan loop.
- Ktor webhook route for an AI video job: receiveText and HMAC SHA-256
A Ktor route reads the raw body with call.receiveText(), checks Sume's sume-v1 HMAC over timestamp.body in Kotlin and returns 401 when the secret is empty.
Written by Sume