Video Frame Extractor - Keyframes, Scenes, Thumbnails for AI
Pricing
from $1.60 / 1,000 frames
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
Maintained by CommunityActor stats
0
Bookmarked
215
Total users
118
Monthly active users
21 hours ago
Last modified
Categories
Security
Limited permissionsShare
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
skipBlankFramesoff 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
| Mode | What you get | Good for |
|---|---|---|
auto (default) | First frame + the strongest scene cuts, topped up with evenly spaced frames to reach frameCount | A representative storyboard of any video |
scenes | Only real scene changes (ffmpeg scene score above the threshold) | Edited videos, trailers, ads |
count | Exactly N evenly spaced frames | Consistent sampling for AI |
interval | One frame every N seconds | Long recordings, lectures, CCTV-style footage |
timestamps | One 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 |
keyframes | The video's real keyframes (I-frames chosen by the encoder), up to maxFramesPerVideo | Lossless-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
- Click Try for free.
- Paste direct links to your video files into Video URLs, one per line.
- Pick a Mode:
auto,scenes,countorinterval. - Click Start, then open the frame links and the contact sheet from the results, or read them through the API.
Input
| Field | What it does | Default |
|---|---|---|
videoUrls | Direct links to video files (MP4, MOV, WEBM, MKV, AVI, TS...) or public YouTube / TikTok / Instagram / Facebook / X video links | 2 sample videos |
mode | auto, scenes, count, interval, timestamps, keyframes or bestThumbnails | auto |
timestamps | Times for timestamps mode: 90, 90.5, 1:30, 00:01:30.500 | - |
startTime / endTime | Only take frames inside this window (every mode) | whole video |
frameCount | Frames per video for auto / count | 12 |
intervalSeconds | Seconds between frames for interval | 10 |
sceneThreshold | Scene-cut sensitivity (lower = more cuts) | 0.3 |
maxFramesPerVideo | Cap for scenes / interval / keyframes | 50 |
skipBlankFrames | Step past black / fade-to-black / single-colour frames (up to 2 s forward); blank frames are dropped and not charged | true |
imageFormat | jpg, png or webp | jpg |
maxWidth | Downscale wider frames (0 = original resolution) | 1280 |
contactSheet | Also save a grid image of all frames (free) | true |
maxVideoSizeMb | Skip larger files | 500 |
includeSummaryRow | One summary row per video + one frame row per image (recordType) instead of one row per video | false |
{"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
timestampsreturns one frame at each time you list, in time order, and addsrequestedTime(the time as you typed it) to each frame. Times after the end of the video, or outside the window, are listed intimestampsSkippedwith 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. Fillingtimestampswhilemodeis left onautoswitches to this mode.keyframesreturns the encoder's keyframes (I-frames) - every one of them up tomaxFramesPerVideo, or an even spread over all of them when there are more.keyframesInVideosays how many there were. A black keyframe is dropped, free.startTime/endTimelimit any mode,bestThumbnailsincluded, to one part of the video; the row gets awindowfield. A start time after the end of the video gives a free error row; an invalid window stops the run before anything is charged.
Video platform links (YouTube, TikTok, Instagram, Facebook, X)
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.
How platform links are read (fast on long videos)
- 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/fileSizeBytesdescribe 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,countandintervalmode, and in any mode withstartTime/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 (
autoandsceneswithout 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 (
countmode) = 800 frames = $1.60 - One 2-hour lecture, one frame every 60 s = 120 frames = $0.24 (set
maxFramesPerVideoto 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'sdefaultDatasetId, and a Split Out node onframesgives one n8n item per frame withurl,timestampSecondsandtimecode. 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 overframes. - Zapier: the Apify app's Run Actor action, then use the frame
urlfields 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 ApifyClientfrom openai import OpenAIapify = 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/jpegparts (or use the contact sheet as one image). - Fewer tokens: one
contactSheetUrlimage (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 ApifyClientclient = 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.
scenesmode scans every frame, so long videos take longer;countandintervaldecode only the frames they need.- No audio or transcription - images and metadata only.
countnever 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.
Can I extract frames from YouTube or TikTok links?
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.
Related Actors by Kanto Labs
- 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.