9:16 story image from Sume, then resize to 1080x1920 with Pillow
The v1 image API does not serve explicit pixel sizes, so ask for 9:16, then cover-crop to 1080x1920 in Pillow. Includes the script, models and prices.

To get a 1080x1920 vertical file from POST /v1/images, request aspect_ratio: "9:16" and resize the result with Pillow. The docs say explicit pixels are not served in v1 (read 2026-10-03), and size is only a shorthand for a resolution tier. Every one of the 19 catalog models lists 9:16, so the choice is about price: Qwen Image and Grok Imagine are $0.025 per image.
What the API takes
On /v1/images you control shape with aspect_ratio and, on some models, a resolution tier (0.5K, 1K, 2K, 4K on the Nano Banana rows; 1K and 2K on Ideogram 4.5 and Imagen 4 Ultra). The exact pixel count comes back with the file, so measure it instead of assuming.
| Model | Price per image | Resolution control |
|---|---|---|
| Qwen Image | $0.025 | None |
| Grok Imagine | $0.025 | None |
| Flux 2 Pro | $0.0375 | None |
| Seedream 4.5 | $0.05 | None |
| Nano Banana 2 | $0.10 at 1K | 0.5K, 1K, 2K, 4K |
| GPT Image 2.5 (estimate, high) | $0.0659 | None |
Cover-crop, don't stretch
Scale the image so it covers 1080x1920, then trim the overflow equally from both sides. A stretch would distort faces and logos. A model that returns a ratio slightly off 9:16 will lose only a few pixels.
import os, requests
from io import BytesIO
from PIL import Image, ImageOps
r = requests.post("https://api.sume.com/v1/images", timeout=120,
headers={"Authorization": f"Bearer {os.environ['SUME_API_KEY']}"},
json={"model": "qwen/qwen-image", "aspect_ratio": "9:16",
"prompt": "poster-style illustration of a lighthouse at dusk, empty sky in the top third"})
if r.status_code != 200:
raise SystemExit(f"{r.status_code}: {r.text[:200]}")
raw = requests.get(r.json()["data"][0]["url"], timeout=60).content
im = Image.open(BytesIO(raw)).convert("RGB")
print("returned", im.size)
ImageOps.fit(im, (1080, 1920), Image.LANCZOS).save("story.jpg", quality=90)
Keep the safe areas yourself
Story apps overlay their own buttons and captions at the top and bottom. Ask for an empty top and bottom third in the prompt, and place text in your own editor.
Upscaling
If a model returns fewer pixels than 1080 wide, the resize enlarges the picture and softens it. Use a higher tier where one exists, such as 2K on Nano Banana 2, which costs $0.15, or render once and enlarge with an upscaler.
Related posts
More in Developers
- ADK 2.11 abort_signal and /run_sse cancel: Sume job keeps billing
Google ADK 2.11.0 can abort a run and cancels /run_sse on disconnect. A Sume job started by that run is not cancelled by it.
- ADK 2.11 tool nodes pause for approval: gate Sume paid tools
Google ADK 2.11.0 tool nodes can pause for user approval via RequestInput. Use it so a Sume paid tool call waits for a person before it spends.
- Agent crashed after submitting: find the Sume runs it started
List a Format's runs, read trigger.idempotency_key, and rebuild which rows already ran after a fresh agent session or crash, without paying for a duplicate.
- Teammate continues a thread: why webhook_delivery.url reads null
A Sume Agent turn reading a teammate's job sees delivery state, but webhook_delivery.url and last_error are null. Who reads which jobs, explained.
Written by Sume