# YouTube Video Frame Extractor (`ttechx/youtube-video-frame-extractor`) Actor

Extract JPEG frames from any YouTube video: at exact timestamps (to the millisecond), every N seconds, or N evenly spaced frames, at 360p-1080p. Downloads only the seconds around each frame, so even hour-long videos are fast. Pay per frame; failed frames are free.

- **URL**: https://apify.com/ttechx/youtube-video-frame-extractor.md
- **Developed by:** [TTech](https://apify.com/ttechx) (community)
- **Categories:** Videos, Social media, AI
- **Security:** Limited permissions
- **Stats:** 1 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.50 / 1,000 frame captureds

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

## YouTube Video Frame Extractor

Paste YouTube links and get JPEG frames from them. You choose which frames:

- **Exact timestamps**, to the millisecond, such as `00:01:23.250`
- **One frame every N seconds**, such as every 10 seconds
- **N evenly spaced frames** across the whole video or a part of it

Frames come at 360p, 480p, 720p or 1080p. The Actor returns one image link per frame.

### Why this Actor

| | This Actor | Typical frame extractors |
|---|---|---|
| **YouTube links** | Paste any YouTube, Shorts or youtu.be link | Many accept only direct video files (MP4 URLs) |
| **What it downloads** | Only the few seconds that hold each frame | Often the whole video |
| **Speed** | Seconds for a handful of frames, even from hour-long videos | Grows with video length |
| **Precision** | The exact frame on screen at that millisecond | Varies; some snap to the nearest keyframe |
| **Pricing** | Per frame. No per-video, per-minute or per-megabyte fees | Often per video plus per frame, sometimes per MB |
| **Proxies** | None needed: nothing to set up | Often extra, or your own |
| **Failed frames** | Free | Varies |
| **Retries** | Automatic | Varies |

### What you can use it for

- Thumbnails and preview images at chosen moments
- Frame-by-frame study of editing, animation or camera style
- Stills from YouTube for computer vision, AI training or AI agents
- Screenshots for articles, reports and documentation

### Input

| Field | Description |
|---|---|
| `videos` | YouTube URLs (`watch?v=`, `youtu.be/`, Shorts, embed or live links) or 11-character video ids. Up to 500. |
| `mode` | `timestamps` (default), `interval` or `evenly`. |
| `timestamps` | Exact timestamps mode: `HH:MM:SS` or `HH:MM:SS.mmm`, such as `00:01:23` or `00:01:23.250`. Up to 1000, captured from every video. |
| `intervalSeconds` | Interval mode: seconds between frames, from 0.1 up. Default 10. |
| `frameCount` | Evenly spaced mode: frames per video, 1-1000. Default 10. |
| `startTime`, `endTime` | Interval and evenly spaced modes: optional range, such as `00:01:00` to `00:05:00`. Default: the whole video. |
| `resolution` | `360`, `480`, `720` (default) or `1080`. A video smaller than this keeps its native size. |

Exact timestamps:

```json
{
    "videos": ["https://www.youtube.com/watch?v=Ap8jTCMK3c4"],
    "timestamps": ["00:00:50", "00:01:10.500"],
    "resolution": "720"
}
```

One frame every 30 seconds of the first 5 minutes:

```json
{
    "videos": ["https://youtu.be/Ap8jTCMK3c4"],
    "mode": "interval",
    "intervalSeconds": 30,
    "endTime": "00:05:00"
}
```

20 frames spread over the whole video, in 1080p:

```json
{
    "videos": ["Ap8jTCMK3c4"],
    "mode": "evenly",
    "frameCount": 20,
    "resolution": "1080"
}
```

In the interval and evenly spaced modes, frames follow each video's own length, so one input
works for a mix of short and long videos. Evenly spaced frames sit in the middle of equal
parts of the range. This skips the often-black first frame and the very end.

### Output

The Actor pushes one dataset item per video and frame. The image is saved in the run's
key-value store, and `imageUrl` links to it directly.

```json
{
    "videoId": "Ap8jTCMK3c4",
    "videoUrl": "https://www.youtube.com/watch?v=Ap8jTCMK3c4",
    "timestamp": "00:01:10.500",
    "resolution": "720p",
    "status": "OK",
    "imageUrl": "https://api.apify.com/v2/key-value-stores/.../records/Ap8jTCMK3c4_00-01-10.500_720p.jpg",
    "imageKey": "Ap8jTCMK3c4_00-01-10.500_720p.jpg",
    "error": null
}
```

`resolution` is the size you actually got, by the shorter side. It can be below what you
asked for when the video itself is smaller.

A frame that could not be captured has `"status": "fail"`, `imageUrl: null` and the reason in
`error`, for example a timestamp past the end of the video. Other frames of the run are not
affected. In exact timestamps mode, `timestamp` is echoed exactly as you sent it.

**Which frame you get:** the one on screen at that time, meaning the first frame at or after
the timestamp. Near the very end of a video, you get its last frame.

### Pricing

You pay per captured frame, and failed frames are free.

| | Per frame | Per 1,000 frames |
|---|---|---|
| 360p, 480p or 720p | $0.0025 | $2.50 |
| 1080p | $0.006 | $6 |

There's also a small start fee of $0.005 per run (per GB of memory, with a minimum of one, so
$0.005 at the default 256 MB). There are no per-video, per-minute or per-megabyte fees. 1080p costs more because each
1080p frame needs about three times the video data. You pay for the resolution you actually
get: a 1080p request on a video that only goes up to 720p is charged as 720p.

The Actor respects your run's maximum charge setting: it stops capturing once that limit is
reached, and keeps everything captured so far. If Apify moves a run to another server, the
run continues where it was: frames already captured are not captured or charged again.

### Limits

- Live streams that are still live are not supported. Private, deleted, region-locked and age-restricted videos fail with an error.
- At most 1000 frames per video in the interval and evenly spaced modes, and 1000 timestamps per run.
- On Apify's Free plan, 100 frames per calendar month. Paid Apify plans have no monthly limit.
- Timestamps go up to `99:59:59.999`.
- For very large runs, raise the run timeout. The Actor stops cleanly before the timeout and keeps everything captured so far.

# Actor input Schema

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

Video URLs (watch, youtu.be, Shorts, embed or live links) or 11-character video ids, up to 500. Every timestamp below is captured from each video.

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

Exact timestamps you list, one frame every N seconds, or N frames spread evenly over the video. Interval and evenly spaced modes apply to each video's own length.

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

Times to capture, as HH:MM:SS or HH:MM:SS.mmm (1-3 fraction digits), e.g. 00:01:23 or 00:01:23.250. You get the frame on screen at that time. Up to 1000 per run; duplicates are dropped.

## `intervalSeconds` (type: `number`):

Seconds between frames, from 0.1 up. For example 10 gives frames at 0:00, 0:10, 0:20 and so on. At most 1000 frames per video.

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

Frames per video. The range is split into equal parts and the frame in the middle of each part is taken, so you skip the black first frame and the very end.

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

Interval and evenly spaced modes only: start the range here, as HH:MM:SS\[.mmm]. Default: the start of the video.

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

Interval and evenly spaced modes only: end the range here, as HH:MM:SS\[.mmm]. Default: the end of the video.

## `resolution` (type: `string`):

Frame height. A video smaller than this keeps its native size.

## Actor input object example

```json
{
  "videos": [
    "https://www.youtube.com/watch?v=Ap8jTCMK3c4"
  ],
  "mode": "timestamps",
  "timestamps": [
    "00:00:50",
    "00:01:10.500"
  ],
  "intervalSeconds": 10,
  "frameCount": 10,
  "resolution": "720"
}
```

# Actor output Schema

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

One item per (video, timestamp): status, image URL and error, if any.

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

The captured JPEG frames, keyed <videoId>*\<HH-MM-SS\[.mmm]>*<height>p.jpg.

# 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://www.youtube.com/watch?v=Ap8jTCMK3c4"
    ],
    "timestamps": [
        "00:00:50",
        "00:01:10.500"
    ],
    "intervalSeconds": 10,
    "frameCount": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("ttechx/youtube-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://www.youtube.com/watch?v=Ap8jTCMK3c4"],
    "timestamps": [
        "00:00:50",
        "00:01:10.500",
    ],
    "intervalSeconds": 10,
    "frameCount": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("ttechx/youtube-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://www.youtube.com/watch?v=Ap8jTCMK3c4"
  ],
  "timestamps": [
    "00:00:50",
    "00:01:10.500"
  ],
  "intervalSeconds": 10,
  "frameCount": 10
}' |
apify call ttechx/youtube-video-frame-extractor --silent --output-dataset

```

## MCP server setup

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