App Store screenshots reject alpha: flatten a Sume PNG first
App Store Connect screenshots cannot include alpha or transparency. A PNG with an alpha channel from an image API needs flattening; here is a Pillow check.

Apple's screenshot specification says images cannot include alpha channels or transparencies, and accepts .jpeg, .jpg and .png with 1 to 10 screenshots per set. A PNG straight from an image API may carry an alpha channel even when nothing in it is transparent, so flatten it onto a solid color before upload.
What Apple lists for the 6.9-inch iPhone display
The page lists three accepted sizes for each orientation.
| Orientation | Accepted sizes in pixels |
|---|---|
| Portrait | 1260 x 2736, 1290 x 2796, 1320 x 2868 |
| Landscape | 2736 x 1260, 2796 x 1290, 2868 x 1320 |
Why alpha sneaks in
Image APIs that return PNG often write RGBA files. The Sume Image API accepts output_format of png, jpeg, webp or svg, so asking for jpeg avoids alpha entirely, and the docs mark background as a ChatGPT Image 2.5 option you do not need here. A background or marketing frame generated as PNG, or any cut-out you composited yourself, can still hold an alpha channel, so test the final file rather than trusting the source.
Flatten and verify
The script requests a 9:16-ish backdrop as JPEG, resizes to the 1290 x 2796 accepted portrait size, and refuses to save if the mode is not RGB. For a PNG you composited, replace the open step with Image.open(path) and paste onto a solid RGB canvas using the alpha as the mask.
import os, io, requests
from PIL import Image
r = requests.post(
"https://api.sume.com/v1/images",
headers={"Authorization": f"Bearer {os.environ['SUME_API_KEY']}"},
json={"model": "bytedance-seed/seedream-4.5", "aspect_ratio": "9:16", "prompt": "clean pastel gradient backdrop, no text", "output_format": "jpeg"},
timeout=90,
)
r.raise_for_status()
if r.status_code != 200:
raise SystemExit("202: read the finished job from /v1/jobs/{id}/result")
img = Image.open(io.BytesIO(requests.get(r.json()["data"][0]["url"], timeout=60).content))
if img.mode in ("RGBA", "LA", "P"):
rgba = img.convert("RGBA")
flat = Image.new("RGB", rgba.size, (255, 255, 255))
flat.paste(rgba, mask=rgba.getchannel("A"))
img = flat
img = img.convert("RGB").resize((1290, 2796), Image.LANCZOS)
assert img.mode == "RGB"
img.save("appstore-shot-bg.png", optimize=True)A check you can run on every file
The cheapest guard is to open every file you are about to upload and assert on its mode and size. Mode RGB means three channels; RGBA, LA and palette images with transparency can all carry alpha. Check the pixel size against the accepted list from Apple's page, since a file that is one pixel off is rejected the same as one that is far off. Do the check after the last edit, not after generation, because every later composite or crop can reintroduce an alpha channel. Apple also says 1 to 10 screenshots per set, so a script that writes more than ten files for one device size is a bug worth catching before upload. Keep the unflattened source images in a separate folder, so a change of background color means re-running the flatten step rather than regenerating anything and paying for it again.
Aspect note
1290 x 2796 has a width-to-height ratio of 0.461, narrower than 9:16 (0.5625), so a plain resize squeezes the image horizontally by about 18%. Crop the generated image to the target ratio first if the content has faces or a device frame. The backdrop in this example has neither, so a resize is enough. Whatever the file, the real app capture should sit on top of it unmodified.
Sources
Related posts
More in Developers
- Are Claude Code mods safe with a Sume API key in your env?
Claude Code mods run unsandboxed and can read env vars and settings files. What that means for a Sume API key, the CLI config file and an OAuth session.
- arun_ run ids: Format run or Action run? Store the family
A Sume arun_ id shows up under both /v1/format-runs and /v1/action-runs, and waitForRun requires a family. Store the surface next to the id and route reads.
- Astro API route for Sume webhooks: export const prerender = false
An Astro endpoint can receive Sume job webhooks if it is rendered on demand. Set prerender false, read the raw body, and verify the sume-v1 signature.
- Async, webhook or sync: pick a Sume communication mode by job length
Use sync only for jobs that finish inside its 30 second cap, such as many images. Use async plus polling or webhook for video, and keep polling as the backup.
Written by Sume