usage.cost on a Sume video poll is nullable: guard it before you sum
The /v1/videos poll schema allows usage.cost to be null. Sum only completed jobs, keep unknowns apart in a ledger, and compare to the Wan 3.0 $3.75 estimate.

The usage.cost field on a /v1/videos poll is nullable in the OpenAPI schema, so a ledger that does total += poll["usage"]["cost"] will crash or add zero for a job that has no figure yet. Sum cost only for completed jobs and keep the ids with no cost in their own list.
What the field is
Sume reserves the provider list price times 1.25 at submit, and usage.cost is the billable amount for the job. Your own estimate from pricing_skus is a forecast; the poll is the receipt.
Treat the two as separate columns. When they differ, the receipt wins and the estimate needs fixing.
| Model and resolution | Rate per second | Estimate for 30 s | Estimate for 4 clips |
|---|---|---|---|
| wan-3.0 480p | $0.0625 | $1.875 | $7.50 |
| wan-3.0 720p | $0.125 | $3.75 | $15.00 |
| wan-3.0 1080p | $0.25 | $7.50 | $30.00 |
| seedance-2.5 720p (9:16) | $0.5778 | $17.334 | $69.336 |
A ledger that cannot lie
Decimal(str(cost)) avoids float noise when the value arrives as a JSON number. Jobs that are not completed, or that report no cost, go to the unknown list so a review step can look at them.
from decimal import Decimal
ledger = {"total": Decimal("0"), "unknown": []}
def add(poll):
if poll["status"] != "completed":
return
cost = (poll.get("usage") or {}).get("cost")
if cost is None:
ledger["unknown"].append(poll["id"])
else:
ledger["total"] += Decimal(str(cost))
# after polling every job to a terminal status:
for poll in finished_polls:
add(poll)
print(ledger["total"], len(ledger["unknown"]))Gotchas
The poll body is a bare object, not a {data} envelope, so read poll["usage"] directly. Failed and cancelled jobs are skipped here on purpose; what a failed job was billed is a question for your invoice and the docs on credits, not for this sum.
Store the cost with the job id the moment you see it. Re-polling later costs a read, which is cheap on the 4,800 per minute Free read budget, but a missing row is hard to rebuild.
Sources
Related posts
More in Developers
- Validate a cut list against video-trim's rules before you submit
Video trim has no unbilled check endpoint, so mirror its rules locally: 1800 s source, 0.2 s to 900 s output, one of end or duration, exact with output.
- VEED Fabric audio must be under 10 MB: stereo wav tops out near 56 s
Sume lists VEED Fabric 1.0 audio as Sume-hosted, under 10 MB, at most 300 seconds. A 44.1 kHz stereo wav hits 10 MB near 56 seconds; a 128 kbps mp3 never does.
- Fabric request: image_url and avatar_handle together are not allowed
Sume's VEED Fabric 1.0 body needs exactly one visual source: image_url or avatar_id / avatar_handle, never two. Valid bodies and audio rules.
- Veo 3.1 preview ends October 22: what to change in each request
Google retires three Veo 3.1 preview ids on October 22, 2026 and names Omni as the replacement. A field-by-field list of what changes, and the Sume id to send.
Written by Sume