Save a finished Seedance clip to your own storage, not the result URL

When a Seedance 2.5 or Wan 3.0 job completes, read /v1/jobs/{id}/result, take the artifact url and content_type, and copy the file into your own storage.

5 min readSume
All posts

Read GET /v1/jobs/{id}/result once the job reports result_ready: true, take each entry of result.artifacts (it has id, url, type, and content_type), and copy the file into your own storage. Store your copy's location next to the Sume job id, so you never depend on a single URL later.

Sume returns Sume media URLs, never raw provider URLs, and a result exists only after completion. Before that, /result answers 409 job_not_completed rather than an empty body.

What a result holds

A completed video job returns the envelope below inside data. The type tells you what you have, and content_type gives the file extension to use.

{
  "id": "job_...",
  "status": "completed",
  "result": {
    "artifacts": [
      {
        "id": "artf_...",
        "url": "https://media.sume.com/artifacts/...",
        "type": "video",
        "content_type": "video/mp4"
      }
    ]
  }
}

Copy it

Stream to disk in chunks so a 30-second 1080p file does not sit in memory. Swap the file write for an upload call to your storage client. Name the file after the job id so a retry overwrites instead of duplicating.

import os, requests
BASE = "https://api.sume.com"
H = {"Authorization": f"Bearer {os.environ['SUME_API_KEY']}"}

def save(job_id: str, out_dir: str = ".") -> list[str]:
    r = requests.get(f"{BASE}/v1/jobs/{job_id}/result", headers=H, timeout=30)
    r.raise_for_status()
    paths = []
    for i, art in enumerate(r.json()["data"]["result"]["artifacts"]):
        ext = art["content_type"].split("/")[-1]
        path = os.path.join(out_dir, f"{job_id}-{i}.{ext}")
        with requests.get(art["url"], stream=True, timeout=120) as dl:
            dl.raise_for_status()
            with open(path, "wb") as f:
                for chunk in dl.iter_content(1 << 20):
                    f.write(chunk)
        paths.append(path)
    return paths

print(save("job_123"))

What to keep

Keep the job id, the model id, and the prompt with the file. GET /v1/jobs/{id}/events gives a public timeline you can use to debug later, but it does not show provider task ids or raw provider URLs.

If you only need the file on a laptop, sume jobs download <job_id> --output-dir ./out does the same copy from the CLI.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume