One probe, three verdicts: Reels, Shorts and TikTok ad limits
Read a clip's size, rate, length and bitrate once with Sume video inspect, then test it against Instagram, YouTube and TikTok's published limits.

If one master video goes to three platforms, one probe is enough to test it against all three. Sume's video inspect returns width, height, frame rate, duration and file size. From those you can check Instagram's Reel limits, YouTube's Shorts shape and length, and TikTok's ad minimums and bitrate floor in about ten lines.
The limits are from three vendor pages, all read 2026-10-03: Instagram Reel size, YouTube Shorts upload and TikTok video ad specifications. The script tests only what those pages state, so it cannot promise a platform will accept a file.
The limits in one table
Each row is one rule the script tests.
| Platform | Rule tested |
|---|---|
| Instagram Reel | Ratio 1.91:1 to 9:16, at least 30 FPS, at least 720 pixels |
| YouTube Short | Up to 3 minutes, square or vertical |
| TikTok ad (non-Spark) | 9:16 from 540x960, 16:9 from 960x540 or 1:1 from 640x640; up to 10 minutes; 500 MB or less; at least 516 kbps |
The script
Per the Sume docs, the probe takes frames: false and returns size_bytes alongside the video fields. Bitrate here is the average over the whole file, audio included, so it is a rough check against the 516 kbps floor, and a frame rate of 29.97 reads as under 30.
import json, os, time, urllib.request as u
def api(m, 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 else None
with u.urlopen(u.Request("https://api.sume.com" + path, data, h, method=m)) as r:
d = json.load(r)
return d.get("data", d)
def probe(url):
d = api("POST", "/v1/video-inspect", {"video_url": url, "frames": False}, "p12")
for _ in range(30):
if d.get("probe"): return d["probe"]
time.sleep(2); d = api("GET", "/v1/video-inspect/" + d["id"])
p = probe("https://media.sume.com/artifacts/artf_demo/ad.mp4")
w, h, s = p["width"], p["height"], p["duration_seconds"]
kbps = p["size_bytes"] * 8 / s / 1000
mins = {(9, 16): (540, 960), (16, 9): (960, 540), (1, 1): (640, 640)}
tt = any(abs(w / h - a / b) < 0.01 and w >= mw and h >= mh
for (a, b), (mw, mh) in mins.items())
print("Reel", 9 / 16 <= w / h <= 1.91 and p["fps"] >= 30 and min(w, h) >= 720)
print("Short", s <= 180 and w <= h)
print("TikTok ad", tt and kbps >= 516 and p["size_bytes"] <= 500e6 and s <= 600)
Reading the output
A 1080x1920 file at 30 fps, 45 seconds and 12 MB averages about 2,133 kbps and passes all three. The same file at 24 fps fails the Reel line only, and the fix is a 30 fps re-encode. A 1080x1920 clip over 180 seconds still suits TikTok ads, up to 600 seconds, but fails the Short line.
Extending it
Each platform check is one line, so adding a fourth platform means one more print. Keep the limits in a dictionary at the top and put the date you read the vendor page next to them, because these pages change and a stale number in a script is worse than none.
Sources
Related posts
More in Media tools
- Open vs closed captions vs subtitles: what Sume's captions are
Sume's video captions are open captions burned into the pixels. The docs list no SRT or VTT export, and SRT uploads are unsupported. When that is right.
- OpenShot 4.0.1 Razor tool vs a hosted trim: exact or keyframe
OpenShot 4.0.1 upgrades its Razor cutting tool. Sume's video-trim cuts a clip by start and duration, exact (re-encode) or keyframe (stream copy), at $0.02.
- Performance Max AI video: sourced, enhanced or auto-generated?
Performance Max has three AI video features: sourced (opt-in), enhancements (default on), auto-generated (when you upload none). Which one touches your clip.
- Photo in a Timeline slot ignores source_in and motion: why
A still has no time axis, so Timeline holds it for the whole slot and warns still_source_in_ignored or motion_ignored. Real camera moves need a video clip.
Written by Sume