Keep your Sora-style create_video() call: map it onto Sume

Sora's seconds, size and input_reference become duration, resolution plus aspect_ratio, and a first frame. Here is that map as a Python wrapper over Sume.

4 min readSume
All posts

You can keep a Sora-shaped create_video(prompt, seconds, size, input_reference) function and change only its body. Higgsfield's migration guide maps seconds to duration, size to resolution plus aspect_ratio, and input_reference to an image URL. On Sume, the same three changes apply, with the reference sent as a first frame.

Field map from Sora-style requests to Sume /v1/videos (Sora side from the Higgsfield guide, read 2026-10-03)
Sora-style fieldSume fieldNote
secondsdurationInteger seconds; limits differ per model
sizeresolution + aspect_ratioA size field returns 400 unsupported_parameter on Sume
input_referenceframe_images[0] with frame_type first_framePublic HTTPS image URL
Bearer tokenAuthorization: Bearer $SUME_API_KEYSame header shape

The wrapper

The wrapper translates four common size strings and derives an idempotency key from the arguments, so repeating a call replays the same job rather than paying twice. It returns the submit response, which carries id and polling_url.

import hashlib
import os
import requests

SIZES = {"1280x720": ("720p", "16:9"), "720x1280": ("720p", "9:16"),
         "1920x1080": ("1080p", "16:9"), "1080x1920": ("1080p", "9:16")}

def create_video(prompt, seconds=8, size="1280x720", input_reference=None,
                 model="seedance-2.5"):
    resolution, aspect = SIZES[size]
    body = {"model": model, "prompt": prompt, "duration": int(seconds),
            "resolution": resolution, "aspect_ratio": aspect}
    if input_reference:
        body["frame_images"] = [{"type": "image_url", "frame_type": "first_frame",
                                 "image_url": {"url": input_reference}}]
    raw = f"{model}|{prompt}|{seconds}|{size}|{input_reference}"
    key = hashlib.sha256(raw.encode()).hexdigest()[:32]
    headers = {"Authorization": f"Bearer {os.environ['SUME_API_KEY']}",
               "Idempotency-Key": key}
    r = requests.post("https://api.sume.com/v1/videos", json=body, headers=headers)
    r.raise_for_status()
    return r.json()

What stays the same

The guide says the submit, poll and download steps are unchanged in shape, and that holds on Sume: poll the returned polling_url until the status is completed, then fetch the file from unsigned_urls[0] or from GET /v1/videos/{jobId}/content with your key. A failed status carries an error field. Statuses on this route are pending, in_progress, completed, failed and cancelled.

What does not carry over

Three differences need a decision rather than a mapping:

  • Durations: Sora-era code may request lengths the new model does not list. Seedance 2.5 accepts 4 to 30 seconds on Sume, so a 2-second request fails validation.
  • Seeds and passthrough: Sume models report seed: false and reject a seed field, and a non-empty provider.options returns 400.
  • Output retention and URLs: treat artifact URLs as opaque and download on completion.

Testing the wrapper

Call it once with a 6-second 1280x720 text prompt and once with an input_reference. Confirm the poll response reports the model you passed and a usage.cost that matches the reservation. The field-by-field request reference is in Video generation.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume