A dry-run flag for Sume API calls: print the request, skip the spend
Add DRY_RUN to code that calls the Sume API: build the body, key and spend cap, print them, and send nothing. Review a batch before it costs money.

A dry-run flag is the cheapest cost control you can write for a Sume integration: build every request exactly as you would send it, print the body, idempotency key and spend cap, and stop before the network call. A reviewer can then read a whole batch before anything is charged.
Why print the cap
The Sume best-practices page says to cap spend on every run with generation_spend_cap_usd and to treat a missing cap as a bug in the client. A dry run that prints the cap makes a missing one visible in review, not on the invoice.
import asyncio, json, os
DRY_RUN = os.environ.get("DRY_RUN", "1") != "0"
async def submit(client, path: str, body: dict, key: str) -> None:
if "generation_spend_cap_usd" not in body:
raise ValueError("every run needs generation_spend_cap_usd")
if DRY_RUN:
print(json.dumps({"path": path, "key": key, "body": body}, indent=2))
return
r = await client.post(path, json=body, headers={"Idempotency-Key": key})
r.raise_for_status()
async def main() -> None:
body = {"input": {"product_url": "https://shop.example.com/p/1"},
"generation_spend_cap_usd": 3}
await submit(None, "/v1/formats/acme/product-promo/runs", body, "order-1-v1")
asyncio.run(main())Default to dry
Default the flag to on, and require an explicit DRY_RUN=0 for a real run, so a copied script never spends by accident. This is a guard in your code; it does not call Sume, so it cannot tell you a price.
What it does not replace
The cap bounds one run, and the balance gate returns 402 insufficient_credits before provider work starts. Dry runs complement them by catching mistakes earlier, such as a loop that submits the same item twice.
| Layer | Control | Catches |
|---|---|---|
| Before the call | Dry run and review | Wrong batch size, missing cap |
| On the run | generation_spend_cap_usd | Runaway single run |
| On the account | Balance and 402 | Spending beyond funds |
Sources
Related posts
More in Developers
- Dub one Short into 8 languages: Python fan-out and the total cost
Detach and transcribe once, then run one TTS job and one render per language. A Python fan-out and the per-Short bill, from Sume's catalog rates.
- Extend a video Veo didn't make: which API takes an uploaded clip
Veo 3.1 extension only accepts Veo-made videos. Here is what Gemini Omni and Sume accept instead when you need to continue a clip you uploaded.
- Extend an AI clip by hand: last frame to the next clip, in Python
No extend endpoint? Pull a clip's last frame with Sume's video-frames API and feed it as the first frame of the next job. Python code under 30 lines.
- Where to find a voice ID for Sume text to speech: voices_list and voi_
Sume TTS wants a voice UUID or a voi_ library id, never a voice name like alloy. Where to copy it from, and the error you get for a name.
Written by Sume