PNG, JPEG or WebP for ad stills: measure file size with Pillow
Sume's Image API has no output_compression field. Request png, jpeg or webp, then re-encode locally and compare sizes before you upload.

Request png, jpeg or webp with output_format, and compress locally if a platform wants smaller files, because output_compression is in the schema but no model serves it. Per the Image API docs, sending it returns 400 unsupported_parameter. So your quality dial lives in Pillow, not in the request.
The script below takes one downloaded PNG and prints how big the same picture is as JPEG and WebP at a few quality levels, so you pick a setting from your own images instead of a rule of thumb.
What does the API give me for format?
The output_format field lists png, jpeg, webp or svg, and the response labels each file with media_type, for example image/webp. A model only accepts what its catalog descriptor lists, so read supported_parameters first. A parameter that is not listed is rejected, not ignored.
How do I compare sizes?
Save one result as a PNG, then re-encode it in memory. Photos usually shrink a lot as JPEG or WebP; flat graphics with sharp text may not, so test both kinds.
import io
from PIL import Image
def sizes(path):
img = Image.open(path).convert("RGB")
out = {"png": len(open(path, "rb").read())}
for q in (95, 85, 75):
for fmt in ("JPEG", "WEBP"):
buf = io.BytesIO()
img.save(buf, fmt, quality=q)
out[f"{fmt.lower()}-q{q}"] = buf.tell()
return out
# Demo file; replace with a downloaded ad still.
Image.effect_noise((600, 600), 30).convert("RGB").save("demo.png")
for name, n in sizes("demo.png").items():
print(f"{name:10} {n / 1024:8.1f} KiB")Which format should I ship?
Keep the PNG as the master, since each re-encode of a JPEG or WebP loses detail, and publish a copy in whichever format meets the upload limit. Open the smallest copy at full size and check text edges before you accept it.
What should I record?
Store the master URL, the format you shipped and the quality number next to the job id, so a later re-export starts from the master and not from the compressed copy.
Sources
Related posts
More in Developers
- A policy snapshot file and a Python check before each AI ad batch
Keep platform AI rules in a JSON file with source and read date, and run a short Python check that flags rules older than 30 days before you queue a batch.
- Poll a Sume bulk-run queue in Python and list items not completed
A short asyncio script that polls GET /v1/format-run-queues/{id} until the queue is completed, prints the counts and lists every item that did not complete.
- Dedupe Sume webhooks with Postgres ON CONFLICT DO NOTHING
Insert job_id or request_id with ON CONFLICT DO NOTHING RETURNING. A returned row means first delivery; no row means a duplicate. Includes the SQL.
- Preflight a video request against the catalog before you pay
Check duration, resolution, aspect ratio, frame types and reference types against a GET /v1/videos/models entry in Python, and catch the 400 fields up front.
Written by Sume