TikTok playable ads: 5 MB zip, no HTTP requests, shrink the video

A TikTok playable ad is a zip under 5 MB with index.html and config.json, and no outside requests. Sume builds no playable, but can shrink the clip inside one.

5 min readSume
All posts

A TikTok playable ad is a .zip file smaller than 5 MB after compression, with an index.html and a config.json in the first-level directory (read 2026-10-03). Sume does not build that package. Its docs list image, audio and video outputs, not an HTML5 playable, so the honest split is: you write the HTML and JavaScript, and Sume can make the small video or still assets that go inside the zip.

The size cap is the part Sume helps with. Because a playable cannot fetch anything from outside, every asset has to ride in that 5 MB.

What TikTok's playable page requires

TikTok's help page lists the packaging, the configuration and the restrictions. The orientation setting in config.json takes three values: 0 for either landscape or portrait, 1 for portrait only and 2 for landscape only. The restrictions rule out the usual ways to cheat a size limit: no mraid.js format, no dynamic materials from external networks, no JavaScript redirects and no HTTP requests (read 2026-10-03). The page also names an SDK script and the call window.openAppStore(); for sending the player to the store.

Playable ad facts from TikTok Ads Help, read 2026-10-03
ItemTikTok's valueImplication
Package.zip, under 5 MB after compressionBudget every asset against 5 MB total
Entry fileindex.html in the first-level directoryNot inside a subfolder
Configconfig.json in the first-level directorySet orientation 0, 1 or 2
NetworkNo HTTP requests, no external dynamic materialsBundle video and images in the zip
RedirectsNo JavaScript redirectsUse the SDK store call
ObjectiveApp PromotionNot for web conversion campaigns
Placement and OSTikTok only; iOS and AndroidNo Pangle placement

Where a clip fits inside 5 MB

If your playable shows a gameplay or demo loop, that loop is likely the biggest file in the zip. Sume's video trim takes one Sume-hosted clip and returns a new MP4 with only the range you ask for. Three of its fields shrink a file without touching codecs: a short duration, a smaller output of width, height and fps (256 to 2160 pixels, fps 24, 25, 30 or 60, and only with the default exact precision), and audio: "drop" when the playable carries its own sound.

Sume exposes no bitrate or CRF field; the docs say the server compiles ffmpeg and rejects client codec fields. So you cannot dial a target size. You choose length, resolution and frame rate, then measure the result and repeat if it is still too big. A trim costs $0.02 per job (confirm in GET /v1/catalog).

The script below cuts a 4-second, 540x960, 24 fps, silent loop and prints the byte size of the result against a budget you set for the video, leaving room in the zip for HTML, code and images.

import os, time, requests
BASE = "https://api.sume.com"
H = {"Authorization": f"Bearer {os.environ['SUME_API_KEY']}"}

def run(path, body, key):
    r = requests.post(BASE + path, headers={**H, "Idempotency-Key": key}, json=body)
    r.raise_for_status()
    job = r.json()["request_id"]
    while True:
        s = requests.get(f"{BASE}/v1/jobs/{job}/status", headers=H).json()["status"]
        if s in ("completed", "failed", "canceled"):
            break
        time.sleep(3)
    return s, (requests.get(f"{BASE}/v1/jobs/{job}/result", headers=H).json() if s == "completed" else None)

BUDGET = 3_000_000  # bytes for the video; keep the rest of 5 MB for code and images
status, res = run("/v1/video-trim", {
    "video_url": "https://media.sume.com/artifacts/artf_demo/demo.mp4",
    "start": 2, "duration": 4, "audio": "drop",
    "output": {"width": 540, "height": 960, "fps": 24}}, "playable-loop-001")
if res:
    size = int(requests.head(res["video_url"], allow_redirects=True).headers["Content-Length"])
    print(res["video_url"], size, "OK" if size <= BUDGET else "too big, shorten or shrink")
else:
    print(status)

A review habit before you upload

Rebuild the zip from a clean folder every time, so a stray file from an earlier test does not inflate the size or break the first-level layout. Check three things before you submit: the compressed size is under 5 MB, index.html and config.json sit at the top of the archive rather than inside a folder, and nothing in your code tries to load a font, script or image from a web address, because the page says HTTP requests are not allowed (read 2026-10-03).

If the video is still too large after you shorten it, cut the frame rate first, then the width and height, and only then the length, because a playable usually needs enough duration to show the whole interaction. Keep each trimmed clip as a separate version so you can compare sizes without redoing the edit. Sume's jobs and results page explains how to poll a job and read its result, which is the same pattern for every attempt.

What to do instead of a playable

A playable is only for the App Promotion objective on the TikTok placement. If you need a video ad for the same app, TikTok's normal in-feed video specs apply instead, and Sume's trim and caption endpoints handle those. For the playable itself, keep Sume's clip as one asset and test the zip in TikTok's own preview before submitting, since neither Sume nor this page can tell you how a given build behaves on a device.

Sources

Related posts

More in Use cases

All Use cases posts

Written by Sume