Sume media tools: which wait for a 200 and which always return 202
Video inspect defaults to sync; trim, filter, detach, compose and timeline to async; frames is always 202. The 30 s wait and where each result is read.

The Sume media tools do not share a default communication mode. Video inspect defaults to sync, waits up to 30 seconds, and returns 200 if it finishes in that time. Video trim, video filter, audio detach, timeline compose, timeline audio and Timeline 1.0 render default to async. Video frames pins async and always returns 202, so mode: "sync" does not change anything on it. Every tool that accepts mode: "sync" uses the same 30-second window, and returns 202 when the work is not done by then.
The table
Reading the result also differs. Some tools have a resource route and some only have the job envelope, so the polling code you write for one will not fit all.
| Tool | Default mode | Sync allowed | Read the result at |
|---|---|---|---|
| video inspect | sync (30 s max) | yes | GET /v1/video-inspect/:id |
| video trim | async | yes, 30 s | GET /v1/jobs/:id/result |
| video filter | async | yes, 30 s | GET /v1/jobs/:id/result |
| audio detach | async | yes, 30 s | GET /v1/jobs/:id/result |
| video frames | async, pinned | no, always 202 | GET /v1/video-frames/:id |
| timeline compose | async | yes, 30 s | GET /v1/jobs/:id/result |
| timeline audio | async | yes, 30 s | GET /v1/jobs/:id/result |
| Timeline 1.0 render | async | yes, 30 s | GET /v1/jobs/:id/result |
A handler that covers all of them
Write one function for the response: if the status code is 200, the body is the result; if it is 202, read the job ID from request_id and poll GET /v1/jobs/:id/status until result_ready, then GET /v1/jobs/:id/result. Frames and inspect also expose their own resource reads, but the job envelope works as the common path. The hosted MCP flow is the same shape: the write tool, then jobs_wait, then the read tool or jobs_result.
import os, time, json, urllib.request
BASE = "https://api.sume.com"
def call(method, path, body=None):
data = json.dumps(body).encode() if body is not None else None
req = urllib.request.Request(BASE + path, data=data, method=method, headers={
"Authorization": f"Bearer {os.environ['SUME_API_KEY']}",
"Content-Type": "application/json",
"Idempotency-Key": "trim-demo-001",
})
with urllib.request.urlopen(req) as resp:
return resp.status, json.load(resp)
if __name__ == "__main__":
status, body = call("POST", "/v1/video-trim", {
"video_url": "https://media.sume.com/artifacts/artf_demo/long.mp4",
"start": 0, "duration": 60, "mode": "sync"})
print(status, body.get("request_id"))Why it matters for agents and short timeouts
Hosts that cut off a call at 60 seconds, or gateways that do so at 30, are safe with sync because the tool's own wait is capped at 30 seconds and falls back to 202. The sync default on video inspect also means a probe-only call with frames: false often returns the answer in one request, with no polling.
Sources
Related posts
More in Developers
- Why the Sume catalog shows $0.02 to $42.86 for one video route
GET /v1/catalog publishes an estimate plus a minimum and maximum for each route. Video spans $0.02 to $42.86, image $0.01 to $7.36, TTS $0.01 to $0.95.
- Sume status vocab: a job is completed, a resource is ready
Sume lists three status vocabularies: job, resource, webhook delivery. Only jobs say completed; resources say ready, so a check on the wrong one never matches.
- Sume timeouts in one table: 30 s, 55 s, 10 s, 90 minutes
Every wait in the Sume API has its own number: sync 30 s, jobs_wait 55 s, webhook attempts 10 s, SDK helpers 10 and 20 minutes, Format runs 90 minutes.
- Sume /v1/usage summary.final is false: a hold is open, not spent
Read GET /v1/usage?job_id= and book cost only when summary.final is true. held_usd_micros and refunded_usd_micros are not spend. Code to poll it.
Written by Sume