JSON to video API: render an MP4 from a timeline document
A JSON to video API renders one MP4 from a document that says which clips play when, over which audio. How Sume's Timeline 1.0 does it, and costs.

A JSON to video API takes a JSON document that describes an edit (which clips play, when, for how long, over which audio) and renders it into one video file on the server, so you don't run a renderer yourself. Your code writes the JSON; the API returns an MP4.
On Sume that document is the body of Timeline 1.0: one audio track plus ordered video[] slots, rendered by POST /v1/timeline-1.0/render. The facts below come from the Timeline 1.0 docs, read on 2026-09-29; anything described as current behavior is read from Sume's code.
What goes in the JSON?
The docs call it a declarative document: the server compiles FFmpeg itself, and callers never send filtergraphs, codecs, or shell fragments. A document has four parts; the limits are in the table further down.
audio: the sound and the output length (audio.duration_seconds), from oneaudio.url, up to 20audio.parts[], oraudio.mode: "silence".video[]: the slots, each with asource_url(a clip or a still), astarton the timeline, aduration, and optionally asource_ininto the file, afit(cover,contain,stretch, orblur), and atransitionon any slot after the first.output: optional size, frame rate, and edge fades. The default is 1080×1920.soundtrack: an optional music bed under the audio.
{
"audio": {
"url": "https://media.sume.com/artifacts/artf_demo/voice.wav",
"duration_seconds": 24
},
"video": [
{
"source_url": "https://media.sume.com/artifacts/artf_demo/intro.mp4",
"start": 0,
"duration": 8
},
{
"source_url": "https://media.sume.com/artifacts/artf_demo/talk.mp4",
"start": 8,
"duration": 16,
"fit": "contain",
"transition": { "type": "fade", "duration": 0.25 }
}
]
}How do I turn my own JSON data into a video?
Map it. A JSON to video API does not read arbitrary JSON; it reads its own schema. If your data is a list of products, scenes, or rows, your code loops over it and writes one Timeline document per video: each item becomes a slot with a start and a duration, and the next item's start is the sum of the durations before it. The first slot must start at 0, and later starts must increase.
Then send each document in two calls:
POST /v1/timeline-1.0/planis unbilled. It runs the schema, the URL checks, and the compiler, and returnsduration_seconds,billable_minutes, andestimated_cost_usd_microswithout creating a job. Validate a timeline before rendering covers it.POST /v1/timeline-1.0/renderwith anIdempotency-Keycreates the job. PollGET /v1/jobs/:id/statusuntil it is terminal, then readvideo_urlfromGET /v1/jobs/:id/result, or pass awebhook_urlto be called when it ends.
Can it render a Lottie JSON animation?
No. A Lottie (bodymovin) animation file is also JSON, but it describes vector shapes and keyframes. The fields in Sume's Timeline docs include no Lottie input, no text layer, and no template variable: its JSON arranges existing video clips and stills over audio. Titles and captions are a separate step, such as timed text over a video.
How much does a JSON to video render cost?
The render reserves $0.10 per output minute, counted as ceil(output minutes), and captures its own compute, never above the reservation. A 90-second video reserves 2 minutes, at most $0.20, plus a 5.5% agent fee by default. The plan call is free.
| Limit | Value |
|---|---|
| Output length | 1–1,800 s (audio.duration_seconds) |
| Slots | 1–200 video[] entries |
| Transition | ≤ 1 s, ≤ 50% of the shorter neighbour; more than 8 chained is refused |
| Output size | Even integers 256–2160 per edge |
| Frame rate | 24, 25, 30, or 60 |
| Media URLs | This workspace's media.sume.com files only |
What are the catches?
- Every URL in the document must already be a file in your Sume workspace, such as an earlier Sume job's output. Off-host URLs like
https://example.com/…are rejected. - The render's sound comes only from
audioandsoundtrack. In current code each clip's own audio is dropped, so put speech or music on the audio track. - A frame rate that differs from a source's is met by repeating or dropping frames, and the result warns
output_fps_resamples_sources.
Sources
Related posts
More in Developers
- Kling API rate limit: concurrency by package and error 1303
Kling's API limits concurrent tasks by resource package, not requests per second. Over the cap, a create fails with HTTP 429, code 1303.
- Promise.allSettled vs Promise.all for a batch of API jobs
Promise.allSettled waits for every promise and reports each outcome; Promise.all rejects on the first failure. For paid API jobs, use allSettled.
- Python API rate limiting: stay under a per-minute limit
Pace Python API calls with an asyncio limiter set under the API's per-minute budget, keep polling on its own budget, and back off on 429 retry-after.
- Python requests default timeout: there isn't one
Python Requests has no default timeout: without timeout= a call can hang indefinitely. Set (connect, read) on every call, and keep it short for job APIs.
Written by Sume