Jupyter contact sheet: one prompt across four Sume image models

A notebook cell that sends one prompt to four Sume image models, tiles the results into a labeled contact sheet with Pillow, and prints each billed cost.

5 min readSume
All posts

To compare image models fairly, send the same prompt, the same aspect_ratio and n: 1 to each model on Sume's POST /v1/images, download each result, paste them into one labeled Pillow sheet, and print each usage.cost beside it. One notebook cell does it, and the sheet is what you show a teammate when the question is which model to pin.

Keep the comparison honest by changing nothing but model. Different models accept different parameters, so the cell sends only model, prompt, n and aspect_ratio, and a model that rejects one of them returns a 400 you can see in the output.

Which four models make a useful first sheet?

Ids are the public org/slug ids from the Sume image page; list prices are the catalog list rate before Sume's 1.25 multiplier.

A four-model comparison set, list prices read 2026-10-04
Model idCatalog list price per imageWhy include it
google/nano-banana-2$0.08 at the 1K defaultFast general model
bytedance-seed/seedream-5-lite$0.035Low-cost Seedream
ideogram/ideogram-v4.5$0.06 at the default medium qualityText-heavy layouts
openai/gpt-image-2.5Token-based; high at 1024 square is the listed rateInstruction following and edits

What is the notebook cell?

Run it once; it prints the status of any model that fails so one error does not blank the whole sheet.

import io, os, requests
from PIL import Image, ImageDraw

H = {"Authorization": f"Bearer {os.environ['SUME_API_KEY']}"}
MODELS = ["google/nano-banana-2", "bytedance-seed/seedream-5-lite",
          "ideogram/ideogram-v4.5", "openai/gpt-image-2.5"]
PROMPT = "a ceramic teapot on a wooden table, soft window light, product photo"
tiles = []
for m in MODELS:
    r = requests.post("https://api.sume.com/v1/images", headers=H, timeout=90,
                      json={"model": m, "prompt": PROMPT, "n": 1,
                            "aspect_ratio": "1:1"})
    if r.status_code != 200:
        print(m, r.status_code, r.text[:120]); continue
    b = r.json()
    im = Image.open(io.BytesIO(requests.get(b["data"][0]["url"]).content))
    im = im.convert("RGB").resize((384, 384))
    ImageDraw.Draw(im).text((8, 8), f"{m}  ${b['usage']['cost']}", fill="white")
    tiles.append(im)
sheet = Image.new("RGB", (384 * len(tiles), 384))
for i, t in enumerate(tiles):
    sheet.paste(t, (i * 384, 0))
sheet

How do I read the sheet?

Judge on the property you care about and write it down before you look: text spelled correctly, product shape kept, lighting plausible. Run the same cell three times with different prompts, because a single sample says little. Cost is on the tile, so a cheaper model that passes your check wins automatically.

Ratios matter. 1:1 is accepted widely, but the catalog is per-model, and a ratio a model does not list returns 400. Read each model's aspect_ratio descriptor from GET /v1/images/models before adding a ratio such as 4:5.

What does this cost to run?

At the list prices above times 1.25, one pass of the four models is a few tens of cents, with the ChatGPT Image 2.5 tile depending on quality and size. Failed generations are not billed, and a completed one is billed in full, so re-running a cell re-bills every tile that succeeds.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume