# Video Frame Extractor (`cyber1nj3ct0r/video-frame-extractor`) Actor

Take still frames and thumbnails out of videos: evenly spread, one every few seconds, or at the times you name. Give MP4, WebM, MOV or other video links; get JPEG, PNG or WebP frames with links, times and sizes, plus each video's length and resolution.

- **URL**: https://apify.com/cyber1nj3ct0r/video-frame-extractor.md
- **Developed by:** [Sanira Botheju](https://apify.com/cyber1nj3ct0r) (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 $0.70 / 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

### What does Video Frame Extractor do?

It takes still frames out of videos. Give it the addresses of video files (MP4, WebM, MOV, MKV, AVI and most other formats) and choose how to pick the frames:

- **Evenly spread**: a set number of frames across the whole video, for thumbnails, previews and contact sheets.
- **Every few seconds**: one frame each interval from the start, for reviewing or labelling footage.
- **At these times**: the exact moments you list, such as `1:23` or `83.5`.

Each frame is stored as a JPEG, PNG or WebP file with a link you can open or share, together with its time in the video, its size, and the video's length, resolution, codec and frame rate. The frames are ready to feed to an AI model that reads pictures, to a search index or to a web page.

### What data do you get?

| Field | What it holds |
|---|---|
| `fileUrl` | Link to the frame's picture file; it opens without logging in to Apify |
| `timestamp`, `timestampSec` | Time of the frame in the video, as `00:01:23.500` and in seconds |
| `width`, `height`, `bytes`, `format` | Size of the frame in pixels, its file size and format |
| `frameIndex`, `videoPosition` | Number of the frame within its video, and of the video in your list |
| `video` | The video's length in seconds, width, height, codec, frames per second, container and file size |
| `error` | Empty for a frame that was taken; otherwise a code such as `NOT_A_VIDEO`, `FILE_TOO_LARGE`, `HTTP_404` or `NO_FRAME` |

### How to take frames from a video

1. Paste the video links under **Video addresses**, or link a text file that lists them.
2. Choose **How to pick frames** and the number of frames, the interval or the times.
3. Optionally set the **Picture format** and a **Frame width** (the height follows the video's shape).
4. Press **Start**. The **Frames** tab shows every frame with its picture.

### How much does it cost to extract frames?

You pay per frame taken:

| Plan | Price |
|---|---|
| Free and Starter | $1.00 per 1,000 frames ($0.001 each) |
| Scale | $0.85 per 1,000 frames |
| Business | $0.70 per 1,000 frames |

| What you do | Cost |
|---|---|
| 10 thumbnails from each of 100 videos | $1.00 |
| One frame every 10 seconds of a one-hour video (360 frames) | $0.36 |
| One cover picture for each of 50 videos | $0.05 |

Downloading and reading a video costs nothing extra. A video that cannot be read (a broken link, not a video, larger than your limit) counts as one result and costs the price of one frame. Apify's free plan gives $5 of credit each month, enough for about 5,000 frames. Set a maximum cost for the run, and the tool stops before it would go over it.

### Input

```json
{
    "videoUrls": [
        { "url": "https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4" }
    ],
    "mode": "count",
    "frameCount": 10,
    "format": "jpg",
    "width": 640,
    "quality": 85
}
```

For one frame every 30 seconds use `"mode": "interval", "intervalSecs": 30`; for given times use `"mode": "timestamps", "timestamps": ["0:05", "1:30", "125.5"]`. More options: frames per video at most, the largest video to download (500 MB by default), the time limit per download, and the User-Agent.

### Output example

```json
{
    "videoUrl": "https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4",
    "videoPosition": 1,
    "frameIndex": 5,
    "timestampSec": 2.275,
    "timestamp": "00:00:02.275",
    "fileKey": "v0001-f0005-2275ms.jpg",
    "fileUrl": "https://api.apify.com/v2/key-value-stores/STORE_ID/records/v0001-f0005-2275ms.jpg",
    "format": "jpg",
    "width": 960,
    "height": 540,
    "bytes": 26073,
    "video": { "durationSec": 5.055, "width": 960, "height": 540, "codec": "h264", "fps": 29.97, "container": "mov,mp4,m4a,3gp,3g2,mj2", "bytes": 1128375 },
    "extractedAt": "2026-10-16T15:16:11.541Z",
    "error": null
}
```

### What can you use it for?

- Make thumbnails and preview strips for a video library or a shop's product videos.
- Give an AI model that reads pictures a few frames per video to describe, tag or check them, instead of the whole video.
- Pull the frames at chosen moments for a report, a training set or a quality check.
- Find each video's length, resolution, codec and frame rate in bulk, from the `video` field.

### Use it from the API

```bash
curl -X POST "https://api.apify.com/v2/acts/cyber1nj3ct0r~video-frame-extractor/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"videoUrls": [{"url": "https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4"}], "frameCount": 5}'
```

The same works from Python, JavaScript, Make, Zapier and n8n through Apify's integrations. Each result row holds `fileUrl`; the files are also in the run's key-value store under `fileKey`.

### FAQ

#### Which videos work?

Video files that sit at their own address and that ffmpeg can read: MP4 (H.264, H.265), WebM (VP8, VP9, AV1), MOV, MKV, AVI, MPEG and more. Links to pages of video sites such as YouTube, Instagram, TikTok or Vimeo are web pages, not video files, and do not work; the tool does not take videos out of those sites. Streams that are cut into many small pieces (HLS `.m3u8`, DASH) are not read either.

#### How exact are the times?

The frame is the one shown at that moment in the video, decoded from the nearest keyframe before it, so it is exact to the frame. With **Evenly spread**, frames sit in the middle of equal parts of the video, never on the black first or last picture.

#### What about large or long videos?

Each video is downloaded to the run's disk first, up to **Largest video** (500 MB by default). The disk is twice the run's memory and two videos are read at the same time, so for videos larger than about 1,000 MB give the run more memory. Taking a frame from a long video is usually about as fast as from a short one, because the tool jumps to the time instead of reading the whole video.

#### Does it detect scene changes?

Not yet: frames are picked by time. Write an issue if you need scene detection.

#### A site blocks the download. What can I do?

The tool says who it is in its User-Agent and does not get round blocks, logins or paywalls, and it uses no proxies. If a site refuses it, the result says so with the HTTP status. Only use videos you have the right to use.

#### Something does not work

Open an issue on the tool's **Issues** tab with the run's link. Issues are answered.

# Actor input Schema

## `videoUrls` (type: `array`):

Direct links to video files (MP4, WebM, MOV, MKV, AVI and most others ffmpeg reads), for example <code>https://www.example.com/clip.mp4</code>. Add them one by one, paste many with <b>Bulk edit</b>, or link a text file that lists them. Pages of video sites (YouTube, Instagram, TikTok) are not video files and do not work.

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

<b>Evenly spread</b> takes the number of frames below across the whole video. <b>Every few seconds</b> takes one frame each interval from the start. <b>At these times</b> takes the frames at the times you list.

## `frameCount` (type: `integer`):

For <b>Evenly spread</b>: how many frames to take from each video.

## `intervalSecs` (type: `integer`):

For <b>Every few seconds</b>: the time between two frames.

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

For <b>At these times</b>: one time per line, in seconds (<code>83.5</code>) or as minutes and seconds (<code>1:23</code>, <code>1:02:03</code>). Times past the end of a video are skipped.

## `format` (type: `string`):

JPEG files are small, PNG files keep every detail, WebP files are the smallest at the same look.

## `width` (type: `integer`):

Make frames this many pixels wide; the height follows the video's shape. Leave empty to keep the video's own size. Frames are never made larger than the video.

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

For JPEG and WebP: higher gives a sharper picture and a larger file.

## `maxFramesPerVideo` (type: `integer`):

No video gives more frames than this, whatever the choice above. Your spending limit for the run also stops it.

## `maxVideoSizeMb` (type: `integer`):

Videos larger than this are not downloaded. The run's disk space is twice its memory, and two videos are read at once: for videos above 1,000 MB, give the run more memory. 1 MB is 1,000,000 bytes.

## `requestTimeoutSecs` (type: `integer`):

How long downloading one video may take.

## `userAgent` (type: `string`):

The name the tool sends when it downloads a video. Leave empty to use the tool's own name, which tells site owners what is visiting. Set your own only for sites you run or have permission to use.

## Actor input object example

```json
{
  "videoUrls": [
    {
      "url": "https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4"
    },
    {
      "url": "https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.webm"
    }
  ],
  "mode": "count",
  "frameCount": 10,
  "intervalSecs": 10,
  "format": "jpg",
  "quality": 85,
  "maxFramesPerVideo": 100,
  "maxVideoSizeMb": 500,
  "requestTimeoutSecs": 300
}
```

# Actor output Schema

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

One record per frame with its link, time in the video, picture size and the video's facts, or the reason a video could not be read.

## `frameFiles` (type: `string`):

The run's key-value store, which holds every frame's picture file.

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

Counts for the run: videos read and failed, frames taken and failed, times past the end of a video.

# 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 = {
    "videoUrls": [
        {
            "url": "https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4"
        },
        {
            "url": "https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.webm"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("cyber1nj3ct0r/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 = { "videoUrls": [
        { "url": "https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4" },
        { "url": "https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.webm" },
    ] }

# Run the Actor and wait for it to finish
run = client.actor("cyber1nj3ct0r/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 '{
  "videoUrls": [
    {
      "url": "https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4"
    },
    {
      "url": "https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.webm"
    }
  ]
}' |
apify call cyber1nj3ct0r/video-frame-extractor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,cyber1nj3ct0r/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/kp1eahlmysgudmSif/builds/KT8vxh85yt3laB60g/openapi.json
