LinkedIn video ad thumbnail: pick a still with video frames

LinkedIn's initializeUpload takes an optional thumbnail. Pull up to 24 candidate stills from your MP4 with POST /v1/video-frames and pick one.

5 min readSume
All posts

To choose a thumbnail for a LinkedIn video ad, pull candidate stills from the finished MP4 with POST /v1/video-frames (up to 24 frames per call, at times you name) and pick the best one. LinkedIn's Videos API page says uploadThumbnail defaults to false, that a system-generated thumbnail may be added if you do not upload one, and that for an ads video a requested thumbnail must process successfully for the video to be servable (read 2026-10-06). So a thumbnail you chose beats one you did not, but it is also one more thing that can block serving.

Sources: LinkedIn Videos API, Microsoft Learn (read 2026-10-06) and Video frames: stills at times you name. That page does not state thumbnail size or format rules, so check them on LinkedIn's own pages before you upload.

How to pick the moments

Ask for six or eight times spread over the clip, not only the start. The first frame of a video is often a fade or a logo card. Frames at 1, 2.5, 4, 6, 8 and 10 seconds give you a face, a product and a text card to compare. Each value must be at least 0 and less than the clip's duration, or the worker fails with frame_time_out_of_range and tells you the probed duration. The source must be on media.sume.com and at most 300 seconds.

The call

A submit always returns 202 because the family is async only; do not send mode: "sync". format is jpeg by default or png for lossless inspection, and max_edge clamps the long edge between 16 and 2160 pixels. Leave it off to keep the source frame size, which is what you want for a thumbnail that will be scaled later. Poll GET /v1/video-frames/{id} until resource_status is ready, then read frames[], each with t, url, width and height.

import json, os, time, urllib.request

H = {"Authorization": "Bearer " + os.environ["SUME_API_KEY"],
     "Content-Type": "application/json"}

def call(method, url, body=None, extra=None):
    data = json.dumps(body).encode() if body else None
    req = urllib.request.Request(url, data=data, method=method,
                                 headers={**H, **(extra or {})})
    with urllib.request.urlopen(req) as r:
        return json.load(r)

sub = call("POST", "https://api.sume.com/v1/video-frames", {
    "video_url": "https://media.sume.com/artifacts/artf_demo/ad-30s.mp4",
    "at": [1, 2.5, 4, 6, 8, 10],
    "format": "jpeg",
}, {"Idempotency-Key": "li-thumb-001"})
rid = sub["request_id"]
while True:
    res = call("GET", "https://api.sume.com/v1/video-frames/" + rid)
    if res.get("resource_status") in ("ready", "failed"):
        break
    time.sleep(3)
for f in res.get("video_frames", res).get("frames", []):
    print(f["t"], f["url"])

What to look for in a thumbnail

Choose the frame where the subject is sharp, the face is not mid-blink and the text, if any, is whole. Avoid frames with a burned caption cut off mid-word. A frame whose url is null means that one instant failed; the job still succeeds for the others, so read the rest and request the missing time again.

Thumbnail facts from LinkedIn's Videos API page and Sume's video frames page, read 2026-10-06.
ItemSourceDetail
uploadThumbnailLinkedInDefault false; system thumbnail may be added
Ads video with thumbnailLinkedInThumbnail must process to be servable
at[]Sume1 to 24 times, each in the clip
Source lengthSumeUp to 300 seconds
format / max_edgeSumejpeg or png; 16 to 2160 px

What to do with the picked frame

Download it and open it at full size. In LinkedIn's flow you set uploadThumbnail to true in initializeUpload, and the response then carries a thumbnailUploadUrl. The page's example uploads the image to that URL with a media-type-family: STILLIMAGE header and an application/octet-stream content type, and expects a 201 Created back. If none of the stills works, the clip itself probably needs a stronger opening; trim a different start and run the extraction again. Because frames are durable media.sume.com images, you can keep the chosen one with the ad's record.

What the frames cost

Video frames does not have a flat price. Sume bills the job by its Modal compute, container seconds times the Modal list rate times 1.25, plus the platform fee, and the bill is never more than the hold reserved at submit. Pulling six frames from a short ad is a small job, so asking for a generous spread of candidates costs little compared with uploading a poor thumbnail to an ad that must serve.

Sources

Related posts

More in Use cases

All Use cases posts

Written by Sume