One product photo, five ad shapes: Flux 2 Pro $0.1875, Qwen $0.125
Five shapes (1:1, 4:5, 9:16, 16:9, 21:9) from one photo: $0.1875 on Flux 2 Pro, $0.125 on Qwen Image, $0.50 on Nano Banana 2.1. Seedream 5 Lite lacks 21:9.

Five ad shapes from one product photo (1:1, 4:5, 9:16, 16:9 and 21:9) cost $0.1875 on Flux 2 Pro, $0.125 on Qwen Image and $0.50 on Nano Banana 2.1 on Sume, one call per shape. Seedream 5.0 Lite lists the first four shapes but not 21:9, so it covers four of the five for $0.175. The prices are the billed endpoint figures from origin/main, read 2026-10-08.
This is regeneration, not cropping. Each call asks the model to stage the product again for the new frame, so the composition, background and sometimes small label details change from shape to shape. Check each result against the original before it goes into an ad.
Cost by row
Each row below takes up to 10 reference images. The count is the number of the five shapes the row lists.
| Model | Per image | Shapes listed (of 5) | Cost for 5 |
|---|---|---|---|
| Qwen Image | $0.025 | 5 | $0.125 |
| Flux 2 Pro | $0.0375 | 5 | $0.1875 |
| Seedream 5.0 Lite | $0.04375 | 4 (no 21:9) | $0.175 for 4 |
| Nano Banana 2.1 | $0.10 | 5 | $0.50 |
The loop
Each call passes the same reference and names its own aspect_ratio. Leaving aspect_ratio off is not the same as auto, and a shape you want should be named. The script prints the status and moves on if a call does not return 200; a 202 means the job is still running and its result is fetched from the job.
import os, requests
PHOTO = "https://example.com/tin.jpg"
RATIOS = ["1:1", "4:5", "9:16", "16:9", "21:9"]
headers = {"Authorization": f"Bearer {os.environ['SUME_API_KEY']}"}
total = 0.0
for ratio in RATIOS:
body = {
"model": "black-forest-labs/flux.2-pro",
"prompt": "Same tin, same label, restaged for this frame shape",
"input_references": [{"type": "image_url", "image_url": {"url": PHOTO}}],
"aspect_ratio": ratio,
}
r = requests.post("https://api.sume.com/v1/images", json=body,
headers=headers, timeout=60)
if r.status_code != 200:
print(ratio, "status", r.status_code)
continue
out = r.json()
total += out["usage"]["cost"]
print(ratio, out["data"][0]["url"])
print(f"total ${total:.4f}")
Keeping the set consistent
Same wording across all five prompts, with only the framing instruction changing, gives the most similar set. Tall 9:16 and wide 21:9 frames are the shapes where the product is most likely to shrink or move, so say in the prompt where it sits and roughly how large.
A cheaper first pass on Qwen Image costs $0.125 for the set. If the results hold up for your product, you do not need the dearer row.
Run the five calls one after another while you are testing, then switch to a small number in parallel once the prompt is stable. Each call still waits up to 30 seconds before Sume hands back a job envelope, so a timeout of 60 seconds in the client is enough to see either outcome.
Keep the original photo URL public for the duration of the batch. Sume fetches reference URLs from the public internet and refuses localhost, private-network and non-HTTPS addresses before it submits the work.
Sources
Related posts
More in Use cases
- Photographer portfolio reel from stills with Omni Flash
Turn 10 portfolio photos into a 60-second reel with Gemini Omni Flash: six seconds each, $7.50 at 720p on Sume, with 360p drafts first at $2.25.
- Pixel art and isometric video with Omni Flash: prompts
Prompt wording for pixel-art and isometric looks in Gemini Omni Flash, what Google's page says the model cannot control, and the cost of a draft on Sume.
- Podcast clip pipeline: $0.24 per 60-second captioned clip
Cut a captioned clip from a long recording on Sume: detach audio, transcribe, trim the video, caption it. Four API calls and $0.24 per clip, itemised.
- Podcast teaser video: Omni cover-art clip plus your audio
Omni Flash makes its own sound and takes no audio upload. How to make a 9:16 podcast teaser on Sume: animate cover art for $1.00, then bring in your own audio.
Written by Sume