Preflight an image request from Sume capability descriptors

Read supported_parameters from GET /v1/images/models and reject unsupported fields, enum values and out-of-range counts before a paid call returns a 400.

5 min readSume
All posts

You can catch most Image API mistakes before sending anything: fetch GET /v1/images/models, read the supported_parameters object for your model, and compare each field in your request against its typed descriptor. Sume rejects a field the model does not list with 400 unsupported_parameter, so a ten-line check in your own code saves a failed call and a retry loop.

This is a generic pattern. Image vendors keep changing which models take references, masks or custom sizes, and the Sume catalog publishes those facts as data rather than prose. The shapes below come from the Image API docs, read on 2026-10-03.

What are capability descriptors?

Every model row in GET /v1/images/models carries supported_parameters, a map from field name to a typed descriptor. The docs define three kinds. An enum is a discrete allowlist of string values. A range is any integer within min and max. A boolean means the field is supported when present and unsupported when absent.

Sume serves every catalog model through a single sume endpoint in v1, so the model-level and endpoint-level descriptors are identical. You can read them from the list call alone and skip the per-endpoint record unless you also want the pricing lines.

Descriptor kinds and how to test a request value (read 2026-10-03)
DescriptorExampleTest
enumaspect_ratio with values 1:1, 16:9, 4:3Value must be in values
rangen with min 1 and max 4Integer within min and max
range on a list fieldinput_references with min 0 and max 10Number of items within min and max
booleanprompt presentField must exist in the map

What does the preflight look like in Python?

The function below fetches the catalog, finds the model, and walks your request body. It skips fields that are not model capabilities, such as provider, metadata, mode and webhook_url. It is synchronous on purpose: one GET and some comparisons need no event loop.

import os
import requests

H = {"Authorization": f"Bearer {os.environ['SUME_API_KEY']}"}
SKIP = {"model", "provider", "metadata", "mode", "webhook_url", "wait_timeout_seconds"}

def preflight(body):
    r = requests.get("https://api.sume.com/v1/images/models", headers=H, timeout=30)
    r.raise_for_status()
    rows = {m["id"]: m for m in r.json()["data"]}
    model = rows.get(body["model"])
    if model is None:
        return [f"unknown model {body['model']}"]
    params, errs = model["supported_parameters"], []
    for key, val in body.items():
        d = params.get(key)
        if key in SKIP:
            continue
        if d is None:
            errs.append(f"{key}: not supported by this model")
        elif d.get("type") == "enum" and val not in d["values"]:
            errs.append(f"{key}={val!r}: allowed {d['values']}")
        elif d.get("type") == "range":
            n = len(val) if isinstance(val, list) else val
            if not d["min"] <= n <= d["max"]:
                errs.append(f"{key}: {n} outside {d['min']}-{d['max']}")
    return errs

print(preflight({"model": "bytedance-seed/seedream-4.5", "prompt": "x", "n": 9}))

What will it not catch?

A preflight only knows what the descriptors say. Some rules in the docs are not expressed as an enum or range, and you should keep them in your own validator. For ChatGPT Image 2.5 custom sizes, both edges must be multiples of 16, the maximum edge is 3840, the aspect ratio is at most 3:1, and the pixel count must fall between 655,360 and 8,294,400. Reference and mask URLs must be public HTTPS addresses; localhost, private-network and non-HTTPS URLs are rejected before submission.

It also cannot know whether your wallet can cover the call or whether a slow configuration will exceed the 30-second blocking budget and come back as 202 with a job envelope. Check the status code, not the body shape: 200 is the image response and 202 is the job envelope.

How do you use the result in a batch?

Run the preflight once per distinct request shape, not once per row. If a CSV of 500 products all use the same model and parameters, validate the template, then submit each row with its own Idempotency-Key. Cache the catalog response for the length of the run; it changes when models are added or retired, not between two rows of a batch.

When the check fails, surface the message to whoever wrote the request. A line such as n: 9 outside 1-4 is far more useful in a log than a bare 400. The docs also note that per-model ceilings can be lower than the schema maximum for n, which is exactly the kind of limit the descriptor carries.

Is a rejected request billed?

The docs state that image billing is all-or-nothing: a generation is either completed and billed in full, or it fails and is not billed. A request your preflight stops locally never reaches Sume at all, so it costs nothing and does not count against concurrency. See Errors and credits for the status codes worth retrying; an unsupported parameter is not one of them.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume