# YouTube Most Replayed Scraper - Heatmap Peaks & Timestamps (`neverempty/youtube-most-replayed-scraper`) Actor

Find the most replayed moments of any YouTube video: the top peaks of YouTube's own Most replayed heatmap as timestamped links, the chapter each peak falls in and the caption line spoken there, plus the raw heatmap segments. Videos without a heatmap are listed free.

- **URL**: https://apify.com/neverempty/youtube-most-replayed-scraper.md
- **Developed by:** [NeverEmpty](https://apify.com/neverempty) (community)
- **Categories:** Social media, Videos, Automation
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.65 / 1,000 video returneds

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

## YouTube Most Replayed Scraper - Heatmap Peaks, Chapters & Timestamps

Find the moments viewers replay most in any YouTube video. For each video you get the **top peaks of YouTube's own "Most replayed" heatmap as timestamped links** that start playback right there, the **chapter** each peak falls in, and the **caption line spoken at that moment** - plus the raw heatmap segments, if you want to draw or analyse the curve yourself.

Videos that have no heatmap are returned as a free row that says so. You pay only for videos that have one.

### Best for

- **Clipping and Shorts** - jump straight to the parts people rewatch instead of scrubbing through a long video.
- **Content research** - see which chapter and which sentence held attention, across a list of your own or competitors' videos.
- **Heatmap analysis** - the full `heatmap` array (start, duration, intensity 0 to 1) for every video, ready for a chart.

### What one row looks like

Measured on 2026-09-14 on "But what is a neural network? | Deep learning chapter 1" (3Blue1Brown, `aircAruvnKk`):

```json
{
    "status": "ok",
    "videoId": "aircAruvnKk",
    "title": "But what is a neural network? | Deep learning chapter 1",
    "channelTitle": "3Blue1Brown",
    "lengthSeconds": 1120,
    "topMomentTimestamp": "8:35",
    "topMomentUrl": "https://www.youtube.com/watch?v=aircAruvnKk&t=515s",
    "topMomentIntensity": 0.8682,
    "topMomentChapter": "Why layers?",
    "topMomentTranscript": "The goal is to have some mechanism that could conceivably combine pixels into edges, or edges into patterns, or patterns into digits. And to zoom in on one very specific example, let's say the hope is for one particular neuron in the second layer to pick up",
    "topMoments": [
        { "rank": 1, "source": "youtube-most-replayed-label", "startSeconds": 515.2, "endSeconds": 526.4, "timestamp": "8:35", "url": "https://www.youtube.com/watch?v=aircAruvnKk&t=515s", "intensity": 0.8682, "chapterTitle": "Why layers?", "transcriptText": "The goal is to have some mechanism that could conceivably combine pixels into edges ..." },
        { "rank": 2, "source": "youtube-most-replayed-label", "startSeconds": 627.2, "endSeconds": 638.4, "timestamp": "10:27", "url": "https://www.youtube.com/watch?v=aircAruvnKk&t=627s", "intensity": 0.828, "chapterTitle": "Edge detection example", "transcriptText": "So a common thing to do is to pump this weighted sum into some function ..." }
    ],
    "momentSpacingSeconds": 56,
    "openingExcludedSeconds": 33.6,
    "mostReplayedLabels": [
        { "startSeconds": 504, "endSeconds": 582.4, "labelSeconds": 515.2, "timestamp": "8:35", "url": "https://www.youtube.com/watch?v=aircAruvnKk&t=515s", "label": "Most replayed" }
    ],
    "chapterSource": "description",
    "chapters": [{ "title": "Introduction example", "startSeconds": 0, "timestamp": "0:00", "url": "https://www.youtube.com/watch?v=aircAruvnKk&t=0s" }],
    "transcriptLanguage": "en",
    "transcriptIsAutoGenerated": false,
    "transcriptNote": null,
    "heatmapSegmentCount": 100,
    "heatmapSegmentSeconds": 11.2,
    "heatmap": [{ "startMillis": 0, "durationMillis": 11200, "intensityScoreNormalized": 0.3526 }]
}
```

(Arrays are shortened here; a real row has all top moments, all chapters and all 100 heatmap segments.)

### How the moments are chosen

YouTube's heatmap splits a video into equal segments (100 on every video measured on 2026-09-14: 11.2 seconds each on a 1,120-second video, 0.19 seconds each on a 19-second one) and gives each an intensity from 0 to 1, where 1 is that video's most replayed segment.

Ranking by intensity alone does not work: viewers who start a video always pass through its first seconds, so on popular videos the opening segment is often the strongest (on MrBeast's "$456,000 Squid Game In Real Life!" the first three segments read 1.000, 0.247, 0.160, while YouTube's own "Most replayed" labels sit at 5:54 and 12:35). So `topMoments` is built like this:

1. **YouTube's own "Most replayed" labels come first** (`source: "youtube-most-replayed-label"`), ordered by the intensity of the segment they point at. The moment starts at the label's time.
2. **The opening is excluded**: 3% of the video's length or the first segment, whichever is longer (33.6 seconds on the 1,120-second example). A YouTube label inside the opening is left out of `topMoments` too. The excluded length is returned in `openingExcludedSeconds`, and every label is still returned unchanged in `mostReplayedLabels`.
3. **The rest are heatmap peaks** (`source: "heatmap-peak"`): only segments at least as strong as both neighbours, strongest first, never with intensity 0.
4. **A peak closer than minSecondsBetweenMoments** to a moment already picked is skipped, so the moments are different scenes rather than slices of one peak. The default is automatic: 5% of the video's length, at least 5 seconds (56 seconds on the 1,120-second example). YouTube's own labels are not thinned by this rule.
5. Each moment's `url` starts playback at the moment's start, rounded down to the second. A video can return fewer moments than you asked for.

`mostReplayedLabels` are the "Most replayed" labels YouTube itself shows on the progress bar, returned exactly as YouTube places them (0 to 3 per video in the videos measured on 2026-09-14; PSY - GANGNAM STYLE had 3, one of them at 0:02).

### Chapters and captions

- `chapterTitle` is the chapter whose start is at or before the moment. Chapters come from the video description (`chapterSource: "description"`) or YouTube's automatic chapters (`"auto"`); many videos have neither, and then it is `null`.
- `transcriptText` is the captions spoken during that heatmap segment, up to 300 characters. Uploaded captions in **transcriptLanguage** are preferred, then YouTube's auto-generated ones in that language. If the video has neither, the first available captions are used as they are - nothing is translated - and `transcriptNote` names the language used.
- If a video has no captions, or they cannot be read, the video row is still returned with `transcriptText: null` and `transcriptNote` saying why. Captions never block or change the charge for a row.

**How captions are read, plainly:** the public watch page's own caption links return an empty file (checked again on 2026-09-14), so captions are read through YouTube's player endpoint (`/youtubei/v1/player`) and caption file, the way transcript tools do. YouTube's robots.txt disallows those paths for crawlers. If you prefer not to use them, set **includeTranscript** to `false`: only the public watch page is requested, and everything except `transcriptText` is still returned.

### Rows you are never charged for

- `no-heatmap` - the video page was read in full and YouTube shows no Most replayed graph for this video. In the videos measured on 2026-09-14 the graph was missing on uploads with a few hundred or a few thousand views (for example 222 views) and on a 24/7 live stream. The row still carries the video's title, channel, length and view count.
- `age-restricted` - YouTube asks viewers to sign in to confirm their age, and the page carries no Most replayed graph. It is not retried. (An age-restricted video whose page does carry the graph is returned and charged like any other.)
- `blocked` - YouTube answered with a bot check on every attempt. **A bot check is never reported as "no heatmap"**: measured on 2026-09-14, the bot-check page YouTube sends to Apify's own IP still contains a heatmap for some videos and not for others, so nothing can be concluded from it.
- `incomplete-page` - the page came back without the video's own data on every attempt.
- `no-such-video` - YouTube reports the video as unavailable (wrong id, private or removed).
- `unreadable` - any other page that could not be read.
- `invalid-input` - a line that is not a video URL or id (a channel or playlist URL, for example).
- `duplicate` - the same video given twice; it is read and charged once.
- `not-read` - lines left unread because **maxVideos** or the run's maximum total charge was reached.
- `budget-reached` - a video was read but did not fit inside the maximum total charge; the row says how many were returned.
- `example-input` - the input was empty and one example video was read.

Videos are returned one at a time. When the run's maximum total charge is reached, the next video is not requested.

### Input

| Field | Meaning |
|---|---|
| `videos` | One per line: watch URL, youtu.be link, /shorts/ or /live/ URL, or an 11-character id. |
| `maxVideos` | Maximum video rows to return (1-1,000, default 100). |
| `topMomentsCount` | Top moments per video (1-20, default 5). |
| `minSecondsBetweenMoments` | Minimum spacing between heatmap peaks in seconds; 0 = 5% of the video length, at least 5 seconds. |
| `includeTranscript` | Add the caption line at each moment (default on; see above). |
| `transcriptLanguage` | Preferred caption language code (default `en`). |
| `includeHeatmap` | Include all raw heatmap segments (default on). |
| `useProxy` | Request pages through a proxy (default on). Measured on 2026-09-14: from Apify's own IP 5 of 7 watch page requests were bot checks; through a datacenter proxy 4 of 4 were read in full. |

```json
{
    "videos": ["https://www.youtube.com/watch?v=aircAruvnKk", "dQw4w9WgXcQ"],
    "topMomentsCount": 5,
    "includeTranscript": true,
    "transcriptLanguage": "en"
}
```

### How it reads YouTube

- One watch page per video (`/watch?v=...&hl=en&gl=US`), plus two small requests per video for captions when **includeTranscript** is on.
- The heatmap is read only from the block whose video id matches the video you asked for, and the row is returned only when the page's own video details are present and match that id.
- A bot check, HTTP 429 or a page missing the video data is retried on a new proxy session, up to 3 attempts.
- A short pause between videos.

# Actor input Schema

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

One video per line: a watch URL (https://www.youtube.com/watch?v=...), a youtu.be link, a /shorts/ or /live/ URL, or an 11-character video id. The same video given twice is read and charged once. If left empty, one example video is read and the output says so.

## `maxVideos` (type: `integer`):

How many video rows to return in total (1 to 1,000). You are charged only for videos that have a heatmap and are returned; rows for videos without one are free.

## `topMomentsCount` (type: `integer`):

How many moments to return for each video (1 to 20): YouTube's own Most replayed labels first, then the strongest heatmap peaks outside the opening of the video. Each one has a timestamped link that starts playback there.

## `minSecondsBetweenMoments` (type: `integer`):

A heatmap peak is returned only if it is at least this many seconds away from every moment already picked, so the top moments are different scenes rather than neighbouring slices of the same peak. YouTube's own Most replayed labels are always kept. 0 = automatic: 5% of the video's length, and at least 5 seconds.

## `includeTranscript` (type: `boolean`):

On = for each moment, the captions spoken during that heatmap segment are added as transcriptText (up to 300 characters). This uses YouTube's player endpoint (/youtubei/v1/player) and caption file, the same way transcript tools do; YouTube's robots.txt disallows those paths for crawlers. Off = only the public watch page is requested and transcriptText is empty. If a video has no captions or they cannot be read, the video row is still returned and transcriptNote says why.

## `transcriptLanguage` (type: `string`):

Preferred caption language code (en, es, de, ja...). Uploaded captions in that language are used first, then YouTube's auto-generated ones in that language. If the video has neither, the first available captions are used as they are - nothing is translated - and transcriptNote says which language was used.

## `includeHeatmap` (type: `boolean`):

On = every row also carries heatmap: all segments of YouTube's Most replayed graph (startMillis, durationMillis, intensityScoreNormalized from 0 to 1). Off = only the top moments, labels and chapters.

## `useProxy` (type: `boolean`):

On (recommended) = pages are requested through a proxy. Measured on 2026-09-14: from Apify's own IP, 5 of 7 watch page requests came back as a bot check, and through a datacenter proxy 4 of 4 were read in full. A bot check is retried on a new proxy session, up to 3 attempts, and is never reported as a video without a heatmap.

## Actor input object example

```json
{
  "videos": [
    "https://www.youtube.com/watch?v=aircAruvnKk",
    "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
  ],
  "maxVideos": 100,
  "topMomentsCount": 5,
  "minSecondsBetweenMoments": 0,
  "includeTranscript": true,
  "transcriptLanguage": "en",
  "includeHeatmap": true,
  "useProxy": true
}
```

# Actor output Schema

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

One row per YouTube video with a Most replayed heatmap: the top moments as timestamped links with their chapter and caption line, YouTube's own Most replayed labels, the chapters and the raw heatmap segments. Videos without a heatmap, unavailable videos and bot checks are uncharged rows that say why.

# 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=aircAruvnKk",
        "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("neverempty/youtube-most-replayed-scraper").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=aircAruvnKk",
        "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("neverempty/youtube-most-replayed-scraper").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=aircAruvnKk",
    "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
  ]
}' |
apify call neverempty/youtube-most-replayed-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,neverempty/youtube-most-replayed-scraper"
        }
    }
}
```

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/5TFuJwrPcVgqRax0k/builds/KkykkLvLvdZGd8HUZ/openapi.json
