Email header image 600x200: render 1920x640 at exactly 3:1
A 600x200 email header is 3:1, the widest shape GPT Image 2.5 accepts. Render 1920x640, then export 1200x400 for retina and 600x200 for 1x with Pillow.

A 600x200 email header is exactly 3:1, and 3:1 is the widest shape GPT Image 2.5 allows, so you can render it without a crop. Ask for 1920x640 and export two files: 1200x400 for high-density screens and 600x200 for the 1x slot.
This shape is right on the limit. The OpenAI guide puts custom aspect ratios between 1:3 and 3:1, and Sume's catalog rejects anything past it on this model, so 3:1 passes and 3.1:1 would not.
Checks
Divide each edge by 16 and the sizes come out whole numbers, 120 and 40. The area is 1,228,800 pixels, well inside the range.
| Check | Value | Result |
|---|---|---|
| Both edges multiples of 16 | 1920 = 16 x 120, 640 = 16 x 40 | pass |
| Longest edge at most 3840 | 1920 | pass |
| Aspect ratio at most 3:1 | 3.000:1 | pass |
| Pixels 655,360 to 8,294,400 | 1,228,800 | pass |
One render, two exports
The script renders once and writes both sizes. Because the shapes match, ImageOps.fit does not crop; it only scales.
import os, requests
H = {"Authorization": f"Bearer {os.environ['SUME_API_KEY']}"}
def generate(body):
r = requests.post("https://api.sume.com/v1/images", headers=H, json=body, timeout=60)
if r.status_code != 200: # 202 = still running, read data.status_url
raise SystemExit(f"{r.status_code}: {r.text[:300]}")
return r.json()["data"][0]["url"]
from io import BytesIO
from PIL import Image, ImageOps
url = generate({
"model": "openai/gpt-image-2.5",
"prompt": "Soft watercolor header: autumn leaves along the bottom edge, pale cream sky, empty space in the middle for a title",
"image_size": {"width": 1920, "height": 640},
"quality": "medium",
})
master = Image.open(BytesIO(requests.get(url, timeout=60).content)).convert("RGB")
for name, size in {"header@2x.png": (1200, 400), "header.png": (600, 200)}.items():
ImageOps.fit(master, size, Image.LANCZOS).save(name)Price
Output-only figures from the Flare token rate are below. Sume bills list times 1.25. A header is simple art, so medium is often enough, and the Sume default of high costs roughly four times as much at this size.
| Quality | Provider list | Sume at list x 1.25 |
|---|---|---|
| low | $0.0019 | $0.0024 |
| medium | $0.0046 | $0.0058 |
| high | $0.0186 | $0.0232 |
Design for the small file
A 600-pixel header is read at a glance in an inbox preview. Use big shapes, one focal point and a quiet center, then put the title in as live text in your email template, not as pixels in the image. That keeps it readable if images are blocked and lets you change the wording without paying for a new render.
Draft cheap, finish once
A display set usually needs several concepts before one is approved. Request the first round at quality: "low", pick the layout, then repeat only the winner at high. Quality is a per-request field on both ChatGPT Image 2.5 variants (openai/gpt-image-2.5 and openai/gpt-image-2.5-sunburst), and the accepted values are auto, low, medium, high, xhigh and max. Avoid auto: Sume reserves the max price for it, so set the tier yourself.
Flare is the faster variant and Sunburst is tuned for edit precision, according to the fal pages. Both share the same token rates, so for an ad pass you can pick on behavior, not on price. Auto routing on Sume (sume/auto) uses Flare.
If the call returns 202 instead of 200
POST /v1/images waits up to 30 seconds and answers 200 with the images when the job finishes in time. When it does not, the route answers 202 with the standard job envelope, and the images come from GET /v1/jobs/{id}/result. Slow settings such as 4K, high quality and large n are the likeliest to fall back. A 1-2 megapixel ad master at high quality usually stays in the wait budget, but the code should branch on the status code, not the body shape.
For a batch of ad sizes, send each request with mode: "async" and read the results afterwards, or add a webhook_url with mode: "webhook". A failed synchronous job returns 502 with an error code and a next_action, and Sume does not bill failed generations.
Sources
Related posts
More in Use cases
- Employee handbook page to a 20 s training clip: one policy, one scene
Turn one handbook page into a Wan 3.0 training clip on Sume: pick the policy, picture the behavior, type the rule as captions, and review it with HR before use.
- Employee onboarding videos: ten 30-second Wan 3.0 modules for $37.50
Ten 30 second onboarding clips on Sume's Wan 3.0 cost $37.50 at 720p, $18.75 at 480p or $75.00 at 1080p. Concurrency by plan and what to keep out of frames.
- English podcast clip to a Spanish short: dub and captions, $0.38
An English podcast clip turned into a Spanish short costs up to $0.38 on Sume: trim, detach, transcribe, speak, render and one caption job at $0.20.
- Engraved and monogrammed gifts: one order-by cue per lead time
Personalised gifts need earlier cutoffs than stock items. Compute each order-by date from production plus transit days and burn all lines in one caption job.
Written by Sume