TikTok ad cuts in 9:16, 1:1 and 16:9 from one master clip
TikTok ads allow 9:16, 1:1 and 16:9 with set minimums. Cut all three from one master with Sume Timeline, picking cover or blur so little is lost.

TikTok's in-feed ad specification lists three aspect ratios for non-Spark ads, each with a minimum size: 9:16 at least 540x960, 16:9 at least 960x540, and 1:1 at least 640x640. One 9:16 master can produce all three in Sume's Timeline, but the fit mode decides how much of the picture survives, and it differs by ratio.
The sizes are from TikTok Ads Help: video ad specifications (read 2026-10-03). The page also lists the formats .mp4, .mov, .mpeg, .3gp and .avi, a length of up to 10 minutes, a file size of 500 MB or less and a bitrate of at least 516 kbps. Sume's Timeline output is MP4.
What each cut keeps
From a 1080x1920 master, cover fills the frame and crops the overflow. For a 1080x1080 square it keeps 1080 of 1920 rows, 56.25 percent of the height. For a 1920x1080 wide frame it would scale to 1920 wide and keep only 1080 of about 3413 rows, 31.6 percent. So use cover for 1:1 if the subject sits mid-frame, and blur for 16:9, which keeps the full vertical frame fitted over a blurred fill.
| Ratio | Output | TikTok minimum | fit | Picture kept |
|---|---|---|---|---|
| 9:16 | 1080x1920 | 540x960 | cover | 100% |
| 1:1 | 1080x1080 | 640x640 | cover | 56.25% of the height |
| 16:9 | 1920x1080 | 960x540 | blur | 100%, shown smaller |
Plan all three, then render
Timeline 1.0 bills $0.10 per started output minute, so three 20-second cuts cost $0.30. The unbilled plan call confirms that before you commit. This loop runs the plan for each cut.
import json, os, time, urllib.request as u
def call(method, path, body=None, key=None):
h = {"Authorization": "Bearer " + os.environ["SUME_API_KEY"],
"Content-Type": "application/json"}
if key:
h["Idempotency-Key"] = key
data = json.dumps(body).encode() if body is not None else None
req = u.Request("https://api.sume.com" + path, data, h, method=method)
with u.urlopen(req) as r:
out = json.load(r)
return out.get("data", out)
SRC = "https://media.sume.com/artifacts/artf_demo/master.mp4"
CUTS = {"9x16": (1080, 1920, "cover"), "1x1": (1080, 1080, "cover"),
"16x9": (1920, 1080, "blur")}
for name, (w, h, fit) in CUTS.items():
body = {"audio": {"mode": "silence", "duration_seconds": 20},
"video": [{"source_url": SRC, "start": 0, "duration": 20, "fit": fit}],
"output": {"width": w, "height": h}}
plan = call("POST", "/v1/timeline-1.0/plan", body)
print(name, plan["billable_minutes"], plan["estimated_cost_usd_micros"])
Spark Ads differ
The same page lists separate rules for Spark Ads: mp4 or mov and no duration restriction. This post covers only the non-Spark specification, so check the page for the Spark row before you reuse these cuts there.
Check the outputs
After rendering, read each file back with video inspect and compare its width and height with TikTok's minimums. 1080x1920, 1080x1080 and 1920x1080 clear them with room, so the main risk is a master that was exported too small to begin with. Resolution you do not have cannot be added later by an encoder.
Sources
Related posts
More in Media tools
- Cyber Monday half-banner: price card over a clip, compose stack
Stack a Sume-hosted price card above a product clip in one vertical shot with Timeline compose, at a flat $0.02 per job, then drop it into a Timeline.
- Deepgram's 2 GB limit says extract audio first: use Sume audio detach
Deepgram's pre-recorded API allows files up to 2 GB and says to extract audio from large videos first. Sume audio detach does that for a hosted video.
- Demand Gen video under 10 seconds: no in-stream; upload 1 to 5 videos
Demand Gen needs 5 seconds minimum, but videos under 10 seconds can't serve on YouTube in-stream. Upload 1 to 5 per ad; crop a 9:16 master on Sume.
- Demucs is archived: still use it to split a song into stems?
Demucs was archived on January 1, 2025 but is MIT-licensed and still splits drums, bass, vocals and other. How to feed it a track from Sume audio detach.
Written by Sume