One request, many models: API fan-out
Pass several model names in one generate call, get a request id per model, and run a bake-off without writing your own dispatch loop.
The generate image and video endpoints accept model as a string or as an array of names. One POST starts one job per named model, credits are taken for the whole set up front, and the response lists each model with its own request id. You poll those ids separately. That is the bake-off primitive: the same prompt, the same aspect ratio, N models, no dispatch loop of your own.
The studio UI does the other version of this: one HTTP call per selected model. You can do that too. You do not have to. The array form is the one that removes the loop.
The request
POST /api/v1/generate/image and POST /api/v1/generate/video both take model as string | string[]. A single name and a one-element array are the same job. Two or more names are a fan-out.
curl -s -X POST https://api.versely.studio/api/v1/generate/image \
-H "Authorization: Bearer $VERSELY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": ["Flux Pro Ultra", "Imagen 4"],
"prompt": "Matte black dropper bottle, 45-degree key light, seamless grey, pack shot, 1:1",
"aspect_ratio": "1:1"
}'
Rules that bite on the first real bake-off:
- Same modality. This is an image endpoint or a video endpoint, not a mixed bag. An edit model in an image array still needs at least one input image; a first-last-frame model still needs two. The server validates the whole array before dispatch and returns
400withinvalidModelsif any name requires inputs you did not send. - Names, not slugs. Send the catalog
name(Flux Pro Ultra), not a URL slug. Aliases are canonicalised server-side, but a slug that only exists on a marketing page is not a name. num_imagesmultiplies. On image generate, the server builds one task per model per image count.num_images: 2with three models is six jobs and six charges, not three. For a bake-off, leave it at 1.- One prompt. There is a single
prompt(and a single aspect ratio, duration, resolution). That is the point of a controlled comparison. If you need a per-model prompt, you are back to one POST per model.
Auth binds the user from the key. Omit user_id; a value that does not match the key is rejected.
Video is the same shape on POST /api/v1/generate/video, with duration, aspect_ratio, and resolution applied to every model in the array. Pick values every name in the array actually accepts; do not assume a duration one model cannot render will be snapped for the others.
What comes back
Treat the HTTP 200 as "accepted," not "done." Generation is async. The body is a list, not a single URL.
Two shapes show up in the wild, because different providers serialise the fan-out differently. Parse both.
List of tasks (the KIE image/video controllers):
{
"message": "Image generation started for multiple models",
"data": [
{ "model": "Flux Pro Ultra", "taskId": "abc-111" },
{ "model": "Imagen 4", "taskId": "abc-222" }
]
}
An entry may carry error instead of taskId if that one model failed to start. The others can still be in flight. Credits for the failed names are refunded at that model's price; the rest stay charged.
Successful / failed buckets (the documented GenerationResponse):
{
"success": true,
"message": "Generation completed. 1 succeeded, 0 failed.",
"data": {
"successful": [
{
"success": true,
"model": "Flux Pro Ultra",
"data": { "request_id": "abc-111" }
}
],
"failed": [],
"total": 1
}
}
Walk every taskId / request_id / requestId you can find. One HTTP call, N ids, N rows in your job table. Poll GET /api/v1/status/:requestId per id. Do not wait for the first to finish before you start the others; they are already running.
A 402 on the POST means the sum of the array did not fit the balance. Nothing starts. There is no free allowance to fall back on. Price the set first, or shrink the array. The credit estimate path exists for this; so does checking credits in the agent.
A bake-off you can defend
Fan-out removes the dispatch loop. It does not remove the need to decide what you are measuring. Running four video models on one prompt and picking a favourite is a mood. Running four video models on a frozen prompt suite, scoring the same axes, and keeping the winner is a test.
A working setup:
- Freeze the prompt (and the negative, if you use one). One sentence of change between runs is a different test. Prompt adherence is an axis, not a vibe.
- Freeze aspect ratio, duration, and resolution. Fan-out applies the same values to every model; that is a feature. If a model cannot honour
1080p, it should lose on that run, not get a secret 720p. - Confirm every name with a provider-check (same type you are about to POST) so a retired or misspelled name fails the preflight instead of one slot in the array.
- POST once with the array. Store every request id against the model name.
- Poll to completion. Score with a written rubric, not a glance. The five-axis video rubric is built for this; the twenty-prompt suite is the input side of the same discipline.
Keep the array small. Three models is a comparison. Twelve is a bill. The text-to-image tool and the AI video generator are the right place to explore a candidate before you put it in a charged array.
The agent surface can also name several models in one generate_images / generate_videos call (models, plural). That path cycles names across num_images rather than guaranteeing one result per model. For a bake-off you will actually score, use the REST array and num_images: 1.
What fan-out will not do for you
It will not pick the provider. The server decides the provider chain from its own priority list. A provider field on the body is discarded. If you care which vendor ran the job, that is not a lever you have; you care which model name you sent.
It will not make seeds portable. Same prompt, same seed, different model is a different picture, and the same model on a different provider is not a reproducibility guarantee either. If the test is "which model follows this brief," do not also pretend you are testing seed stability.
It will not let you mix an image model and a video model in one call. Use two POSTs.
It will not skip moderation. The prompt is still a write-request text field. A structurally valid array can 400 with Content policy violation before any model is contacted. Fix the prompt; do not retry the same body.
It will not give you a webhook when the N jobs finish. Poll each id. If you do not persist those ids, you have N charges and nothing to collect.
FAQ
Can I send three video models and one image model in the same POST?
No. /generate/image and /generate/video are different endpoints. Split the bake-off by modality. Inside one endpoint, every name has to be valid for that type; provider-check with type=image or type=video is the cheap way to prove it.
If one model in the array fails to start, do the others still run?
Yes, on the list-of-tasks path. You will see error on the failed entry and taskId on the rest. Credits for the failed names are refunded; the rest stay in flight. Poll the ids you got, do not assume the whole POST rolled back.
Is this cheaper than N separate POSTs?
You pay for every model that actually starts, either way. The saving is engineering: one round trip to dispatch, one place that applied the prompt, one batch_id if you send one. It is not a discount on the catalog.
Why would I still send one POST per model?
When you need a per-model duration, a per-model reference image, or a prompt variant. Fan-out is for a controlled comparison under identical inputs. The moment the inputs diverge, the array is lying about what it compared. Send separate calls, and put a batch_id on each if you want retries of that one call absorbed for five minutes.