TypeScript: check 8:1 against /v1/images/models before you POST
A 20-line TypeScript guard that reads the aspect_ratio values for a Sume image model and refuses a ratio it does not list, instead of a 400 at request time.

Sume rejects an image request that sets a parameter the model does not list with 400 unsupported_parameter, and Nano Banana Pro and sume/auto do not list 8:1. A TypeScript guard that reads supported_parameters.aspect_ratio.values first turns a mid-batch 400 into an immediate, readable error before any generation starts.
The guard
It fetches the catalog, finds the model row, and compares. In production, cache the list for a while instead of fetching it on every call.
const BASE = "https://api.sume.com";
const auth = { Authorization: `Bearer ${process.env.SUME_API_KEY}` };
async function listed(model: string): Promise<string[]> {
const r = await fetch(`${BASE}/v1/images/models`, { headers: auth });
if (!r.ok) throw new Error(`models ${r.status}`);
const { data } = await r.json();
const row = data.find((m: { id: string }) => m.id === model);
return row?.supported_parameters?.aspect_ratio?.values ?? [];
}
export async function generate(model: string, ratio: string, prompt: string) {
const ok = await listed(model);
if (!ok.includes(ratio)) {
throw new Error(`${model} does not list ${ratio}; it lists ${ok.join(", ")}`);
}
const r = await fetch(`${BASE}/v1/images`, {
method: "POST",
headers: { ...auth, "Content-Type": "application/json" },
body: JSON.stringify({ model, prompt, aspect_ratio: ratio }),
});
return { status: r.status, body: await r.json() };
}What it returns for common pairs
| Model | Ratio | Guard result |
|---|---|---|
| google/nano-banana-2.1 | 8:1 | Pass |
| google/nano-banana-pro | 8:1 | Throws, ratio not listed |
| google/nano-banana-2.1 | 4:5 | Pass |
| openai/gpt-image-2.5 | 4:1 | Throws, ratio not listed |
Gotchas
sume/autois not in the catalog list, so the guard returns an empty list for it. Skip the guard for Auto or special-case it.- The guard checks the ratio only.
resolution,quality, andnhave their own descriptors on the same row. - A
200carries the image andusage.cost; a202carries the job envelope. Branch onstatus.
Sources
Related posts
More in Developers
- usage.cost on a Sume video poll is nullable: guard it before you sum
The /v1/videos poll schema allows usage.cost to be null. Sum only completed jobs, keep unknowns apart in a ledger, and compare to the Wan 3.0 $3.75 estimate.
- Validate a cut list against video-trim's rules before you submit
Video trim has no unbilled check endpoint, so mirror its rules locally: 1800 s source, 0.2 s to 900 s output, one of end or duration, exact with output.
- VEED Fabric audio must be under 10 MB: stereo wav tops out near 56 s
Sume lists VEED Fabric 1.0 audio as Sume-hosted, under 10 MB, at most 300 seconds. A 44.1 kHz stereo wav hits 10 MB near 56 seconds; a 128 kbps mp3 never does.
- Fabric request: image_url and avatar_handle together are not allowed
Sume's VEED Fabric 1.0 body needs exactly one visual source: image_url or avatar_id / avatar_handle, never two. Valid bodies and audio rules.
Written by Sume