Video ad end card: add a fade-in and fade-out with Timeline
Close an ad with a 1-second fade-out and open it with a 0.5-second fade-in using output.fade_in_seconds and fade_out_seconds in a Timeline 1.0 render.

To end a video ad on a clean fade, set output.fade_out_seconds in a Timeline 1.0 render, and output.fade_in_seconds for the opening. Each accepts 0 to 5 seconds, and their sum must not exceed the output length, or the plan fails with edge_fades_exceed_output. A render is $0.10 per output minute, rounded up, at the public rate; the plan call is unbilled.
Source: Timeline 1.0: assemble clips into one MP4. Confirm the live price in GET /v1/catalog.
Fades or an end card clip?
A fade is a style choice; an end card is content. A logo card with a call to action should be its own clip or still, put in the last video[] slot, because stills are static holds in Timeline. Then output.fade_out_seconds fades the whole result to black after it. That is the combination most ads need: a still end card for a few seconds with a short fade at the end. A hard cut to black on the last frame can look like a rendering mistake.
The document
The ad below is 15 seconds: 12 seconds of footage with a 3-second end card, then a half-second fade-in at the start and a one-second fade-out at the end. fit: "cover" is the default and fills the frame, and blur fills a mismatched ratio with a blurred copy; for an end card with text near the edges, use contain so nothing gets cropped. Stills accept a motion field that the render ignores, with a motion_ignored warning, which is not a failure.
import json, os, urllib.request
doc = {
"audio": {
"url": "https://media.sume.com/artifacts/artf_demo/voice-15s.wav",
"duration_seconds": 15,
},
"video": [
{"source_url": "https://media.sume.com/artifacts/artf_demo/ad-12s.mp4",
"start": 0, "duration": 12},
{"source_url": "https://media.sume.com/artifacts/artf_demo/end-card.png",
"start": 12, "duration": 3, "fit": "contain",
"transition": {"type": "fade", "duration": 0.25}},
],
"output": {"width": 1080, "height": 1920, "fade_in_seconds": 0.5,
"fade_out_seconds": 1},
}
req = urllib.request.Request(
"https://api.sume.com/v1/timeline-1.0/plan",
data=json.dumps(doc).encode(),
headers={"Authorization": "Bearer " + os.environ["SUME_API_KEY"],
"Content-Type": "application/json"},
)
with urllib.request.urlopen(req) as r:
print(json.load(r))
Limits that trip people up
A transition is only allowed on slots after the first, its duration is at most 1 second and at most half the shorter neighbour, and it must land on at least one output frame. More than 8 adjacent fades are refused with too_many_chained_transitions. The first slot's start must be 0. Coverage can stop at most 0.5 seconds before the end of the spine. If the frame rate of your sources differs from the one you ask for, the render reports output_fps_resamples_sources, so leave output.fps out unless you need a specific rate.
| Field | Allowed | Refusal |
|---|---|---|
| output.fade_in_seconds | 0 to 5 | edge_fades_exceed_output if the sum is too long |
| output.fade_out_seconds | 0 to 5 | Same |
| transition.duration | Up to 1 s and half the neighbour | transition_too_long |
| Transition on first slot | Not allowed | transition_on_first_segment |
| Adjacent fades | At most 8 | too_many_chained_transitions |
Run the plan before the render
Plan first, because it checks every one of those rules without charging you. When it passes, submit the same document to /v1/timeline-1.0/render with an Idempotency-Key, and check the result's warnings[]. If the end card is hard to read, regenerate the card, not the whole ad.
The plan answer carries duration_seconds, segment_count, billable_minutes and estimated_cost_usd_micros. For the 15-second document above you should see one billable minute, because a render reserves ceil(audio.duration_seconds / 60) minutes. Check that number before you render, since a longer end card or a second ad in the same document is the usual way a cheap render becomes a two-minute one.
Soft warnings are not failures. A source that is shorter than its slot is padded or looped, a still reports motion_ignored if you gave it a motion, and a transition may be snapped to the frame grid. They appear in warnings[] on the render so you can decide whether the result is acceptable. A fade in, a fade out and a short transition into the card are three different controls, and it helps to change only one at a time when you compare two renders of the same ad.
Sources
Related posts
More in Use cases
- Which Sume video model makes a 15-second 9:16 ad? A catalog filter
List the models in GET /v1/videos/models that accept 15 seconds at 9:16 with a short Python filter, instead of trusting a stale table.
- Yard sign artwork via API: 3:2 art, a quoted headline, a print check
Generate yard sign artwork at 3:2 on Sume with GPT Image 2.5 or Ideogram 4.5, keep the headline to a few words, quote it, and check the pixel size for print.
- YouTube Shorts series covers: one template, one edit per episode
YouTube is rolling out Shorts series with covers. Make one template with Ideogram 4.5, then change only the episode number with one edit call per episode.
- Shorts series: a season of 30-second episodes with Seedance 2.5
Shorts series are rolling out. A season of eight 30-second episodes costs $138.72 on Seedance 2.5 at 720p and $30.00 on Wan 3.0. The math and a batch loop.
Written by Sume