# Video Frame Extractor (`ensorcelling_melilot/video-frames`) Actor

Turn video files into still images. Pick a frame every N seconds, N evenly spaced frames, or one frame at every scene change. Time stamps, image links and video details for each frame. $0.001 per frame + $0.005 per video.

- **URL**: https://apify.com/ensorcelling_melilot/video-frames.md
- **Developed by:** [ensorcelling \_melilot](https://apify.com/ensorcelling_melilot) (community)
- **Categories:** Videos
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 frame extracteds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

Turn **video files into still images**. Give the Actor direct links to videos and pick how to sample them: a frame every N seconds, a fixed number of evenly spaced frames, or one frame at every scene change. Each frame is saved to Apify storage, and you get a table with the time stamp, image link and size of every one.

Powered by ffmpeg inside the Actor. No browser, no proxies, no login.

### Why this one?

- **Three sampling modes.** Every N seconds, N evenly spaced frames (first and last moment included), or scene-change detection with an adjustable sensitivity.
- **Ready for thumbnails, review and AI pipelines.** JPEG, PNG or WebP, optional max width (never enlarged), exact time stamps (`00:01:23.500`) and video details (length, size, frame rate, codec) on every row.
- **Fair price.** $0.001 per frame plus $0.005 per video. Videos that fail, are too large or give no frames are free.
- **Doesn't die on one bad video.** Each video and each frame gets its own status and a plain-language error.
- **Safe by default.** Redirects are checked one hop at a time; localhost, private networks and cloud metadata addresses are refused.

### What it does not do

It reads **direct video file links** (MP4, MOV, WebM, MKV, AVI and anything else ffmpeg understands). It does not download from YouTube, TikTok, Instagram or other video sites, and it does not decode DRM-protected or streaming-manifest (HLS/DASH) links. Use videos you have the rights to process.

### Input

| Field | What it does |
|---|---|
| Video URLs | Direct links to video files |
| How to pick frames | `interval` (every N seconds), `count` (N evenly spaced) or `scenes` (scene changes) |
| Seconds between frames | For `interval`. Default 10 |
| Number of frames | For `count`. Default 10 |
| Scene change sensitivity | For `scenes`. 0.1 finds many cuts, 0.6 only hard cuts. Default 0.3 |
| Max frames per video | Hard limit whatever the mode. Default 100 |
| Image format | `jpg` (default), `png`, `webp` |
| Max image width | Shrink wide frames; 0 keeps the original size |
| Max video size (MB) | Larger videos are skipped and not charged. Default 100 |
| Extra request headers | Referer, Cookie etc. for hosts that check them |

In `scenes` mode the first frame of the video is always included, so every video returns at least one image.

### Output

One dataset row per frame:

```json
{
  "status": "extracted",
  "videoUrl": "https://example.com/clip.mp4",
  "frameNumber": 3,
  "timestampSeconds": 20,
  "timestamp": "00:00:20.000",
  "imageUrl": "https://api.apify.com/v2/key-value-stores/.../records/v001-f0003.jpg",
  "storeKey": "v001-f0003.jpg",
  "contentType": "image/jpeg",
  "sizeBytes": 18234,
  "videoDurationSeconds": 62.4,
  "videoWidth": 1280,
  "videoHeight": 720,
  "videoFps": 29.97,
  "videoCodec": "h264"
}
```

The images are in the run's key-value store under `storeKey`. A `SUMMARY` record counts videos, frames and failures. Rows with `status` `failed` carry an `error` that says why.

### Pricing

Pay per event:

- **$0.001 per frame extracted** (`frame-extracted`)
- **$0.005 per video processed** (`video-processed`, only when at least one frame was saved)

Example: 20 frames from each of 100 videos costs 100 × $0.005 + 2,000 × $0.001 = **$2.50**.

### Tips

- Long videos and fine sampling cost more frames. Set **Max frames per video** to stay in budget; the run also respects the maximum cost you set in Apify.
- Seeking is fast, so a 2-hour video costs about the same time as a 2-minute one for the same number of frames; scene detection has to read the whole video and takes longer.

# Actor input Schema

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

Direct links to video files (MP4, MOV, WebM, MKV, AVI and anything else ffmpeg can read), one per line. Links to YouTube or other video sites are not supported.

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

Every N seconds, a fixed number of evenly spaced frames, or one frame at each scene change (cut).

## `everySeconds` (type: `number`):

Used when the mode is 'Every N seconds'. The first frame is taken at 0 s.

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

Used when the mode is 'N evenly spaced frames'. The first and last moments of the video are included.

## `sceneThreshold` (type: `number`):

Used for scene changes. Lower finds more cuts (0.1 is very sensitive), higher only finds hard cuts (0.6). Default 0.3.

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

Hard limit on frames taken from one video, whatever the mode.

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

File type of the extracted frames.

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

Shrink frames wider than this, keeping the aspect ratio. 0 keeps the original size. Frames are never enlarged.

## `maxVideoMB` (type: `integer`):

Videos larger than this are skipped and not charged.

## `requestHeaders` (type: `object`):

Optional headers sent when downloading the videos, for example a Referer or Cookie for hosts that check them.

## Actor input object example

```json
{
  "videoUrls": [
    "https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4"
  ],
  "mode": "interval",
  "everySeconds": 10,
  "frameCount": 10,
  "sceneThreshold": 0.3,
  "maxFramesPerVideo": 100,
  "imageFormat": "jpg",
  "maxWidth": 0,
  "maxVideoMB": 100
}
```

# Actor output Schema

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

One row per frame: video, time stamp, image link and size.

## `allFields` (type: `string`):

Every row with all fields.

## `storage` (type: `string`):

The extracted frames themselves.

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

Counts of videos and frames processed and failed.

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

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

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

```

## MCP server setup

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

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/CaXeo6inA7mtXshxO/builds/SBnlDcqDHrN1gizgK/openapi.json
