Python urllib only: a Wan 3.0 test job for $0.125
No requests, no SDK: 27 lines of Python urllib submit a 2-second 480p Wan 3.0 job, poll it and save the MP4. The job costs $0.125, the cheapest Wan clip.

A locked-down build box, a Lambda layer you cannot extend or a teaching repo with no dependencies all have one thing in common: you cannot pip install requests. The Sume video API does not need it. Submit, poll and download are plain HTTP, and Python's urllib follows the 302 on the content URL by itself.
The script below uses wan-3.0 because it has the smallest job in the catalog. Wan 3.0 accepts 2 to 30 seconds at 480p, 720p or 1080p. At 480p a 2-second clip is 2 x $0.0625 = $0.125, which makes it a cheap way to prove your key, your balance and your download path work before you send a real brief. Nothing here is specific to Wan, though: if you pass a different model id the same code runs, and the only things that change are the price and the limits the catalog advertises for that id. Asking for a value the model does not list, such as a 1-second clip on a model with a 2-second floor, is not silently adjusted; the API answers 400 unsupported_capability and does not start a job.
The script
Export SUME_API_KEY and run it. It was run against a local stand-in server returning the documented 202, poll and 302 shapes, with POLL_SECONDS=0; against the live API leave the default of 30 seconds between polls.
import json, os, time, urllib.request
BASE = os.environ.get("SUME_API_BASE", "https://api.sume.com")
KEY = os.environ["SUME_API_KEY"]
def call(url, body=None, headers=None):
req = urllib.request.Request(url, headers={"Authorization": f"Bearer {KEY}", **(headers or {})})
if body is not None:
req.data = json.dumps(body).encode()
req.add_header("Content-Type", "application/json")
return urllib.request.urlopen(req) # follows the 302 on /content
job = json.load(call(f"{BASE}/v1/videos", {
"model": "wan-3.0", "prompt": "Rain on a neon street", "duration": 2,
"resolution": "480p", "aspect_ratio": "16:9"}, {"Idempotency-Key": "wan-test-0001"}))
while True:
time.sleep(int(os.environ.get("POLL_SECONDS", "30")))
state = json.load(call(job["polling_url"]))
if state["status"] in ("failed", "cancelled"):
raise SystemExit(f'{state["status"]}: {state.get("error")}')
if state["status"] == "completed":
with open("out.mp4", "wb") as f:
f.write(call(state["unsigned_urls"][0]).read())
print("cost", state["usage"]["cost"])
breakWhat it proves
One successful run settles four separate questions at the cost of 12.5 cents.
- The key authenticates: a bad key would have come back as 401 on the first call.
- The workspace has USD balance: with none, submit answers 402
insufficient_credits. - Your network can reach the artifact host, because the final read goes through the redirect.
usage.costis in the poll body, so your accounting code has something real to parse.
Wan 3.0 price ladder
The Sume price is the provider list times 1.25. Wan 3.0 is priced by the second and the resolution tier, so it is easy to estimate: multiply seconds by the rate.
| Resolution | Per second | 2 s clip | 30 s clip |
|---|---|---|---|
| 480p | $0.0625 | $0.125 | $1.875 |
| 720p | $0.125 | $0.25 | $3.75 |
| 1080p | $0.25 | $0.50 | $7.50 |
When to leave the test model
Wan 3.0 takes up to 10 images, 5 videos and 5 audio files as references and has audio as an option, so it can carry real work too. Switch the model string to seedance-2.5 or kling-3 when you need their look; the rest of the script stays the same because /v1/videos has one request shape for every model.
Sources
Related posts
More in Developers
- Queue a 30 s H3 Max Recast job: the reserve at 768p and 1080p
H3 Max Recast swaps people in a 5 to 30 s video. Price arithmetic for a 30 s source at 768p and 1080p, plus the Python to submit it and poll it.
- queue_admission_unavailable: a failed Sume job you can retry
If Sume cannot evaluate generation admission, the job fails with queue_admission_unavailable, retryable true, and is refunded. Read it, then create again.
- No language warning on Sume TTS? Raw voice ids skip the check
Sume's voice-language check needs library metadata. Send a raw voice id it cannot look up and no mismatch warning appears. How to add your own safety net.
- Read Sume rate limit headers with curl: remaining, reset, retry-after
Run curl -D - on any /v1 route to read ratelimit-limit, ratelimit-remaining, ratelimit-reset, and retry-after on a 429. Anonymous reads are 4x Free writes.
Written by Sume