quality on Flux or Seedream returns 400 on Sume: which rows take it
Only GPT Image 2.5, GPT Image 2 and Ideogram 4.5 list quality on POST /v1/images. Send it to Flux, Seedream or Qwen and you get 400 unsupported_parameter.

On POST /v1/images, quality is accepted by only some rows. In the Sume catalog the rows that list a quality parameter are ChatGPT Image 2.5 (and its Sunburst variant), ChatGPT Image 2 and Ideogram 4.5. Send quality to Flux 2 Pro, Flux 2 Flex, Seedream, Qwen, Recraft V4, Nano Banana or Imagen and the request fails with 400 unsupported_parameter. Nothing is billed for a rejected request.
Which rows list it
The Image API docs say a parameter a model does not list is rejected, and that the model's supported_parameters descriptor is the source of truth. The catalog code only fills a quality enum for the rows below.
| Row | quality values | Default |
|---|---|---|
| ChatGPT Image 2.5 and Sunburst | auto, low, medium, high, xhigh, max | high |
| ChatGPT Image 2 | low, medium, high | not documented |
| Ideogram 4.5 | low, medium, high | medium |
| Flux 2 Pro, Flux 2 Flex, Qwen Image, Qwen Image Max, Recraft V4, Seedream 4, 4.5, 5 Lite | not listed | 400 if sent |
The fix is in the request, not the model
If you want a cheaper or better tier on a row without quality, pick a different row. Flux 2 Pro costs $0.0375 and Flux 2 Flex costs $0.0625 per image at one fixed tier each. Seedream 5 Lite is $0.04375. These are flat prices with no tier to choose.
Resolution works the same way. It is listed only on Nano Banana, Imagen 4 Ultra, Soul and Ideogram 4.5. On other rows you choose a size with image_size or aspect_ratio instead.
Build the body from the descriptor
Rather than keep a table in your own code, read supported_parameters and drop what is not there. This function filters a body to the keys a model lists.
import os, requests
H = {"Authorization": f"Bearer {os.environ['SUME_API_KEY']}"}
r = requests.get("https://api.sume.com/v1/images/models", headers=H, timeout=30)
r.raise_for_status()
CATALOG = {m["id"]: m["supported_parameters"] for m in r.json()["data"]}
def fit(model: str, body: dict) -> dict:
keep = set(CATALOG[model]) | {"model"}
return {k: v for k, v in body.items() if k in keep}
body = {"model": "black-forest-labs/flux.2-pro", "prompt": "a mug", "quality": "high"}
print(fit(body["model"], body))Silent dropping has a cost too
The filter hides the difference. If your app shows a quality control, hide it for rows without the parameter instead of dropping the value silently, or users will think they chose high and got the only tier on offer.
What a 400 looks like in practice
The error code is unsupported_parameter and the message names the field. Because the check runs before generation, the call is not queued and not billed, so a wrong body costs you a round trip and nothing else.
A common cause is a UI that shares one settings panel across models. Switch the model and the panel keeps the old quality value. Clear or hide any field that the new row does not list, and test the switch from GPT Image 2.5 to Flux 2 Pro with quality set, since that is the case that breaks.
- Rows without quality can still differ in price: $0.025 to $0.09375 across the flat rows.
- Rows with quality price by tier (Ideogram 4.5) or by tokens (GPT Image 2.5).
- Quality is also a field on Image 1.0, where low is the default.
Sources
Related posts
More in Developers
- Railway closes idle HTTP at 5 minutes: why Sume sync waits 30 s
Railway keeps a request open up to 15 minutes only while data moves. Sume sync mode caps the wait at 30 seconds, so long video jobs need async or a webhook.
- Ratio check before a batch: 4:5, 9:16 and 8:1 on three Sume rows
Imagen 4 lists 5 ratios, GPT Image 2.5 lists 9 and Nano Banana 2.1 lists 15. Which of 4:5, 9:16 and 8:1 each Sume row accepts, and the fallback.
- Read back a Sume TTS job to keep the next line in the same voice
A completed Sume text_to_speech job records model_id, voice, language, output_format, generation_config and speed. Reuse them so line two matches line one.
- Worker crashed mid-poll: list Sume jobs and join on idempotency_key
After a restart, GET /v1/jobs?status=processing lists your in-flight video jobs. Each row carries the idempotency_key you sent, so match your records on it.
Written by Sume