pricing_skus has three key shapes: one Python function for the total
GET /v1/videos/models returns per-video-second-<res>, per-video-second and per-1000-video-tokens. A Decimal function totals a 30 s clip and flags token pricing.

GET /v1/videos/models returns pricing_skus in three key shapes, and only two of them can be multiplied by seconds: per-video-second-<res> and per-video-second. The third, per-1000-video-tokens, is how Seedance is priced, so a 30 s 720p Seedance 2.5 clip is $17.334 from Sume's pricing code, not from a rate times 30.
The three shapes and what each one gives you
Each model descriptor carries a pricing_skus object whose values are strings with four decimals. Which keys exist depends on the model, so a client that reads one fixed key will break the first time it meets another family.
The catalog also lists a model only when its provider is configured, so loop over what the endpoint returns instead of a hardcoded list.
- per-video-second-<res>, for example per-video-second-720p: one rate per resolution (Wan 3.0, MiniMax H3, H3 Max, Gemini Omni Flash 1.1).
- per-video-second, with an optional per-video-second-audio: a flat rate, plus a higher one when audio is on.
- per-1000-video-tokens: a token rate; the token count depends on resolution, aspect and length and is not in the catalog.
| Model | SKU key | Rate per second | Clip | Total |
|---|---|---|---|---|
| wan-3.0 | per-video-second-720p | $0.125 | 30 s | $3.75 |
| gemini-omni-flash-1.1 | per-video-second-1080p | $0.1875 | 10 s | $1.875 |
| minimax-h3-max | per-video-second-1080p | $0.20 | 15 s | $3.00 |
| seedance-2.5 (9:16) | per-1000-video-tokens | $0.5778 (derived) | 30 s, 720p | $17.334 |
One function that totals a clip
Return None when no per-second key exists, so token-priced models are never silently priced at zero. Use Decimal so the four-decimal strings add up exactly.
import os
from decimal import Decimal
import requests
def clip_usd(skus, seconds, res):
for key in (f"per-video-second-{res}", "per-video-second"):
if key in skus:
return Decimal(skus[key]) * seconds
return None # token-priced: no count in the catalog
r = requests.get(
"https://api.sume.com/v1/videos/models",
headers={"Authorization": f"Bearer {os.environ['SUME_API_KEY']}"},
timeout=30,
)
r.raise_for_status()
for m in r.json()["data"]:
total = clip_usd(m["pricing_skus"], 10, "720p")
print(m["id"], total if total is not None else "no per-second rate")Gotchas
The function ignores per-video-second-audio on purpose; add it when you send generate_audio on a model that lists it. Resolutions differ by model: MiniMax H3 is 768p, not 720p, so it prints no rate in the loop above. Read supported_durations and supported_resolutions from the same descriptor before you total anything.
Sume reserves the provider list price times 1.25 at submit and reports the billable amount in usage.cost on the poll, so check your estimate against that field after the first real job.
Sources
Related posts
More in Developers
- Sume SDK errors by status: which class for 401, 402, 403, 404, 409
The @sume-com/sdk error classes by HTTP status, which fields they carry, and how run helpers differ from generated operations that resolve instead of throwing.
- Sume TTS language field: set ja or ko, or the voice reads in English
Omit language on Sume TTS and the provider defaults to English; Sume infers ko or ja only from a Hangul- or kana-only script. Set it on non-English scripts.
- A known-answer test for the Sume webhook signature: one body, one hex
A fixed secret, timestamp, and body give a fixed sume-v1 value. Use it to unit-test your verifier: valid, stale, empty secret, and rotation cases in Python.
- Webhook and poll race on a Sume video: one SQLite insert decides
Keep polling as a backup to the job webhook. Dedupe on job_id with INSERT OR IGNORE so only one path downloads. 10 deliveries 30 s apart cover 270 s.
Written by Sume