Extend an AI clip by hand: last frame to the next clip, in Python
No extend endpoint? Pull a clip's last frame with Sume's video-frames API and feed it as the first frame of the next job. Python code under 30 lines.

To continue a clip on Sume, take its last frame and start the next job from it. Sume's POST /v1/video-frames returns frame images at timestamps you choose, and POST /v1/videos accepts a first_frame in frame_images on models that list it. Expect some drift in faces and lighting from clip to clip; a longer single generation avoids that when a model allows it.
Steps
Check that your chosen model lists first_frame in supported_frame_images on GET /v1/videos/models.
- Take the completed clip's media.sume.com artifact URL from GET /v1/jobs/{id}/result. The frames endpoint needs a workspace artifact, not the api.sume.com content link.
- POST /v1/video-frames with at set to a time just before the end (duration minus 0.1). Each at value must be at least 0 and less than the duration.
- Poll GET /v1/video-frames/{id} until resource_status is ready, then read frames[0].url.
- Submit the next /v1/videos job with that URL as a first_frame in frame_images.
Code
import asyncio, os, httpx
API = "https://api.sume.com"
H = {"Authorization": f"Bearer {os.environ['SUME_API_KEY']}"}
async def last_frame(c, url, duration):
r = await c.post(f"{API}/v1/video-frames", headers=H,
json={"video_url": url, "at": [duration - 0.1]})
r.raise_for_status()
fid = r.json()["id"]
while True:
s = (await c.get(f"{API}/v1/video-frames/{fid}", headers=H)).json()
if s.get("resource_status") == "ready":
return s["frames"][0]["url"]
await asyncio.sleep(3)
async def main():
async with httpx.AsyncClient(timeout=60) as c:
frame = await last_frame(c, os.environ["CLIP_URL"], float(os.environ["CLIP_SECONDS"]))
body = {"model": os.environ["VIDEO_MODEL"], "prompt": os.environ["PROMPT"],
"frame_images": [{"type": "image_url", "image_url": {"url": frame},
"frame_type": "first_frame"}]}
r = await c.post(f"{API}/v1/videos", headers=H, json=body)
print(r.status_code, r.json().get("id"))
asyncio.run(main())Limits to know
| Item | Limit |
|---|---|
| Timestamps per call | 1 to 24 values in at, or fps up to 2 |
| Source length | Up to 300 seconds |
| Result shape | frames list with t, url, width, height |
Sources
Related posts
More in Developers
- Fabric audio under 10 MB: how many seconds of wav and mp3 fit
Fabric lip-sync takes Sume-hosted audio under 10 MB, 300 s max. Stereo wav hits the size cap near 56 s; 128 kbps mp3 would reach 625 s, so length binds first.
- Fan out four Omni Flash clips in parallel and poll the jobs
Submit four Gemini Omni Flash requests at once on Sume, store each job id, poll status with backoff, then join the clips with Timeline.
- Where to find a voice ID for Sume text to speech: voices_list and voi_
Sume TTS wants a voice UUID or a voi_ library id, never a voice name like alloy. Where to copy it from, and the error you get for a name.
- Find dead air in a Reel: sentence segments and silence split
Sume video inspect splits a transcript into sentence segments at pauses of 0.2 to 3 s. Use the gaps between segments to find dead air before you trim.
Written by Sume