Vidu 24-hour and FLUX 3 signed result links: copy the file first
Vidu result URLs last 24 hours and FLUX 3 signed URLs about 2 hours, or about 10 minutes by another line. Stream the Sume clip to disk and keep the job id.

Treat every result link from a generation API as short-lived: stream the file into your own storage as soon as the job is complete, and store the job id so you can read the record again. On Sume, a completed /v1/videos job gives you unsigned_urls, and the content endpoint takes your API key as a Bearer header.
The two vendors in this week's launches state different lifetimes, and the FLUX 3 page states two of them.
How long do the vendor links live?
These are the figures printed on the vendors' own pages. The FLUX 3 page gives both numbers in different places, so plan for the shorter one.
| Vendor | Stated lifetime | Page |
|---|---|---|
| Vidu task detail | Result URLs valid for 24 hours (url, cover, watermarked) | platform.vidu.com task detail |
| FLUX 3 | Signed result URLs expire about 2 hours after the job finishes | docs.bfl.ai/flux_3 |
| FLUX 3 | The signed result URL is valid for about 10 minutes, so download it promptly | docs.bfl.ai/flux_3 |
What does Sume hand back?
A completed /v1/videos job carries unsigned_urls pointing at the content endpoint on api.sume.com, plus usage.cost. The docs give no expiry for those URLs, so this post claims none. Copy the file anyway if you need it to outlive any link.
How do I stream it to disk?
Check the status first, then stream in chunks so a long clip never sits in memory. The key travels in the header, never in the URL.
import os, requests
H = {"Authorization": f"Bearer {os.environ['SUME_API_KEY']}"}
def save(job_id, path):
r = requests.get(f"https://api.sume.com/v1/videos/{job_id}",
headers=H, timeout=30)
r.raise_for_status()
job = r.json()
if job["status"] != "completed":
raise SystemExit(f"not ready: {job['status']}")
with requests.get(job["unsigned_urls"][0], headers=H,
stream=True, timeout=120) as s:
s.raise_for_status()
with open(path, "wb") as f:
for chunk in s.iter_content(1 << 20):
f.write(chunk)
return job["usage"]["cost"]
print(save(os.environ["JOB_ID"], "clip.mp4"))Does the same rule apply to Sume artifacts on other routes?
Non-video jobs return artifacts with a media.sume.com url in the job result, and the docs tell you to use those Sume media URLs. The habit is the same either way: read the result once, copy what you need, and record the job id so a later question about that clip has an answer.
What should I store next to the file?
Keep the job id, the model, and usage.cost. With the id you can read GET /v1/jobs/{id}/status later, and GET /v1/jobs/{id}/result once the job is completed. Never log the Bearer header or paste a signed vendor URL into a ticket.
Sources
Related posts
More in Developers
- Vidu: failed and moderated videos use no credits. What Sume does
Vidu's pricing page says failed generations, moderation rejections included, use no credits. Sume reserves at submit and releases on failure. How to check each.
- Vidu task states mapped onto Sume job statuses in Python
Map Vidu task states (created, queueing, processing, success, failed) to Sume queued, processing, completed and failed so an old poller keeps working.
- waitForRun or waitForJob? Pick by which Sume endpoint made the id
A Sume run id cannot be read as a job id. Which create endpoint made your id decides waitForRun or waitForJob, with a TypeScript example of each.
- Wan 3.0 reference clips: four 4-second videos break the 15 s cap
Wan 3.0 accepts up to 5 reference videos totalling 15 seconds at 16 fps or more. Four 4-second clips total 16 seconds and fail; three 5-second clips pass.
Written by Sume