Gemini Omni Interactions API versus Sume /v1/videos: field mapping

Google returns Omni video as base64 in an Interactions response. Sume returns 202 with a polling_url and a content URL. See the mapping and a bytes helper.

5 min readSume
All posts

Google's Gemini Omni call is POST /v1beta/interactions with a model and an input, and the video comes back inside the response as base64 data. Sume's equivalent is POST /v1/videos with model and prompt; it returns 202 with a job id and polling_url, and you download the MP4 from a content URL when the status is completed.

The two shapes

Google's minimal request is a model id of gemini-omni-1.1-flash and an input string. Video output appears in a steps array as a model_output with a video content item of MIME type video/mp4 and base64 data; the SDK exposes interaction.output_video.data. Sume follows the OpenRouter video shape: bare model id, prompt, optional duration, resolution, aspect_ratio, frame_images, input_references, and callback_url.

Gemini Omni Interactions API versus Sume /v1/videos (read 2026-10-05)
ConceptGoogle Interactions APISume /v1/videos
Model idgemini-omni-1.1-flashgemini-omni-flash-1.1
Promptinputprompt
First responseVideo inside the response202 with id, polling_url, status
Outputbase64 data in stepsunsigned_urls[0] content URL
AuthAPI key in the query stringAuthorization: Bearer
Retry safetyNot documented on the page readIdempotency-Key header

What changes in your code

The big change is the loop. With Google you read the video from the response; with Sume you poll GET /v1/videos/{id} and the statuses are pending, in_progress, completed, failed and cancelled. Cost is returned as usage.cost on completion. The single-reference rule also differs: a lone reference goes in input_references and conditions the whole clip.

A helper that behaves like output_video.data

This function returns raw MP4 bytes for a prompt, so the rest of an app that expects bytes from Google's SDK does not need to change.

import os, time, hashlib, json, requests

H = {"Authorization": f"Bearer {os.environ['SUME_API_KEY']}"}

def output_video_bytes(prompt, **extra):
    body = {"model": "gemini-omni-flash-1.1", "prompt": prompt, **extra}
    key = "map-" + hashlib.sha256(json.dumps(body, sort_keys=True).encode()).hexdigest()[:24]
    r = requests.post("https://api.sume.com/v1/videos", json=body, timeout=60,
                      headers={**H, "Idempotency-Key": key})
    r.raise_for_status()
    url = r.json()["polling_url"]
    while True:
        s = requests.get(url, headers=H, timeout=30).json()
        if s["status"] == "completed":
            return requests.get(s["unsigned_urls"][0], headers=H, timeout=120).content
        if s["status"] in ("failed", "cancelled"):
            raise RuntimeError(s.get("error", s["status"]))
        time.sleep(10)

data = output_video_bytes("A marble rolls down a chain-reaction track", duration=5)
open("out.mp4", "wb").write(data)
print(len(data), "bytes")

Migration steps

A base64 payload in a response is simple but heavy: a 10-second 4K clip can be many megabytes inside a JSON body. A content URL lets you stream the file to disk and keeps your JSON handlers small.

  • Swap the model id to gemini-omni-flash-1.1.
  • Replace the query-string key with a bearer header.
  • Move to a poll loop, or set callback_url and verify the signature.
  • Add an Idempotency-Key derived from the request body.

Features that do not map one to one

Google's guide lists extension up to 40 seconds and edit inputs of 10 seconds or less; on Sume, edit uses the Video Router's video_url field and no extend field is exposed. Google bills output by the token; Sume bills the provider list per second times 1.25. See the Video Router and Videos guide for the full contract.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume