Find and rank the best thumbnail frames in a video with the Mux Robots API, and steer which kinds of frames score highest. Use this workflow to pick poster images or thumbnails for your video gallery.
Find the strongest thumbnail frames in a video. Mux samples frames across a Mux asset, scores each one for thumbnail quality, and returns the top candidates ranked best to worst with per-category subscores and a short description of each frame. It's useful for auto-selecting a poster image, building a thumbnail picker, or A/B testing thumbnail options. See the Find Best Thumbnails API referenceAPI for the full endpoint specification. See Mux Robots pricing for unit costs.
Check the shared workflow prerequisites before creating a job.
Find best thumbnails requires a video asset in the ready state. Audio-only assets are not supported, since the workflow scores sampled video frames.
find-best-thumbnails jobcurl https://api.mux.com/robots/v1/jobs/find-best-thumbnails \
-H "Content-Type: application/json" \
-X POST \
-d '{
"parameters": {
"asset_id": "YOUR_ASSET_ID",
"max_thumbnails": 3
}
}' \
-u ${MUX_TOKEN_ID}:${MUX_TOKEN_SECRET}This request is asynchronous. The POST returns immediately with the job in pending status and does not include results. We strongly recommend listening for the robots.v1.job.find_best_thumbnails.completed webhook. The payload contains the full completed job, so no follow-up API call is needed. If webhooks aren't an option, you can poll GET /robots/v1/jobs/find-best-thumbnails/{JOB_ID} with the id from the response until the status is completed.
| Parameter | Type | Description |
|---|---|---|
asset_id | string | Required. The Mux asset ID of the video asset to pull thumbnail candidates from. |
max_thumbnails | integer | Optional. Maximum number of candidate thumbnails to return, from 1 to 5. Defaults to 5. |
output_steering | object | Optional. Curated controls that guide how candidates are scored without changing the response schema. See Output steering. |
update_asset_thumbnail | boolean | Optional. When true, the highest-scoring thumbnail's timestamp is written to the asset's thumbnail_time once the job completes, making that frame the asset's default thumbnail. Defaults to false. See Set the asset's default thumbnail. |
Use output_steering when you want best-effort control over which frames score highest. These fields guide the workflow but do not guarantee exact output. All output_steering fields are optional; include only the fields you need.
| Field | Type | Description |
|---|---|---|
selection_strategy | string | Preferred kind of thumbnail candidate to favor. Supported values: face_or_action, clean_composition, high_contrast, brand_safe, and campaign_thumbnail. |
looking_for | string | Open-ended description of what you're explicitly looking for in candidate thumbnails. |
audience | string | Intended audience used to guide scoring. |
campaign_style | string | Short description of the campaign or channel thumbnail style to prefer when supported by the frame. |
scoring_priorities | array of strings | Rubric criteria to emphasize in close calls, matching the subscores. Supported values: focus, face_or_action, composition, contrast_color, and brand_fit. |
scope | object | Time window in seconds on the asset timeline, with start_time and end_time. Frames are sampled only from within this window, while returned timestamps remain absolute asset timestamps. Omit start_time to begin at the asset start, and end_time to continue through the asset end. The window must contain at least one video frame. |
{
"parameters": {
"asset_id": "YOUR_ASSET_ID",
"max_thumbnails": 5,
"output_steering": {
"selection_strategy": "face_or_action",
"looking_for": "a clear shot of the host mid-demo with the product visible",
"audience": "developers evaluating a video API",
"campaign_style": "clean, high-contrast tech explainer",
"scoring_priorities": ["focus", "contrast_color"]
}
}
}By default, the workflow only returns candidates and leaves the asset untouched. Set update_asset_thumbnail to true to have Mux write the top-scoring frame's timestamp to the asset's thumbnail_time when the job completes, so requests to https://image.mux.com/{PLAYBACK_ID}/thumbnail.jpg return that frame.
{
"parameters": {
"asset_id": "YOUR_ASSET_ID",
"max_thumbnails": 3,
"update_asset_thumbnail": true
}
}The write is best-effort. The outcome is reported in outputs.asset_updates.thumbnail.status as updated or failed. If it fails, the job still completes and returns its ranked candidates, and you can set thumbnail_time yourself from outputs.best_thumbnails.
A new default thumbnail will appear for some clients sooner than others, depending on their local cache settings. See Set a default thumbnail for how the change propagates, and for how to set or clear thumbnail_time directly.
The outputs object is included in the job once its status is completed. You'll receive it on the robots.v1.job.find_best_thumbnails.completed webhook (recommended), or you can fetch it with GET /robots/v1/jobs/find-best-thumbnails/{JOB_ID}. It contains:
| Field | Type | Description |
|---|---|---|
best_thumbnails | array | Selected candidate thumbnails, ordered best to worst by overall quality score. |
best_thumbnails[].time | number | Frame timestamp in seconds on the asset timeline. Fractional seconds are supported. |
best_thumbnails[].overall | number | Overall quality score from 0 to 1. |
best_thumbnails[].description | string | AI-generated description of the frame content. |
best_thumbnails[].alt_text | string | Suggested alternative text for the frame, for use with the thumbnail image in an accessible UI. |
best_thumbnails[].subscores | object | Per-category quality scores, each from 0 to 1. |
best_thumbnails[].subscores.focus | number | Sharpness and focus quality. |
best_thumbnails[].subscores.face_or_action | number | Presence of faces or dynamic action. |
best_thumbnails[].subscores.composition | number | Visual composition and framing quality. |
best_thumbnails[].subscores.contrast_color | number | Color contrast and vibrancy. |
best_thumbnails[].subscores.brand_fit | number | Alignment with brand guidelines. |
asset_updates | object | Outcome of the asset write requested with update_asset_thumbnail. Omitted when no outcome was recorded. |
asset_updates.thumbnail.status | string | updated when the asset's thumbnail_time was written, or failed when the write did not succeed. |
Each candidate is identified by its time, in seconds. To render the frame as an image, pass it to the image API: https://image.mux.com/{PLAYBACK_ID}/thumbnail.jpg?time={TIME}.
This example uses a five-minute asset, consuming 5,250 units. It is the job delivered in the robots.v1.job.find_best_thumbnails.completed webhook's data field and returned by GET /robots/v1/jobs/find-best-thumbnails/{JOB_ID}:
{
"data": {
"id": "rjob_thumb123",
"workflow": "find-best-thumbnails",
"status": "completed",
"units_consumed": 5250,
"created_at": 1746403200,
"updated_at": 1746403260,
"parameters": {
"asset_id": "YOUR_ASSET_ID",
"max_thumbnails": 3
},
"outputs": {
"best_thumbnails": [
{
"time": 42,
"overall": 0.91,
"description": "The host faces the camera mid-sentence with the product dashboard sharp behind them.",
"alt_text": "Host speaking to the camera in front of a product dashboard.",
"subscores": {
"focus": 0.95,
"face_or_action": 0.92,
"composition": 0.88,
"contrast_color": 0.86,
"brand_fit": 0.9
}
},
{
"time": 118.5,
"overall": 0.78,
"description": "A wide shot of the upload screen with the cursor on the upload button.",
"alt_text": "Upload screen with the cursor on the upload button.",
"subscores": {
"focus": 0.82,
"face_or_action": 0.6,
"composition": 0.85,
"contrast_color": 0.8,
"brand_fit": 0.83
}
}
]
}
}
}