Sume Image API 400 unsupported_parameter: which field fails where
Which Sume image models list quality, resolution, mask_url, background, output_format and references, and which never do (seed, stream). Plus a check script.

The Sume Image API returns 400 unsupported_parameter when a request sets a parameter the selected model does not list, and it never drops the field silently. No model lists seed, output_compression or streaming today, so those always fail. mask_url and background are listed by ChatGPT Image 2.5 (Flare and Sunburst) only. quality, resolution and output_format are listed by a handful of rows each, and the table below says which.
This is from the catalog on origin/main and the Sume docs, read 2026-10-08. The endpoints route is the source of truth, so the pre-flight script below checks it before you send.
Who lists what
Every row lists prompt, aspect_ratio and n. The rest varies.
| Field | Rows that list it |
|---|---|
| seed, output_compression, streaming | None |
| mask_url, background | ChatGPT Image 2.5 (Flare) and ChatGPT Image 2.5 Sunburst only |
| quality | ChatGPT Image 2 (3 values), ChatGPT Image 2.5 and Sunburst (6 values), Ideogram V3 and 4.5 (low, medium, high) |
| resolution | Nano Banana 2.1 and Pro (512 to 4K), Imagen 4 Ultra and Ideogram 4.5 (1K, 2K), Soul (720p, 1080p) |
| output_format | Not listed on Soul or Ideogram 4.5; webp only on Recraft V4; png, jpeg, webp elsewhere |
| input_references above 0 | Not on Soul, Qwen Image Max, Imagen 4 Fast, Imagen 4 Ultra, Recraft V4 |
| n above 1 | Every row except Grok Image |
A pre-flight check
This function reads the endpoint record and returns the keys in your body that the row will reject. It treats an input_references descriptor with a maximum of 0 as unsupported, since the docs say such a model rejects references. It checks names, not values; a value outside an enum is the other thing to compare against the descriptor.
import os, requests
SKIP = {"model", "prompt", "mode", "webhook_url", "provider"}
def unsupported(model_id, body):
url = f"https://api.sume.com/v1/images/models/{model_id}/endpoints"
headers = {"Authorization": f"Bearer {os.environ['SUME_API_KEY']}"}
ep = requests.get(url, headers=headers, timeout=30).json()["endpoints"][0]
listed = ep["supported_parameters"]
bad = []
for key in body:
if key in SKIP:
continue
if key not in listed:
bad.append(key)
elif key == "input_references" and listed[key].get("max", 0) == 0:
bad.append(key)
return bad
if __name__ == "__main__":
body = {"model": "google/imagen-4-fast", "prompt": "a tin", "quality": "high"}
print(unsupported(body["model"], body))
Building request bodies
Keep one body builder per model id and let it add optional fields only when the endpoint record lists them. A shared body with quality: "high" works on Ideogram and ChatGPT Image rows and fails on Flux and Seedream rows. Failed requests are not billed, but they still use a round trip, so the check is cheaper than discovering the rejection at send time.
Capability descriptors have three types: an enum is a list of allowed strings, a range is any integer between a minimum and a maximum, and a boolean means the field is accepted when present.
Log the full error body on every 400. It names the parameter that failed, which is quicker to act on than the status code alone. If you see it on a model you did not change, the catalog row may have changed, so fetch the endpoint record again and update your builder from it.
Sources
Related posts
More in Developers
- result_ready vs terminal vs completed: gate the Sume result fetch
Poll a Sume job until terminal, fetch the result only when result_ready is true. Failed and canceled jobs answer 409 job_not_completed on /result.
- Sume TTS word timestamps to caption cues for a narrated 60-second clip
Ask Sume TTS for timestamps.words, group them into cues and send them to video-captions so no recognition runs. About 25 cents for a 60-second narration.
- Sume TypeScript SDK waitForJob: 20-minute timeout, job keeps billing
How @sume-com/sdk waitForJob polls a generation job, what SumeJobTimeoutError means, and why a client timeout does not cancel or refund the job.
- Webhook endpoint down: redeliver a Sume video job after the retries
Sume retries a job webhook 10 times, 30 seconds apart. If your receiver was down longer, POST /v1/jobs/{id}/webhook/redeliver re-sends the terminal payload.
Written by Sume