# Video Frame Extractor for Scene Changes and Thumbnails (`cleanscrape/video-frame-extractor`) Actor

Turn video links into still images: one per scene change, every N seconds, a set number of frames or exact timestamps. JPG, PNG or WebP with a contact sheet per video. MP4, MOV, WebM, MKV and more, from S3, Dropbox or Drive links. $2 per 1,000 frames.

- **URL**: https://apify.com/cleanscrape/video-frame-extractor.md
- **Developed by:** [CleanScrape](https://apify.com/cleanscrape) (community)
- **Categories:** Videos, Developer tools, AI
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.60 / 1,000 frames

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Video Frame Extractor for Scene Changes and Thumbnails

Turn video files into still images. Paste links to your videos and get back JPG, PNG or WebP frames with their exact times, plus a contact sheet per video. Choose how frames are picked:

- **Overview:** one frame per scene change, with quiet stretches filled in evenly. Fast at any length.
- **Precise scene changes:** every cut, including quick ones, found by looking at every frame.
- **Every N seconds**, for example one frame every 5 seconds for an AI vision model.
- **A set number of frames**, evenly spaced, for example one thumbnail per video.
- **Exact timestamps**, such as `0:42` and `1:15.5`.

Near-identical frames are left out and not charged, so a slide that stays on screen for two minutes gives you one image, not twelve. Phone videos come out upright, and variable frame rate screen recordings get exact times.

It works with MP4, MOV, WebM, MKV, AVI, MPEG-TS, OGV and animated GIF, in H.264, HEVC (also 10-bit), VP9, AV1 and other codecs FFmpeg reads, from any direct file link: your own server, a CDN, S3 or Google Cloud Storage signed links, Dropbox and Google Drive share links. This is an independent tool, maintained by CleanScrape.

### Start with a small run

The form opens with two sample videos, a 5-second clip and the 10-minute open film Big Buck Bunny (© Blender Foundation, CC BY 3.0), and saves up to 12 frames from each. Start it as it is, then paste your own links.

1. In **Video links**, add one link per line.
2. Pick **Which frames** and, if you like, **Frames per video**, image format and size.
3. Start the run. The **Frames** table shows each image with its time; the **Videos** table has one row per video with its contact sheet.

### Which link to use

| You have | Works? |
| --- | --- |
| A link that downloads the file (`…/clip.mp4`, a CDN or storage link) | Yes |
| An S3, Google Cloud Storage or Azure signed link | Yes. Its query string is hidden in the output, since signed links keep their key there |
| A Dropbox or Google Drive share link (file shared with "anyone with the link") | Yes, the Actor uses the file's download link |
| A YouTube, TikTok, Instagram, Vimeo or other video site page | No. These pages play videos but do not hand out the file. The run says so and charges nothing |
| A link that needs a login | No. Use a signed or shared link |

### How frames are picked

| Mode | Best for | How it works |
| --- | --- | --- |
| Overview (default) | Thumbnails, previews, a quick look at what a video contains | Reads only the video's keyframes, which encoders place at cuts, and keeps the ones that look different from the last. Fills the longest gaps with evenly placed frames until "Frames per video" is reached |
| Precise scene changes | Editing, shot lists, ad and trailer analysis, lecture slides | Compares every frame with the one before it and takes the first frame after each cut. Camera moves, fast action and film grain are not counted as cuts; slide changes count once the slide is still. Takes longer on long videos, so it is charged per minute of video scanned |
| Every N seconds | AI vision models, dataset building, time-lapse | A frame every N seconds from the start, up to "Frames per video" |
| A set number of frames | One thumbnail per video, storyboards | Evenly spaced; 1 frame gives the middle of the video |
| Exact timestamps | Moments you already know | Every timestamp you list, in each video. Timestamps past the end of a video are skipped and listed in the note |

**Scene change sensitivity** sets how different two moments must look: *low* for clear cuts only, *medium* (the default) for normal cuts, *high* to also catch slide changes and screen recordings, where only part of the picture changes. With *high*, a gradual change counts once the picture has settled, so camera motion and animation do not. **Start at** and **End at** limit every mode to part of the video, for example to skip an intro.

### What you get

**Frames** (one row per image):

| Column | Meaning |
| --- | --- |
| Image | Link to the image file. When your Apify storage is private, the link is signed so it still opens |
| Time, Seconds | Where the frame is in the video (`00:01:23.400` and `83.4`). This is the real time of the frame that was saved |
| Picked because | `scene-change`, `fill`, `interval`, `evenly-spaced` or `timestamp` |
| Video, Frame | Position of the video in your list and of the frame in the video |
| Width, Height, Format, Bytes | The image |

**Videos** (one row per link): outcome, length, upright width and height, rotation, codec, frame rate, whether the frame rate varies, whether it has sound, file size, frames saved, near-duplicates left out, scan minutes, the contact sheet link and a note. When a link can't be used, the note says why: a page instead of a file, an audio file, a damaged or incomplete file, a refused or missing link, or a file over your size limit.

The images are in the run's key-value store as `frame-001-0001.jpg` (video 1, frame 1) and so on, and the contact sheets as `contact-sheet-001.jpg`.

### Pricing

You pay for the frames you get and for each video read:

| What is charged | Price |
| --- | --- |
| Frame saved | $2.00 per 1,000 frames ($0.002 each) |
| Video read | $0.005 per video, plus $0.005 for each further started GB of file size |
| Scan minute (Precise scene changes only) | $0.01 per minute of 1080p video at 30 fps |

Free: near-identical frames that are left out, contact sheets, the Videos table, and links that can't be read (pages, audio files, damaged files, refused links, files over your size limit).

Precise scene changes decode every frame, so their cost follows the size of the picture: a minute of 1080p at 30 fps is one scan minute, smaller videos count less (720p at 30 fps is about half a minute, never less than a quarter), and larger ones more (4K at 30 fps is four). The total is rounded up per video and shown in the Videos table.

Examples:

| Run | Cost |
| --- | ---: |
| One thumbnail from each of 100 short videos | $0.70 |
| 20-frame overview of one 10-minute video | $0.045 |
| A frame every 5 seconds from a 10-minute video (120 frames) | $0.245 |
| Every slide change in a 1-hour 1080p lecture under 1 GB (precise, about 40 frames) | $0.685 |
| The same lecture with Overview and high sensitivity (slide changes from keyframes, about 40 frames; a moving pointer can add a few extra frames) | $0.085 |
| 1,000 frames from 50 videos | $2.25 |

Apify plan discounts apply to all three prices: 10% on Bronze, 15% on Silver and 20% on Gold and higher. Set a spending limit under Run options for large runs; the run stops cleanly before it reaches the limit and keeps everything saved so far.

### Ready-to-run examples

Each example opens with its input filled in. Paste your own video links and run it.

| Example | What it does |
| --- | --- |
| [One frame per scene of a short film](https://apify.com/cleanscrape/video-frame-extractor/examples/scene-change-frames-from-a-film) | 30 frames at the scene changes of Big Buck Bunny, with a contact sheet. |
| [One thumbnail per video](https://apify.com/cleanscrape/video-frame-extractor/examples/one-thumbnail-per-video) | A single JPG from the middle of each of four videos. |
| [A frame every 10 seconds for AI vision](https://apify.com/cleanscrape/video-frame-extractor/examples/video-frames-every-10-seconds-for-ai) | Small WebP frames every 10 seconds, with their times, ready for a vision model. |
| [Frames at exact timestamps, as PNG](https://apify.com/cleanscrape/video-frame-extractor/examples/video-frames-at-exact-timestamps) | Full-size PNG frames at five chosen moments. |
| [Every cut in a film trailer-length clip](https://apify.com/cleanscrape/video-frame-extractor/examples/precise-scene-detection) | Precise scene detection over the first 2 minutes of Sintel, a frame at every cut. |

### Use it from the API, n8n, Make or an AI agent

Python (`pip install apify-client`):

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_API_TOKEN")
run = client.actor("cleanscrape/video-frame-extractor").call(run_input={
    "videos": ["https://example.com/videos/demo.mp4"],
    "mode": "interval",
    "intervalSeconds": 5,
    "maxFrames": 60,
    "imageFormat": "webp",
    "maxWidth": 768,
})
for frame in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(frame["timecode"], frame["imageUrl"])
```

In n8n or Make, use the Apify node: run the Actor with your video link, then read the Frames dataset; each row's `imageUrl` can go straight to an image or vision step. For one thumbnail per video, use `"mode": "count", "maxFrames": 1`.

It also works as a tool in Claude, Cursor and other AI assistants that support MCP, through Apify's MCP server:

```
https://mcp.apify.com?tools=cleanscrape/video-frame-extractor
```

Then ask, for example: "Take a frame every 10 seconds from this video and describe what happens."

### Common questions

#### How large can a video be?

Up to 4 GB per file; the default limit is 2 GB, set under **Largest video file**. The run needs 1 GB of memory for each 2 GB of file (the default 4 GB of memory takes any file up to the limit). Length matters little outside Precise scene changes: in tests, a 50-frame overview of a 30-minute, 1 GB video took 49 seconds including the download. Precise scene changes read every frame: on the default memory, an hour of 1080p video takes roughly 5 to 35 minutes depending on how it was encoded. If a scan would not finish within the run's timeout, it stops early, the video is not charged, and the note says how long it needs; raise the timeout or the memory (more memory means more CPU) for long videos.

#### Why did I get fewer frames than I asked for?

"Frames per video" is a maximum. Short videos, videos with few scene changes, and near-identical frames that were left out give fewer. The video's note says what was left out. You are only charged for frames saved.

#### Are the frames full quality?

Frames are scaled down to 1280 pixels wide by default, which suits previews and AI models. Set **Largest image width** to 0 for the video's full size, and PNG for lossless images.

#### How long are the images kept?

They are stored in your Apify account like any run's results, for as long as your plan keeps run storage. Download them or copy them to your own storage if you need them longer.

#### Is it legal?

Use it on videos you own or have the right to use. This is not legal advice.

### Good to know

- Times are seconds from the start of the video.
- Rotation stored in phone videos is applied, so frames are upright; the Videos table shows the rotation it found.
- Contact sheets show up to 60 frames per video and are free.

### More tools from CleanScrape

- [Career Page Jobs Scraper for Greenhouse, Lever and Ashby](https://apify.com/cleanscrape/career-site-jobs-scraper): open jobs from company career pages, with salaries and new or closed jobs per company
- [ATP and WTA Tennis Rankings Scraper](https://apify.com/cleanscrape/tennis-rankings-scraper): current ATP and WTA singles, doubles and race rankings with points and movement
- [Flashscore Tennis Scraper](https://apify.com/cleanscrape/tennis-match-data): tennis results, statistics, point-by-point and head-to-head
- [Kick Sponsor Tracker](https://apify.com/cleanscrape/kick-sponsor-tracker): which Kick streamers promote which brands, with codes and broadcast hours
- [Whatnot Scraper](https://apify.com/cleanscrape/whatnot-scraper): Whatnot listings with prices and seller stats, and live shows
- [Whatnot Seller Tracker](https://apify.com/cleanscrape/whatnot-seller-tracker): top sellers per category, items sold, sales per day and followers over time
- [TikTok Shop Product Scraper](https://apify.com/cleanscrape/tiktok-shop-product-scraper): US TikTok Shop prices, sellers and ratings
- [Bilibili Scraper](https://apify.com/cleanscrape/bilibili-scraper): Bilibili videos, danmaku, comments and creators
- [App Store Reviews & Google Play Reviews Scraper](https://apify.com/cleanscrape/app-store-reviews-scraper): reviews from both app stores in one table
- [Google Trends Scraper API](https://apify.com/cleanscrape/google-trends-scraper): search interest over time, by region, related queries and trending searches
- [Map Your Show Exhibitor List Scraper](https://apify.com/cleanscrape/event-exhibitor-monitor): trade show exhibitor lists and booth changes
- [Shopify Product Reviews Scraper API](https://apify.com/cleanscrape/shopify-reviews-scraper): Judge.me and Okendo reviews from Shopify product links
- [Google News Scraper and RSS Feed Reader](https://apify.com/cleanscrape/google-news-scraper): headlines from Google News and publisher feeds
- [Google Jobs Scraper](https://apify.com/cleanscrape/google-jobs-scraper): job listings with salaries and apply links
- [Pinterest Scraper API](https://apify.com/cleanscrape/pinterest-scraper): keyword search, public pin links and board previews; search results may lack a pin link
- [Google Hotels Scraper](https://apify.com/cleanscrape/google-hotels-scraper): hotel prices and booking offers for your dates
- [Meta Threads Scraper](https://apify.com/cleanscrape/threads-scraper): public Threads posts from accounts or post links
- [Likee Scraper API](https://apify.com/cleanscrape/likee-scraper): Likee videos and comments
- [Casino Bonus Checker](https://apify.com/cleanscrape/casino-bonus-checker): the casino bonus each affiliate and review site shows, with promo codes, 18+ notices and affiliate IDs

### Support

Questions, a video that is read wrongly or a format you need: open an issue on the Actor's Issues tab or write to contact.cleanscrape@gmail.com with the run ID.

If the Actor was useful, an honest review on its Store page helps other people find it.

# Actor input Schema

## `videos` (type: `array`):

One direct link to a video file per line (MP4, MOV, WebM, MKV, AVI, …), for example from your own storage, a CDN, S3 or Google Cloud Storage. Signed links work. Pages that only play a video (YouTube, TikTok, Instagram, Vimeo pages) are not supported: use a link that downloads the file itself. Up to 200 videos per run.

## `mode` (type: `string`):

Overview finds scene changes from the video's keyframes, which is fast at any length, and fills quiet stretches with evenly placed frames. Precise scene changes looks at every frame to catch every cut, including quick ones; it costs extra per minute of video scanned.

## `maxFrames` (type: `integer`):

The most frames to save from each video. Each saved frame is charged; near-identical frames that are left out are not.

## `intervalSeconds` (type: `integer`):

For "Every N seconds": time between frames. Up to "Frames per video" frames are saved, from the start.

## `timestamps` (type: `array`):

For "Exact timestamps": one per line, as seconds (83 or 83.5) or as 1:23, 01:23.5 or 1:02:03. The same times are used for every video.

## `startTime` (type: `string`):

Use only the part of each video after this time, for example 0:30 to skip an intro.

## `endTime` (type: `string`):

Use only the part before this time, for example 10:00. Empty means the end of the video.

## `sceneSensitivity` (type: `string`):

How different two moments must look to count as a new scene.

## `skipDuplicates` (type: `boolean`):

Frames that look the same as one already saved from the same video are left out and not charged. Not used for a set number of frames or exact timestamps: those give exactly what you asked for.

## `imageFormat` (type: `string`):

File type of the saved frames.

## `maxWidth` (type: `integer`):

Larger frames are scaled down to this width, keeping their shape; smaller ones are not enlarged. 0 keeps the video's full size.

## `quality` (type: `integer`):

For JPG and WebP: 100 is the best quality and the largest files.

## `contactSheet` (type: `boolean`):

Also save one grid image per video showing up to 60 of its frames with their times. Free.

## `maxFileSizeMb` (type: `integer`):

Larger files are not downloaded and not charged. For files over 2 GB, raise the run's memory under Run options.

## Actor input object example

```json
{
  "videos": [
    "https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4",
    "https://archive.org/download/BigBuckBunny_124/Content/big_buck_bunny_720p_surround.mp4"
  ],
  "mode": "overview",
  "maxFrames": 12,
  "intervalSeconds": 10,
  "timestamps": [],
  "sceneSensitivity": "medium",
  "skipDuplicates": true,
  "imageFormat": "jpg",
  "maxWidth": 1280,
  "quality": 85,
  "contactSheet": true,
  "maxFileSizeMb": 2048
}
```

# Actor output Schema

## `frames` (type: `string`):

One row per saved frame: time, timecode, image link, size and why it was picked.

## `videos` (type: `string`):

One row per video: outcome, length, size, rotation, frame rate, frames saved, contact sheet link, and why a video could not be read.

## `images` (type: `string`):

The image files.

## `contactSheets` (type: `string`):

One grid image per video with the time on each frame.

## `summary` (type: `string`):

The run as JSON, for code and AI agents.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "videos": [
        "https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4",
        "https://archive.org/download/BigBuckBunny_124/Content/big_buck_bunny_720p_surround.mp4"
    ],
    "maxFrames": 12
};

// Run the Actor and wait for it to finish
const run = await client.actor("cleanscrape/video-frame-extractor").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "videos": [
        "https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4",
        "https://archive.org/download/BigBuckBunny_124/Content/big_buck_bunny_720p_surround.mp4",
    ],
    "maxFrames": 12,
}

# Run the Actor and wait for it to finish
run = client.actor("cleanscrape/video-frame-extractor").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "videos": [
    "https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4",
    "https://archive.org/download/BigBuckBunny_124/Content/big_buck_bunny_720p_surround.mp4"
  ],
  "maxFrames": 12
}' |
apify call cleanscrape/video-frame-extractor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,cleanscrape/video-frame-extractor"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/n8nxnt1mm3HxLy0tY/builds/VOW68MfCsnXuuPfuA/openapi.json
