Video Frame Extractor - Keyframes, Scenes, Thumbnails for AI avatar

Video Frame Extractor - Keyframes, Scenes, Thumbnails for AI

Pricing

from $1.60 / 1,000 frames

Go to Apify Store
Video Frame Extractor - Keyframes, Scenes, Thumbnails for AI

Video Frame Extractor - Keyframes, Scenes, Thumbnails for AI

Extract frames from any video file or YouTube, TikTok and Instagram link: scene-change keyframes, frames at exact timestamps, thumbnails or one every N seconds, plus a contact sheet. Image links ready for AI vision. $2 per 1,000 frames.

Pricing

from $1.60 / 1,000 frames

Rating

0.0

(0)

Developer

Kanto Labs

Kanto Labs

Maintained by Community

Actor stats

0

Bookmarked

215

Total users

118

Monthly active users

21 hours ago

Last modified

Security

Limited permissions

Share

Video Frame Extractor - Keyframes, Scene Changes & Thumbnails from Any Video

Extract frames from a video file in one API call: the keyframes at real scene changes, a fixed number of evenly spaced frames, one frame every N seconds, or the best video thumbnail. Every frame gets a public image link and a timestamp, each video gets a contact sheet (a storyboard grid) and its metadata (duration, resolution, fps, codecs).

Built for AI pipelines: vision models such as GPT-4o, Claude and Gemini read images, not video - this actor gives them the right frames in one call. No ffmpeg install, no servers, no API key. You pay only per frame extracted ($2 per 1,000 frames).

What does Video Frame Extractor do?

Video Frame Extractor downloads each video file you link to, reads its metadata (duration, resolution, fps, codecs) and saves still frames as JPG, PNG or WEBP with a timestamp: the real scene changes, a fixed number of evenly spaced frames, or one frame every N seconds. Every video also gets a contact-sheet grid. You get one dataset row per video with a public link to every frame.

Use cases

  • Video understanding with LLMs - send 8-20 keyframes to a vision model to summarize, tag or moderate a video.
  • Thumbnails - pick the best still for a video page, a course lesson or a product clip.
  • Storyboards and contact sheets - review hours of footage at a glance.
  • Ad and creative research - break your own or licensed ad videos into scenes to compare hooks and cuts.
  • Dataset building - sample frames from recorded footage to train or evaluate computer-vision models.
  • Quality checks - spot wrong intros or missing slates in a batch of exports (turn skipBlankFrames off to also see black frames).

Who is it for?

  • AI engineers sending video to vision models (GPT, Claude, Gemini) that read images, not video.
  • Video and content teams picking thumbnails and making storyboards.
  • Ad and creative researchers breaking their own or licensed ads into scenes.
  • ML and data teams sampling frames from footage for datasets.
  • QA teams checking a batch of video exports at a glance.

Frame modes

ModeWhat you getGood for
auto (default)First frame + the strongest scene cuts, topped up with evenly spaced frames to reach frameCountA representative storyboard of any video
scenesOnly real scene changes (ffmpeg scene score above the threshold)Edited videos, trailers, ads
countExactly N evenly spaced framesConsistent sampling for AI
intervalOne frame every N secondsLong recordings, lectures, CCTV-style footage
timestampsOne frame at each exact time you list (90, 1:30, 00:01:30.500)Moments you already know: a timecode from a transcript, a highlight, a chapter start
keyframesThe video's real keyframes (I-frames chosen by the encoder), up to maxFramesPerVideoLossless-quality stills, shot starts in edited videos, dataset sampling

Every mode also takes an optional startTime / endTime window (seconds or HH:MM:SS) to skip an intro or work on one part of a long video. Video platform links (YouTube, TikTok, Instagram, Facebook, X) work in every mode - see below.

Best thumbnails mode - auto-pick the best video thumbnail

Set mode to bestThumbnails and the actor picks the frames a person would choose as the video thumbnail, instead of returning a storyboard. Per video it scores about 40 candidate frames (the encoder's keyframes - the sharpest pictures in the file, placed at scene cuts - or evenly spaced frames for files with few keyframes) on:

  • Sharpness (Laplacian variance; the face counts more than the background), exposure, contrast and colourfulness
  • Faces (YuNet face detector): size, looking at the camera, eyes open, mouth not covered (a hand or object in front of it)
  • Composition (rule of thirds / centred subject), with a small penalty for logos and end credits

Black, blank, blurry and duplicate frames are dropped, the best thumbnailCount frames (default 3, max 10) are kept with a minimum time gap so they are not three copies of one shot, and each one is checked against the next 0.75 s of video (a blink or motion blur on the chosen frame is swapped for a better neighbour).

Each thumbnail comes with its rank, thumbnailScore (0-100), a scoreBreakdown and optional free crops (thumbnailCrops: 16:9 for YouTube, 9:16 for Shorts / Reels / TikTok, 1:1, 4:5) centred on the faces or the main subject. Thumbnails are full resolution up to maxWidth (1280 by default - the YouTube thumbnail size; 0 = original).

{
"videoUrls": ["https://your-bucket.s3.amazonaws.com/uploads/episode-12.mp4"],
"mode": "bestThumbnails",
"thumbnailCount": 3,
"thumbnailCrops": ["16:9", "9:16", "1:1"]
}

Cost: each returned thumbnail is one frame at the normal frame price - 3 thumbnails = $0.006 per video, so 1,000 videos = $6. The ~40 candidates that were scored and dropped, the crops and the contact sheet are free. The scoring is automatic and cannot judge everything a person does (an odd mid-word expression, on-screen text you would rather avoid), which is why it returns several ranked options rather than one.

How to use it

  1. Click Try for free.
  2. Paste direct links to your video files into Video URLs, one per line.
  3. Pick a Mode: auto, scenes, count or interval.
  4. Click Start, then open the frame links and the contact sheet from the results, or read them through the API.

Input

FieldWhat it doesDefault
videoUrlsDirect links to video files (MP4, MOV, WEBM, MKV, AVI, TS...) or public YouTube / TikTok / Instagram / Facebook / X video links2 sample videos
modeauto, scenes, count, interval, timestamps, keyframes or bestThumbnailsauto
timestampsTimes for timestamps mode: 90, 90.5, 1:30, 00:01:30.500-
startTime / endTimeOnly take frames inside this window (every mode)whole video
frameCountFrames per video for auto / count12
intervalSecondsSeconds between frames for interval10
sceneThresholdScene-cut sensitivity (lower = more cuts)0.3
maxFramesPerVideoCap for scenes / interval / keyframes50
skipBlankFramesStep past black / fade-to-black / single-colour frames (up to 2 s forward); blank frames are dropped and not chargedtrue
imageFormatjpg, png or webpjpg
maxWidthDownscale wider frames (0 = original resolution)1280
contactSheetAlso save a grid image of all frames (free)true
maxVideoSizeMbSkip larger files500
includeSummaryRowOne summary row per video + one frame row per image (recordType) instead of one row per videofalse
{
"videoUrls": ["https://your-bucket.s3.amazonaws.com/uploads/demo.mp4"],
"mode": "count",
"frameCount": 8,
"maxWidth": 1024
}

Exact moments of one part of a video:

{
"videoUrls": ["https://www.youtube.com/watch?v=jNQXAC9IVRw"],
"mode": "timestamps",
"timestamps": ["0:03", "8.5", "00:00:15.250"]
}

Timestamps, keyframes and a start/end window

  • timestamps returns one frame at each time you list, in time order, and adds requestedTime (the time as you typed it) to each frame. Times after the end of the video, or outside the window, are listed in timestampsSkipped with the reason and cost nothing. Unreadable values get one free notice row (invalidTimestamps) and the run goes on with the valid ones. Exact moments are kept even when the frame is black. Filling timestamps while mode is left on auto switches to this mode.
  • keyframes returns the encoder's keyframes (I-frames) - every one of them up to maxFramesPerVideo, or an even spread over all of them when there are more. keyframesInVideo says how many there were. A black keyframe is dropped, free.
  • startTime / endTime limit any mode, bestThumbnails included, to one part of the video; the row gets a window field. A start time after the end of the video gives a free error row; an invalid window stops the run before anything is charged.

Public single-video links work as well as files: YouTube videos and Shorts, TikTok videos (also vm.tiktok.com share links), Instagram reels and posts, Facebook videos and reels, and X posts with a video. They are read logged out through Apify Proxy (no cookies, no login, no captcha solving), at up to 1080p. The row gets platform, sourceUrl, videoId, title, author, authorHandle, caption, uploadDate, viewCount and likeCount. Private, age-restricted, region-blocked, removed or login-only videos, live streams, playlists, channels and image posts return a free error row with the reason. The price is the same frame-extracted event as for files - nothing extra per video or per download. Only use frames you have the right to use.

  • Only the size you asked for is fetched. Frames are never wider than "Max frame width" (1280 px by default), so the actor picks the smallest copy of the video that is still at least that wide - for a 1080p YouTube video that is the 1280x720 copy. The saved frames have the same size as before; the row's width / height / fileSizeBytes describe the copy that was read. Set "Max frame width" to 0 for the largest copy (up to 1080p).
  • Long YouTube videos are not downloaded when only a few moments are needed. In timestamps, count and interval mode, and in any mode with startTime / endTime, the frames are read straight from YouTube's server - only the seconds around each frame are transferred - when that is far less than the whole file. The frames are the same as from a full download. Example: 3 timestamps from a 10-minute video read a few MB instead of the whole file.
  • Modes that must look at every frame (auto and scenes without a window, keyframes, bestThumbnails) download the file.

Output

One dataset item per video. Real output from a test run on the Sintel trailer (the frames array is trimmed to 2 of its 12 frames; sceneScore is set when the frame is a detected cut):

{
"videoUrl": "https://media.w3.org/2010/05/sintel/trailer.mp4",
"fileName": "trailer.mp4",
"fileSizeBytes": 4372373,
"durationSeconds": 52.21,
"width": 854,
"height": 480,
"fps": 24,
"videoCodec": "h264",
"audioCodec": "aac",
"bitrateKbps": 669,
"container": "mov",
"mode": "auto",
"scenesDetected": 6,
"frameCount": 12,
"frames": [
{
"index": 4,
"timestampSeconds": 10.877,
"timecode": "00:00:10.877",
"sceneScore": null,
"width": 854,
"height": 480,
"bytes": 5121,
"key": "b670602f-trailer-004-000010877ms.jpg",
"url": "https://api.apify.com/v2/key-value-stores/YuY6xATAnCD87Tv00/records/b670602f-trailer-004-000010877ms.jpg?signature=..."
},
{
"index": 5,
"timestampSeconds": 16.5,
"timecode": "00:00:16.500",
"sceneScore": 1,
"width": 854,
"height": 480,
"bytes": 29030,
"key": "b670602f-trailer-005-000016500ms.jpg",
"url": "https://api.apify.com/v2/key-value-stores/YuY6xATAnCD87Tv00/records/b670602f-trailer-005-000016500ms.jpg?signature=..."
}
],
"blankFramesSkipped": 0,
"contactSheetUrl": "https://api.apify.com/v2/key-value-stores/YuY6xATAnCD87Tv00/records/b670602f-trailer-contact-sheet.jpg?signature=...",
"processingTimeMs": 4841,
"error": null
}

The frame images and the contact sheet are stored in the run's key-value store; the url links open without an Apify login, so you can pass them straight to an AI model or a spreadsheet.

One row per frame (optional)

Set "includeSummaryRow": true to get flat rows instead: per video one summary row (every video field above, plus frameUrls, without the frames list), then one frame row per image (index, timestampSeconds, timecode, url, width, height, sceneScore, plus videoUrl, fileName and - for platform links - platform, videoId, title). Failed videos, timeouts and input notices are one free notice row each. Every row has recordType (summary / frame / notice), so an n8n, Make or Zapier step can filter on it. Same frames, same price - only the row shape changes. Off by default.

How much does it cost to extract frames from a video?

Pay per event: $0.002 per frame ($2 per 1,000 frames), plus Apify's tiny start fee of $0.00005 per run. The contact sheet, the metadata, skipped blank frames and videos that fail to download or decode are free. Apify Silver and Gold plan users pay less per frame ($0.0018 / $0.0016).

Worked example: a 10-minute video at 1 frame every 10 s = 60 frames = $0.12.

{
"videoUrls": ["https://your-bucket.s3.amazonaws.com/uploads/webinar.mp4"],
"mode": "interval",
"intervalSeconds": 10,
"maxFramesPerVideo": 60
}

(interval mode stops at maxFramesPerVideo, which is 50 by default - raise it when you want every interval frame of a longer video.)

More examples:

  • 100 product videos x 8 frames each (count mode) = 800 frames = $1.60
  • One 2-hour lecture, one frame every 60 s = 120 frames = $0.24 (set maxFramesPerVideo to 120)
  • 1,000 videos x 3 best thumbnails = 3,000 frames = $6.00
  • The default demo run ({} input, 2 sample videos x 12 frames) = 24 frames = $0.048

Set Maximum cost per run and the actor never extracts more frames than that budget allows.

Use it in n8n, Make, Zapier or any API

The actor is a plain HTTP API, so every automation tool can call it. The simplest call starts a run, waits for it and returns the dataset rows in one request:

curl -X POST "https://api.apify.com/v2/acts/kantolabs~video-frame-extractor/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"videoUrls": ["https://media.w3.org/2010/05/sintel/trailer.mp4"], "mode": "count", "frameCount": 8}'
  • n8n: add the official Apify node, operation Run actor, Actor kantolabs/video-frame-extractor, input {"videoUrls": ["{{ $json.videoUrl }}"], "mode": "count", "frameCount": 8}, wait for finish on. Then an Apify Get dataset items node with the run's defaultDatasetId, and a Split Out node on frames gives one n8n item per frame with url, timestampSeconds and timecode. That replaces the OpenCV Code node of the usual "describe a video with GPT-4o" templates, which n8n Cloud cannot run. On self-hosted n8n without the Apify node, an HTTP Request node (POST to the URL above) does the same.
  • Make: the Apify app's Run an Actor module (Actor kantolabs/video-frame-extractor, "run synchronously" on), then Get Dataset Items and an Iterator over frames.
  • Zapier: the Apify app's Run Actor action, then use the frame url fields in the next step.
  • Webhooks and schedules: Apify can call your webhook when a run finishes, or run the actor on a schedule.

Use with AI vision (GPT-4o, Claude, Gemini)

Each frame url is a public HTTPS link to a JPG (no Apify login needed), so you can hand it straight to a vision model. 8-20 frames at the default 1280 px width are enough for most "what happens in this video" questions; count mode gives an even spread, auto mode adds the real scene changes.

from apify_client import ApifyClient
from openai import OpenAI
apify = ApifyClient("YOUR_APIFY_TOKEN")
run = apify.actor("kantolabs/video-frame-extractor").call(run_input={
"videoUrls": ["https://media.w3.org/2010/05/sintel/trailer.mp4"],
"mode": "auto",
"frameCount": 10,
})
item = next(apify.dataset(run.default_dataset_id).iterate_items())
content = [{"type": "text", "text": "These are frames of one video in time order. Describe what happens."}]
for f in item["frames"]:
content.append({"type": "text", "text": f"Frame at {f['timecode']}"})
content.append({"type": "image_url", "image_url": {"url": f["url"]}})
reply = OpenAI().chat.completions.create(model="gpt-4o", messages=[{"role": "user", "content": content}])
print(reply.choices[0].message.content)
  • Claude: send each frame as {"type": "image", "source": {"type": "url", "url": frame_url}} in the message content.
  • Gemini: download each frame's bytes and pass them inline as image/jpeg parts (or use the contact sheet as one image).
  • Fewer tokens: one contactSheetUrl image (a labelled grid of all frames) is often enough for a quick summary.
  • AI agents (MCP): connect the Apify MCP server (mcp.apify.com) to Claude, ChatGPT, Cursor or VS Code and the agent can find and call this actor itself whenever it needs to see what is inside a video file.

Integrations and API

Run it from your own code with the official apify-client package (pip install "apify-client>=3"; Python shown, JavaScript works the same way):

from apify_client import ApifyClient
client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("kantolabs/video-frame-extractor").call(run_input={
"videoUrls": [
"https://media.w3.org/2010/05/sintel/trailer.mp4"
],
"mode": "count",
"frameCount": 8
})
for item in client.dataset(run.default_dataset_id).iterate_items():
print(item)

JavaScript: new ApifyClient({ token }).actor('kantolabs/video-frame-extractor').call(input) from the apify-client npm package.

Limits

  • Platform links: public, single videos only, read logged out (see above). LinkedIn, Vimeo, Twitch and Douyin pages are refused.
  • A run that is about to reach its timeout stops cleanly: the videos it could not finish get a free row with skipReason: "timeout".
  • 500 MB per video by default, up to 4 GB. Up to 200 frames per video.
  • scenes mode scans every frame, so long videos take longer; count and interval decode only the frames they need.
  • No audio or transcription - images and metadata only.
  • count never returns more frames than the video really has (a 1-second clip at 30 fps gives at most 30), so you never pay for duplicates.
  • Google Drive / Dropbox / OneDrive share links open a web page, not the file: use their direct-download link (Dropbox: ?dl=1).

How do I extract frames from a video?

Paste a direct link to the video file into Video URLs, pick a mode and click Start. auto gives a 12-frame storyboard (scene changes + even spacing), scenes gives only the real cuts (the keyframes of an edited video), count gives exactly N evenly spaced frames and interval gives one frame every N seconds. Each frame comes back as an image link with its timestamp. From code, use the API examples above.

How do I get a thumbnail from a video?

Use bestThumbnails mode. The actor scores about 40 candidate frames for sharpness, exposure, faces, open eyes and composition and returns the best 3 (up to 10), ranked, with optional 16:9, 9:16, 1:1 and 4:5 crops. 3 thumbnails cost $0.006 per video. If you just need the first frame, use count mode with frameCount: 1 (it returns the middle of the video) or scenes mode with maxFramesPerVideo: 1 (the first frame).

How to convert a video to images?

Use interval mode for one image every N seconds (for example intervalSeconds: 1 = one image per second, up to maxFramesPerVideo, max 200 per video) or count mode for a fixed number. Pick jpg, png (lossless) or webp, and set maxWidth to 0 to keep the original resolution. All images are saved in the run's key-value store, so you can download them in one go from the Storage tab or the API.

Which video formats work?

Anything ffmpeg can decode: MP4/H.264, H.265/HEVC, VP9/WEBM, AV1, MOV, MKV, AVI, MPEG-TS, M4V and more.

How big can the video be?

500 MB by default, up to 4 GB with maxVideoSizeMb. Long videos in scenes mode take longer because every frame is scanned for cuts; count and interval only decode the frames they need.

Will I get (and pay for) black frames?

No, by default. Many videos fade in from black and scene cuts often pass through a black frame. With skipBlankFrames on (the default), a black or single-colour frame is replaced by the first frame with content in the next 2 seconds; if there is none, it is dropped and not charged (blankFramesSkipped counts them). Title cards - text on black - are kept. Turn the option off if you need frames at the exact planned timestamps.

Yes. Paste a public YouTube video or Short, TikTok, Instagram reel, Facebook video or X post link into Video URLs and use any mode. The video is read logged out; private, age-restricted or blocked videos return a free error row.

Something is not working?

Open an issue on the Issues tab - it is answered quickly.

  • Video to LLM Context - keyframes plus the spoken transcript per scene in one AI-ready text block for ChatGPT, Claude or Gemini. Use it when the audio matters too.
  • Video Scene Detection & Shot List - every cut, dissolve and fade with timecodes, a CSV/EDL shot list and optional clips per scene. Use it for editing and ad teardowns.
  • AI Image Upscaler - Real-ESRGAN 2x/4x to sharpen small frames or thumbnails.
  • Video Ad Creative Breakdown - hook, shot list with keyframes, transcript, pacing and CTA for ad and short-form video files, plus a Markdown creative brief.
  • AI Image Caption & Alt Text Generator - captions, alt text, tags and a scene summary. Pick this actor's dataset there to caption every extracted frame with its timestamp.