Preview a HyperFrames composition as PNG stills with the Sume API
POST /v1/hyperframes-previews returns 1-6 PNG stills of a composition without rendering an MP4. Request shape, polling, and what the frames are not.

To look at a HyperFrames composition before you pay for a render, call POST https://api.sume.com/v1/hyperframes-previews with the composition and an optional at list of 1 to 6 times. Sume captures PNG stills, no MP4 is produced and no encode runs, and you read the pictures from GET /v1/hyperframes-previews/{id} once the job completes.
Everything below comes from the live Sume OpenAPI document (read 2026-10-10) and the docs pages on jobs and results and errors and rate limits. The route has no standalone docs page yet, so the schema is the source of truth.
What does the request body need?
The body is strict: unknown keys are rejected. The only required key is hyperframes_composition, and it describes the same document a render would use, so a preview cannot show something the final bake would not.
Optional keys are at (the times to capture), check (covered in a separate post), and the usual delivery keys mode, webhook_url and wait_timeout_seconds.
| Field | Rule | Notes |
|---|---|---|
| html | string, at least 32 characters | The composition markup. |
| clips | 1 to 200 items | Each has id (max 64), source_url (a URI) and relative_path matching assets/ or fonts/ plus a plain file name. |
| width, height | integers 16 to 4096 | Pixels. |
| duration_seconds | above 0, at most 60 | Seconds. |
| at | 1 to 6 numbers, each 0 or more | Omit it and Sume samples 10%, 50% and 97% of the clamped duration. |
| source_tool, html_sha256, fit_duration_to_media | optional | Metadata and a duration option. |
How do I submit and read the stills?
The submit call answers 202 with the usual job envelope: request_id, hyperframes_preview_id, status_url, result_url, events_url, cancel_url, next_poll_after_seconds and terminal. Store the id, then poll. The resource read returns status, resource_status, frames and error.
Each entry in frames carries t, url, width and height. The PNGs are durable media.sume.com artifacts stamped with the stage preview. That stamp matters: they are pictures of a composition that has not been rendered, not a deliverable.
The Python sample uses only the standard library. It sends the key as x-api-key, which is the single credential header the API accepts per request.
import json, os, time, urllib.request
def call(method, path, body=None):
req = urllib.request.Request(
"https://api.sume.com" + path, method=method,
data=json.dumps(body).encode() if body else None,
headers={"x-api-key": os.environ["SUME_API_KEY"], "User-Agent": "sume-example/1.0",
"Content-Type": "application/json"})
with urllib.request.urlopen(req, timeout=30) as r:
return json.load(r)["data"]
comp = {"html": "<div id='root' data-composition-id='main'></div>",
"clips": [{"id": "clip1", "source_url": os.environ["CLIP_URL"],
"relative_path": "assets/clip1.mp4"}],
"width": 1080, "height": 1920, "duration_seconds": 12}
sub = call("POST", "/v1/hyperframes-previews",
{"hyperframes_composition": comp, "at": [0.5, 6, 11]})
pid = sub["hyperframes_preview_id"]
while True:
prev = call("GET", f"/v1/hyperframes-previews/{pid}")
if prev["status"] in ("completed", "failed", "canceled"):
break
time.sleep(3)
for f in prev["frames"] or []:
print(f["t"], f["url"])Which defaults and limits should I plan around?
at is capped at six, so a preview is a spot check, not a contact sheet of the whole timeline. When you omit it, the last sample sits at 97% of the clamped duration and is held inside the timeline rather than on the frame after it.
The published OpenAPI lists no Idempotency-Key parameter on this route, unlike most generation routes. Treat a lost response as a reason to look at your own stored id, not as a reason to submit again, and use the job list to find the earlier job if you must.
Delivery works like other jobs: async is the default, sync and subscribe wait at most 30 seconds and then hand you the job to poll, and webhook sends terminal events only.
- A failed preview is a normal terminal state: read
erroron the resource. - The preview is metered compute rather than a fixed-price model call, so read the amount from your usage ledger rather than assuming a number.
How does this compare with the other preview routes?
Sume has several read-only media routes that look similar from a distance. Video frames pulls stills out of a video that already exists. Reference ingest analyses a reference clip. The HyperFrames preview is the one that works on a composition you wrote, before any video exists.
That is why the request carries the clips and fonts list: Sume stages exactly what a bake would stage, boots Chromium once, and screenshots the requested times. Because it boots once for all requested times, one call with several at values is the natural shape, not a loop of single-time calls.
A practical loop for a templated ad pipeline is to preview at the first frame, the midpoint and the last second of the cut, compare the three stills against a brand checklist, and render only when they pass. The preview is the cheaper place to find a bad crop, because nothing is encoded.
When is a still the wrong tool?
Stills tell you about layout, type and cropping at a few moments. They do not prove motion timing, audio sync or encode quality, because nothing is encoded. If you need those, render.
If your question is about collisions with a caption band or contrast, use the check mode instead of reading pixels by eye; it returns a structured report.
Sources
Related posts
More in Developers
- Edit several Format files in one commit with a change set
One PUT to the Sume Format contents root commits many files at once. Learn what a change set keeps, why it cannot delete, and how If-Match guards the package.
- Python 3.14 uuid.uuid7() as a Sume Idempotency-Key: when it is safe
uuid.uuid7() is new in Python 3.14 and makes a tidy Idempotency-Key for Sume submits, if you generate it once per intent and store it. A runnable stdlib sample.
- Python urllib gets 403 'error code: 1010' from api.sume.com: set a UA
Python's default urllib User-Agent got a plain-text HTTP 403 from api.sume.com in my test while other clients passed. Add a User-Agent and parse errors safely.
- Recover Sume jobs after a crash: match your key to GET /v1/jobs
Your worker died after submit and lost the job ids. Page GET /v1/jobs, match idempotency_key to your own keys, stop at the last page. A 29-line sample.
Written by Sume