Check an image request against the Sume catalog before you send it
Fetch GET /v1/images/models once, then reject a bad ratio, quality or reference count in Python before it reaches POST /v1/images and returns a 400.

To avoid a 400 from POST /v1/images, fetch GET /v1/images/models once, keep the result, and check each request against the model's supported_parameters in your own code first. Sume rejects a parameter that the selected model does not list with 400 unsupported_parameter and never drops it silently, so a local check turns a failed call into a message you wrote.
The Sume Image API docs describe the catalog descriptors: an enum is a list of allowed strings, a range has min and max, and a boolean means the parameter is supported. They also say each model accepts only the values its descriptors list, so you should read supported_parameters before you pin a tier or a ratio. This matters more now that new models arrive often; two different models can differ on quality values, ratios and reference counts.
What the descriptors look like
Each catalog row has the same shape. The table lists the descriptor types you need to handle in a checker.
| Descriptor | Example field | How to check |
|---|---|---|
| enum | aspect_ratio, quality, resolution | Value must be in values |
| range | n, input_references | Count must be between min and max |
| boolean | prompt | Field must be present in the catalog |
| absent | seed, output_compression | Do not send; the API returns 400 |
A small checker
The function below returns a list of problems. It uses the descriptor names from the docs and skips checks for parameters the request does not send. Call it before the POST and log or raise on a non-empty list.
import os
import requests
H = {"Authorization": f"Bearer {os.environ['SUME_API_KEY']}"}
rows = requests.get("https://api.sume.com/v1/images/models", headers=H, timeout=30).json()["data"]
CATALOG = {row["id"]: row["supported_parameters"] for row in rows}
def problems(body):
sp = CATALOG.get(body["model"])
if sp is None:
return ["model is not in the catalog (sume/auto is not listed)"]
out = []
for key, value in body.items():
if key in ("model", "prompt", "input_references"):
continue
d = sp.get(key)
if d is None:
out.append(f"{key} is not supported by {body['model']}")
elif d["type"] == "enum" and value not in d["values"]:
out.append(f"{key}={value} not in {d['values']}")
refs = len(body.get("input_references", []))
cap = sp.get("input_references", {}).get("max", 0)
if refs > cap:
out.append(f"{refs} references, model accepts {cap}")
return out
print(problems({"model": "openai/gpt-image-2.5", "prompt": "x", "seed": 7}))Where to run it
Run the check in the code path that builds requests, not in a test only. Cache the catalog for the life of the process, or for minutes if you run long, and refetch when a model id is unknown. Do not cache for days; the catalog is the source of truth for what a model takes.
Skip the check for sume/auto. It is a Sume-only value that the catalog does not list, and the docs say Sume chooses the family and never names it. If you pin a model for brand work, check against that model; if you use Auto, send only parameters every model takes, such as prompt and aspect_ratio values from common ratios.
What the check will not catch
A preflight is a guard, not a guarantee, and it is worth knowing where it stops.
- It cannot see whether a reference URL is reachable; that fails at job time.
- It does not know the price; read the endpoint pricing lines for that.
- It does not predict a slow job; a high quality at 4K can still return 202.
- It trusts the catalog snapshot; refresh it when you deploy.
- It does not check prompt content or policy.
A test for the checker
Keep a few known-bad requests in a unit test: an unknown ratio for a model, a quality value that only another model takes, and a reference count above the model's maximum. The expected output is a non-empty list for each. Add one known-good request that must return an empty list. When a model is updated and the test starts to fail, you have found a catalog change before a customer did. Mock the catalog response in the test so the test does not need a key or network access, and keep one live smoke test that you run on deploy.
Errors you can still get
A request that passes the checker can still return a 202 job envelope when it takes longer than the 30-second wait, or a 502 when the generation fails. Branch on the status code, not the body shape, as the docs advise. Failed or cancelled generations are not billed, while completed ones are billed in full.
Sources
Related posts
More in Developers
- TTS then H3 Max lip sync: check the 5 to 14.8 s audio window
H3 Max lip sync takes 5 to 14.8 seconds of Sume-hosted audio and clips the rest. Measure a TTS line from its word timings and price it before you submit.
- CI smoke test for Ideogram 4.5 on Sume: one low-quality image
A bash and jq check that submits one 1K low-quality Ideogram 4.5 image, passes on 200 or 202, and explains 401, 402 and 429. List price is $0.03.
- Clamp video duration when swapping Sora for a Sume video model
Each Sume video id has its own seconds range, so old Sora code can ask for a length a model rejects. Clamp per id before you submit; Python table of limits.
- Claude Code PreToolUse hook: block paid Sume calls without a spend cap
A PreToolUse hook that denies Sume generate and TTS tools when max_spend_usd or idempotency_key is missing. Python hook with the settings.json matcher, tested.
Written by Sume