# Frameskim: YouTube frames and moments for AI agents (`emttn/frameskim`) Actor

YouTube frames and screenshots at exact timestamps, the words spoken between them and the moments that answer a query, from any public video URL: video for LLM and AI agents, MCP and automations.

- **URL**: https://apify.com/emttn/frameskim.md
- **Developed by:** [Frameskim Tools](https://apify.com/emttn) (community)
- **Categories:** Videos, AI, Agents
- **Stats:** 10 total users, 7 monthly users, 92.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.12 / video processed

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

## Frameskim: YouTube frames and moments for AI agents

**Let your agent watch the video.** Frameskim turns one public YouTube URL into frames and screenshots at exact timestamps, the words spoken between them, and the moments that best answer a question: video for LLM input, for AI agents.

![Four frames of a timelapse, each with its timestamp](https://frameskim.dev/assets/hero.jpg)

<sub>Image combines frames from 'Timelapse of a Busy Place' by Freestocks (https://youtu.be/uwutP808QsI), licensed CC BY 3.0 (https://creativecommons.org/licenses/by/3.0/).</sub>

Use it to:

- Turn a YouTube tutorial into a step-by-step guide with screenshots.
- Give an LLM that cannot watch video the frames and the words of a video.
- Take screenshots of a video for docs, support answers and bug reports.
- Find where a video says something and see the frame.
- Build thumbnails and storyboards from the moments you pick.

### Extract video frames from YouTube at exact timestamps

Three modes, one video per run:

- **frames**: a frame every N seconds over the range you pick.
- **segment**: the same frames plus the transcript text spoken between each frame and the next, and a ready-to-paste Markdown block.
- **moments**: give a query in plain language; Frameskim scores the transcript and returns the top moments that answer it, each with its frame.

Frames come from the video itself (up to 480p) when YouTube allows it. When it blocks the download, Frameskim falls back to YouTube's storyboard (low resolution) instead of failing, and says so in the run summary (`lowResolutionFallback: true`).

### YouTube screenshots with the words spoken

In `segment` mode each frame comes with what is said until the next one, and the summary item has a Markdown block with one line per frame: the timestamp, the words and the image link. Paste it into a doc, a ticket or a prompt as a guide with screenshots.

### Find the moment in a YouTube video that answers a question

Ask in plain language ("when does the parachute deploy") and set `topK`: you get the best moments first, each with the window where it is said, a score, the words and the frame from the middle of the window.

### Pricing

Pay per event, no subscription. Apify bills each event and never more than the spending limit you set for the run.

| Event | Price (USD) | When |
|---|---|---|
| `video-processed` | $0.12 | Once per run that delivers frames, with `quality: standard` |
| `video-processed-hd` | $0.60 | Once per run when `quality: hd` attempts the full-resolution download (replaces `video-processed`) |
| `frame-media` | $0.015 | Per frame taken from the video itself |
| `frame-storyboard` | $0.002 | Per frame taken from the storyboard fallback |
| `moment-search` | $0.02 | Once per `moments` search |

Quality: hd charges 0.60 USD per video once a full-resolution download is attempted, even if YouTube blocks it and the frames fall back to storyboard; standard charges 0.12 and uses storyboard when full resolution would cost too much.

A run that ends with an error ("... Nothing was charged.") is not billed. If your spending limit covers fewer frames than you asked for, you get the first ones and the summary says `stoppedByBudget: true`.

Typical runs: `frames` with 20 frames from the video costs $0.42 (a 10-minute video, one frame every 30 s); `segment` with 3 storyboard frames costs $0.126; `moments` with 5 moments from the video costs $0.215.

### Examples

#### frames

Input:

```json
{ "mode": "frames", "url": "https://youtu.be/4czjS9h4Fpg", "start": "0:15", "end": "0:45", "every": 10 }
```

Output (one item per frame, then a summary item):

```json
{ "videoId": "4czjS9h4Fpg", "t": 15.0, "ts": "0:00:15", "imageUrl": "https://api.apify.com/v2/key-value-stores/.../records/frame-000-15000.jpg", "width": 854, "height": 480, "source": "media" }
```

#### segment

Input:

```json
{ "mode": "segment", "url": "https://youtu.be/4czjS9h4Fpg", "start": "0:15", "end": "0:45", "every": 10 }
```

Output: the same items with a `text` field (what is said until the next frame); the summary adds `markdown`, one line per frame with `[ts]`, the text and the image link.

```json
{ "videoId": "4czjS9h4Fpg", "t": 15.0, "ts": "0:00:15", "text": "...", "imageUrl": "https://...", "width": 854, "height": 480, "source": "media" }
```

#### moments

Input:

```json
{ "mode": "moments", "url": "https://youtu.be/4czjS9h4Fpg", "query": "when does the parachute deploy", "topK": 3 }
```

Output: one item per moment, best first; `frameTs`/`frameTime` are the second the frame was taken, the middle of the window (values shortened):

```json
{ "videoId": "4czjS9h4Fpg", "start": 0.0, "end": 30.0, "ts": "0:00:00", "frameTs": 15.0, "frameTime": "0:00:15", "score": 0.95, "text": "...", "imageUrl": "https://...", "width": 854, "height": 480, "source": "media" }
```

### YouTube frames API for AI agents

```bash
curl -X POST "https://api.apify.com/v2/acts/emttn~frameskim/run-sync-get-dataset-items" \
  -H "Authorization: Bearer $APIFY_TOKEN" -H "Content-Type: application/json" \
  -d '{"mode": "segment", "url": "https://youtu.be/4czjS9h4Fpg", "start": "1:00", "end": "1:30"}'
```

The response is the list of items, ending with the summary. Images are signed links that open without a token.

### Use it from Claude (MCP)

Add the Apify MCP server with this actor as a tool:

```json
{ "mcpServers": { "apify": { "url": "https://mcp.apify.com/?actors=emttn/frameskim" } } }
```

Then ask, for example: "Use Frameskim to find where this video says the parachute deploys and show me the frame."

The tool starts a run and returns its status; the agent then reads the results with the server's `get-dataset-items` tool (Claude does this on its own). A search takes about a minute.

### Integrations

- **Schedule it.** Save a run as a task and add a schedule in Apify to capture frames of new videos on a fixed day and hour: [how to schedule tasks](https://www.youtube.com/watch?v=GRFW_Loo2dk).
- **Chain it after another actor.** Start Frameskim when a YouTube scraper finishes and pass it the video URLs it found: [Actor-to-Actor integrations](https://www.youtube.com/watch?v=zExnYbvFoBM).
- **n8n.** Use the [n8n template](https://n8n.io/workflows/20886) that turns a YouTube tutorial into a guide with screenshots.
- **Make, Zapier and webhooks** work through Apify's own integrations, and Claude through the MCP server above.

https://www.youtube.com/watch?v=GRFW_Loo2dk

https://www.youtube.com/watch?v=zExnYbvFoBM

Ready-made tasks (screenshot at a timestamp, frames every N seconds) are in the Tasks tab of this page.

### Related actors

- [YouTube Transcript](https://apify.com/emttn/youtube-transcript): the words of the same videos, timestamped, up to 100 per run.
- [Instagram & TikTok Toolkit](https://apify.com/emttn/instagram-tiktok-transcript): transcripts with timestamps and 3 frames per Reel or TikTok.
- [YouTube Scraper](https://apify.com/streamers/youtube-scraper): finds the video URLs of a search, a channel or a playlist to pass here.
- [Fast YouTube Channel Scraper](https://apify.com/streamers/youtube-channel-scraper): lists the videos of a channel.

### Limits

- Single public videos only: one video per run, no channels, playlists or searches.
- `maxFrames` is 50 by default; a longer range widens `every` to stay under it.
- `moments`: 3 searches per month on the free plan and 200 per day on paid plans, over up to 540 transcript windows of a video (about 3 hours).
- Each run respects your Apify spending limit: fewer frames and `stoppedByBudget` when it runs short, and an error with nothing charged when it does not cover the video.
- Videos need captions for `segment` text and for `moments`.

### FAQ

**Why are some frames low resolution?** YouTube blocks many automated downloads. Instead of failing, Frameskim uses YouTube's storyboard images and charges the lower storyboard price. `quality: hd` pays for a full-resolution attempt.

**Which languages?** Any language the video has captions in; set `lang` (default `en`) and Frameskim falls back to the original language.

**What if a run fails?** The status message says why and ends with "Nothing was charged." Retry later if YouTube blocked the request.

**Where do my frames live?** Only in your run's own Apify storage, with your plan's retention. Frameskim keeps no copies.

### Official YouTube API and alternatives

**Is there an official YouTube API for screenshots or frames?** No. In the [YouTube Data API `videos` resource](https://developers.google.com/youtube/v3/docs/videos) there is no method that returns frames at a timestamp: `snippet.thumbnails` holds a few preset thumbnails of the video, not the frames you choose. Frameskim returns the frame at each second you ask for.

**Frameskim vs doing it by hand?** By hand you open the video, scrub to each second and capture the screen, or you write your own extraction code and keep it working as YouTube changes. Frameskim does that for you, lines the frames up with the words spoken, falls back to YouTube's storyboard instead of failing, and bills only what it delivers.

### Legal

Not affiliated with YouTube or Google. By running Frameskim you confirm you have the right to process each video you submit.

Takedown requests: open an issue in the Issues tab of this page or write to support@frameskim.dev. Blocked videos and channels are refused on the next run.

[Terms](https://frameskim.dev/terms) · [Privacy](https://frameskim.dev/privacy)

# Actor input Schema

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

frames: one frame every N seconds. segment: frames plus the words spoken. moments: the parts of the video that best answer a query.

## `url` (type: `string`):

One public YouTube video or Short. Channels, playlists and searches are not accepted.

## `query` (type: `string`):

What to find in the video, in plain words (3 to 300 characters). Only for mode moments.

## `topK` (type: `integer`):

How many moments to return, best first (1 to 10). Only for mode moments.

## `start` (type: `string`):

Where to start: seconds, M:SS or H:MM:SS. Empty is the beginning of the video.

## `end` (type: `string`):

Where to stop: seconds, M:SS or H:MM:SS. Empty is the end of the video.

## `every` (type: `integer`):

One frame every N seconds; widened if it would pass maxFrames.

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

Upper limit of frames per run (1 to 50).

## `quality` (type: `string`):

hd charges 0.60 USD per video once a full-resolution download is attempted, even if YouTube blocks it and the frames fall back to storyboard; standard charges 0.12 and uses storyboard when full resolution would cost too much.

## `lang` (type: `string`):

Transcript language code; falls back to the video's original language.

## `testUserId` (type: `string`):

Internal: only the actor owner can use it, to run moments as another user.

## `healthCheck` (type: `string`):

Internal: only the actor owner can use it; run or fail.

## Actor input object example

```json
{
  "mode": "segment",
  "url": "https://youtu.be/H0XScE08hy8",
  "query": "where does he explain the pricing",
  "topK": 5,
  "start": "1:00",
  "end": "1:30",
  "every": 15,
  "maxFrames": 20,
  "quality": "standard",
  "lang": "en"
}
```

# Actor output Schema

## `results` (type: `string`):

Dataset items: timestamp, frame image URL, transcript text and, in moments mode, the score; the last item is the run summary.

# 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 = {
    "mode": "segment",
    "url": "https://youtu.be/H0XScE08hy8",
    "start": "1:00",
    "end": "1:30",
    "every": 15
};

// Run the Actor and wait for it to finish
const run = await client.actor("emttn/frameskim").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 = {
    "mode": "segment",
    "url": "https://youtu.be/H0XScE08hy8",
    "start": "1:00",
    "end": "1:30",
    "every": 15,
}

# Run the Actor and wait for it to finish
run = client.actor("emttn/frameskim").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 '{
  "mode": "segment",
  "url": "https://youtu.be/H0XScE08hy8",
  "start": "1:00",
  "end": "1:30",
  "every": 15
}' |
apify call emttn/frameskim --silent --output-dataset

```

## MCP server setup

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

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/mcEe8hYB3rtIGmTkb/builds/lWEIwpKH1qRLqOs4e/openapi.json
