TikTok catalog feed: image hosts TikTok lists, and where Sume URLs fit

TikTok's catalog page names supported and unsupported image hosts and asks for 720x1280 video. How to prepare a video_link clip with Sume trim.

5 min readSume
All posts

TikTok's catalog page does not name media.sume.com as a supported image host, so do not assume a Sume URL will be accepted in an image_link column. The page requires image links to end in JPG or PNG, lists hosts it supports (Imgur, Imgshare.io, Photobucket, Imageshack) and hosts it does not (Google Drive, Flickr, Pinterest, 500px, Imgbb, Smugmug, Dropbox) (read 2026-10-03). The safe path is to test one product row with a Sume-hosted URL in a small upload and read TikTok's feed diagnostics before you build a full catalog on it.

The video column is easier to prepare. Here is what TikTok asks for in each field, and which parts Sume can produce.

What the catalog page specifies

The primary image_link should be at least 500 x 500 for square (1:1) carousel ads. additional_image_link accepts up to 10 image URLs in the same JPG or PNG formats, separated by commas inside double quotes. For video_link, TikTok recommends vertical 9:16, at least 720 x 1280 resolution, a bitrate of at least 516 kbps, and sound with captions included. Custom labels, custom_label_0 through custom_label_4, are limited to 100 characters (read 2026-10-03). The page gives no title or description limits.

Catalog media fields, TikTok Ads Help, read 2026-10-03
FieldTikTok's requirementWho produces it
image_linkAt least 500 x 500 for square carousel; URL ends in JPG or PNGYour image pipeline
additional_image_linkUp to 10 URLs, JPG or PNG, comma-separated in quotesYour image pipeline
video_linkVertical 9:16, at least 720 x 1280, at least 516 kbps, with sound and captionsVideo trim and captions
custom_label_0 to 4At most 100 characters eachYour feed builder
HostingImgur, Imgshare.io, Photobucket, Imageshack supported; Dropbox and Google Drive notTest before you scale

Image rows: size, count and format

Most catalog failures are boring: an image link that does not end in .jpg or .png, a URL behind a login, or a picture smaller than 500 x 500. Before you upload a feed, run a short script over every image_link and additional_image_link value that checks the extension, fetches the first bytes and reads the width and height. Count the entries in additional_image_link too, because TikTok allows up to 10 and the commas inside the quoted string are easy to break when a URL itself contains one.

Sume's image routes return Sume-hosted artifacts and accept the formats listed on the image docs, but this post does not claim a particular size for a catalog square. If you generate a product still, ask for a square result, read its size, and confirm it is at least 500 x 500 before it goes in the feed. A white-background packshot and a lifestyle angle are separate rows in additional_image_link, not one image.

Prepare the video_link clip

Video trim can cut a product clip to a short range and set the output size, with edges from 256 to 2160, so 720 x 1280 is in range. The captions TikTok asks for are separate: video captions burns them onto a public HTTPS clip. Sume's docs do not offer a bitrate field, so the 516 kbps figure is something you measure, not request; read the probe from video inspect and compare.

The loop below creates one 720 x 1280 clip per source URL, with a stable idempotency key per product so a retry does not create a second charge for the same row. Replace the URLs with clips on media.sume.com.

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)

clips = {"sku-1001": "https://media.sume.com/artifacts/artf_demo/a.mp4",
         "sku-1002": "https://media.sume.com/artifacts/artf_demo/b.mp4"}
for sku, url in clips.items():
    status, res = run("/v1/video-trim", {
        "video_url": url, "start": 0, "duration": 12,
        "output": {"width": 720, "height": 1280, "fps": 30}}, f"catalog-video-{sku}")
    print(sku, status, res and res["video_url"])

Test the hosting question yourself

Whether a particular host works is TikTok's call. Make a two-row feed, one row with a Sume media URL and one with a host from TikTok's supported list, upload it, and look at the status TikTok gives each product. The Sume media inputs page describes how Sume-hosted media is addressed; it says nothing about TikTok's catalog, so a pass here is something you observe, not something either vendor promises.

If a Sume URL is refused, keep the clip in Sume for editing and download the final file to a host TikTok accepts. Record which host passed, the date, and the product row, so the next person on the feed does not repeat the test.

Sources

Related posts

More in Integrations

All Integrations posts

Written by Sume