Video analyses keyframes: one still per second per scene
A legacy Sume video analysis gives a still for each whole second of a scene plus a keyframe_url near 40%. How stills fail and how to pick your own cover.

Every scene in a legacy Sume video analysis carries keyframes, an array of { t, url } stills covering every whole second of that scene, and a keyframe_url, the representative pick. The pick is the 1-second sample nearest 40% of the scene, or the historical 40% pick on scenes shorter than one second. If you want a thumbnail candidate per scene, keyframe_url is already chosen; if you want a better one, the per-second list is where you look.
Thumbnails matter more for series. YouTube's September 23 post lists custom thumbnails among the Shorts series features (YouTube Blog, read 2026-10-04), so each episode needs its own cover.
What the docs promise, and the ceiling
From the video analyses docs: ffmpeg extracts at least one JPEG still per second of each scene, so the stills ceiling is the 300-second hard duration cap, about 300 JPEGs. A failed second comes back with url: null and a warning keyframe_mirror_failed:scene_N:t_T, and does not drop the scene. If the representative still fails, keyframe_url is null with keyframe_mirror_failed:scene_N.
| Field | What it is | Failure shape |
|---|---|---|
| keyframe_url | Representative still, nearest 40% of the scene | null plus keyframe_mirror_failed:scene_N |
| keyframes[] | One {t, url} per whole second of the scene | url null plus keyframe_mirror_failed:scene_N:t_T |
| Hard cap | 300 seconds of source, about 300 stills | Longer sources are rejected after probe |
Choose a cover yourself
Where keyframe_url is null or just not right, take the nearest working per-second still to the point you want, skipping failed seconds.
scene = {
"start_seconds": 10,
"end_seconds": 20,
"keyframe_url": None,
"keyframes": [
{"t": 10, "url": "https://media.sume.com/artifacts/a/10.jpg"},
{"t": 11, "url": None},
{"t": 12, "url": "https://media.sume.com/artifacts/a/12.jpg"},
{"t": 14, "url": "https://media.sume.com/artifacts/a/14.jpg"},
],
}
span = scene["end_seconds"] - scene["start_seconds"]
target = scene["start_seconds"] + 0.4 * span
usable = [k for k in scene["keyframes"] if k["url"]]
best = min(usable, key=lambda k: abs(k["t"] - target))
print("target", target, "->", best["t"], best["url"])
This resource is legacy: dest answers 410 video_analysis_retired on create and production accepts it until a later change. For new work take stills from video frames (exact times, source size) or video inspect. The thumbnail path is covered in pick a thumbnail frame.
Sources
Related posts
More in Media tools
- Video filter /check: submit or fix and recheck
POST /v1/video-filter/check is unbilled and returns valid, diagnostics and a next_action of submit_video_filter or fix_program_and_recheck. Branch on it.
- Video inspect fast seek: requested_times vs sample_times
A fast-seek inspect grid returns requested_times and sample_times. Use sample_times for what the tiles show, then trim from them, never from the request.
- WCAG 1.4.4 resize text: do burned-in captions need it?
WCAG 1.4.4 exempts captions and images of text, so burned-in captions need no resize. You still choose their size: use design.typography on Sume.
- WebP with transparency: output and import rules
Synthesia's editor now accepts WebP with transparency kept. On Sume, set output_format and background explicitly and check the result for alpha.
Written by Sume