# ytHeatmap — YouTube Attention & Multitmodal Heatmap Engine (`alessiodev-it/ytheatmap`) Actor

Extract YouTube engagement heatmaps & viral moments in sub-seconds. Dual-engine reliability, 90% LLM token reduction, zero GPU costs, anti-bill-shock guarantee.

- **URL**: https://apify.com/alessiodev-it/ytheatmap.md
- **Developed by:** [Alessio Iacoviello](https://apify.com/alessiodev-it) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-usage

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## ytHeatmap

**Extract the attention heatmap from any YouTube video. Native retention data when available. Multimodal signal analysis when it is not. One Actor. Two engines. Zero GPU.**

[![Apify Actor](https://img.shields.io/badge/Apify-Actor-blue)](https://apify.com)
[![Python 3.12](https://img.shields.io/badge/Python-3.12-green)](https://www.python.org)
[![No GPU Required](https://img.shields.io/badge/GPU-Not%20Required-orange)](https://apify.com)
[![Free Tier: 7 Days](https://img.shields.io/badge/Free%20Tier-7%20Days-brightgreen)](https://apify.com)
[![Max Cost: $1.00](https://img.shields.io/badge/Anti--Bill--Shock-Max%20%241.00-red)](https://apify.com)

***

### Why Choose ytHeatmap

#### For LLM and RAG Builders

You need structured attention data inside your prompts without burning thousands of tokens on raw arrays.

- **`made_for_llm: true`** activates Visvalingam-Whyatt geometric simplification, reducing 200+ raw buckets down to 15-40 semantically meaningful points.
- Output arrives as a compact `{ schema, duration_total, average_attention, points }` object -- ready to inject into any prompt template.
- Time unit conversion (millis, seconds, minutes, hours) is handled server-side. No post-processing on your end.
- Average attention score is pre-computed as a 0-100 integer. One number for your retrieval ranking.

**Token savings**: A 60-minute video produces ~720 raw buckets. With LLM mode, you get ~40 points. That is a 94% reduction in prompt tokens.

***

#### For Webhook and Automation Engineers

You need deterministic, schema-stable payloads that never break your downstream pipelines.

- **Fixed JSON contract**: Every response contains `id`, `source_info`, `time_unit`, `made_for_llm`, `duration_ms`, and `heatmap`. No optional fields. No surprises.
- **`source_info.cause`** tells you exactly why a fallback engine was used -- log it, alert on it, branch on it.
- **Webhook-ready**: Push results directly to Zapier, n8n, Make, or any HTTP endpoint via Apify's native webhook dispatch.
- **Idempotent inputs**: Same URL + same config = same output shape. Always.
- Response time: native heatmap extraction completes in under 3 seconds for most videos.

***

#### For Developers

You need clean code, no GPU tax, and a dual-engine SLA that does not silently degrade.

- **Zero GPU cost**: The multimodal engine runs pure-Python Savitzky-Golay smoothing and Visvalingam-Whyatt simplification. No PyTorch. No CUDA. No inference API calls.
- **Dual-engine architecture with explicit fallback**: If YouTube's native heatmap is unavailable (low retention, disabled graph), the multimodal engine (audio RMS delta + transcript text density) activates automatically. You control the behavior via `source_preference`.
- **Proxy rotation built-in**: Datacenter proxies fail fast. Residential proxies retry with fresh IPs. You never configure anything.
- **Source code on the Actor page**: `extract.py`, `transform.py`, `resolve.py`, `validate.py`, `signal.py`. Read it. Fork it. Audit it.
- **Typed pipeline**: `validate -> resolve -> extract -> transform`. Each stage is a pure function. No hidden state.

***

#### For No-Code Users and Analysts

You want to paste a URL, click Start, and get a chart-ready dataset. No code. No terminal. No billing surprises.

- **One input field**: Paste any YouTube URL. That is it.
- **Anti-Bill-Shock guarantee**: A single run will never cost more than $1.00 USD. Period. See the guarantee section below.
- **7-day free tier**: Try unlimited runs for a full week before any charge applies.
- **Dataset export**: Results land in an Apify dataset. Download as CSV, JSON, or Excel. Connect to Google Sheets or Airtable with one click.
- **No technical knowledge required**: The Actor handles video resolution, proxy rotation, format detection, and error recovery internally.

***

### Us vs Others

| Feature | ytHeatmap | GPU-based transcription tools | Manual browser scraping |
|---|---|---|---|
| Native YouTube heatmap extraction | Yes | No | Fragile, breaks on UI change |
| Multimodal fallback (audio + transcript) | Yes, built-in | Requires separate pipeline | Not possible |
| GPU required | No | Yes ($$$) | No |
| Max cost per run | $1.00 (hard cap) | $2-$15+ | Free but unreliable |
| LLM-optimized compact output | Yes (15-40 points) | No | No |
| Savitzky-Golay denoising | Yes | Varies | No |
| Geometric simplification (Visvalingam-Whyatt) | Yes | No | No |
| Proxy rotation with residential fallback | Yes, automatic | Usually no | No |
| Webhook / API integration | Native (Apify) | Varies | No |
| Time unit conversion (ms/s/min/h) | Yes, server-side | Manual | Manual |
| Anti-bot / IP-block handling | Automatic retry + IP rotation | None | Manual |
| Free tier | 7 days | Rarely | N/A |
| Deterministic JSON schema | Yes | No | No |

***

### Dual-Engine Architecture

```mermaid
flowchart TD
    A[User Input: URL + Config] --> B{Validate & Resolve}
    B -->|Invalid URL / Domain| ERR1[ValueError: Rejected]
    B -->|Valid| C{Source Preference}

    C -->|youtube_native| D[Engine 1: YouTube Native Heatmap]
    C -->|multimodal_attention_model| E[Engine 2: Multimodal Signal Analysis]
    C -->|auto| D

    D -->|Heatmap found| F[Raw Heatmap Data]
    D -->|No heatmap + AUTO| G{Duration check}
    D -->|No heatmap + NATIVE only| ERR2[RuntimeError: No native data]
    D -->|IP blocked / 429| PROXY[Proxy Rotation: DC -> Residential]
    PROXY --> D

    G -->|Under 3 hours| E
    G -->|Over 3 hours| ERR3[ValueError: Too long for fallback]

    E --> E1[Download audio via yt-dlp + FFmpeg pipe]
    E --> E2[Fetch transcript JSON3 subtitles]
    E1 --> E3[PCM Delta-RMS per 5s bucket]
    E2 --> E4[Text density per 5s bucket]
    E3 --> F
    E4 --> F

    F --> H{Output Type}
    H -->|raw| I[Return full bucket list as-is]
    H -->|processed| J[Savitzky-Golay Denoise]
    J --> K{made_for_llm?}
    K -->|true| L[Visvalingam-Whyatt Simplification]
    L --> M[Compact LLM Payload]
    K -->|false| N[Full processed array with time conversion]

    M --> O[Push to Dataset + Webhook]
    N --> O
    I --> O
```

***

### Input Schema

```json
{
  "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
  "type": "processed",
  "source_preference": "auto",
  "processing": {
    "time_unit": "seconds",
    "make_for_llm": true
  }
}
```

| Field | Type | Required | Description |
|---|---|---|---|
| `url` | string | Yes | Full YouTube video URL. Shorts are not supported. |
| `type` | string | No | `"raw"` (default) or `"processed"`. Raw skips all post-processing. |
| `source_preference` | string | No | `"auto"` (default), `"youtube_native"`, or `"multimodal_attention_model"`. |
| `processing.time_unit` | string | No | `"millis"` (default), `"seconds"`, `"minutes"`, or `"hours"`. Ignored when type is raw. |
| `processing.make_for_llm` | boolean | No | `false` (default). When true, applies geometric simplification and returns compact format. |

***

### Output Schema

#### Standard Output (`made_for_llm: false`)

```json
{
  "id": "dQw4w9WgXcQ",
  "source_info": {
    "requested": "auto",
    "used": "youtube_native",
    "cause": null
  },
  "time_unit": "seconds",
  "made_for_llm": false,
  "duration_ms": 212000,
  "heatmap": [
    { "start": 0.0, "duration": 5.0, "value": 0.4521 },
    { "start": 5.0, "duration": 5.0, "value": 0.6783 },
    { "start": 10.0, "duration": 5.0, "value": 0.8912 }
  ]
}
```

#### LLM Compact Output (`made_for_llm: true`)

```json
{
  "id": "dQw4w9WgXcQ",
  "source_info": {
    "requested": "auto",
    "used": "multimodal_attention_model",
    "cause": "YouTube native heatmap unavailable (insufficient viewer retention data or disabled graph)."
  },
  "time_unit": "seconds",
  "made_for_llm": true,
  "duration_ms": 212000,
  "heatmap": {
    "schema": ["time", "attention_score_0_to_100"],
    "duration_total": 212.0,
    "average_attention": 63,
    "points": [
      [0.0, 45],
      [15.0, 72],
      [45.0, 91],
      [120.0, 58],
      [195.0, 34],
      [212.0, 22]
    ]
  }
}
```

***

### Webhook Payload Example

Configure an Apify webhook to POST to your endpoint on run completion. The payload your server receives:

```json
{
  "eventType": "ACTOR.RUN.SUCCEEDED",
  "eventData": {
    "actorId": "yt-heatmap-extractor",
    "actorRunId": "abc123xyz",
    "status": "SUCCEEDED"
  },
  "resource": {
    "defaultDatasetId": "ds_abc123",
    "defaultKeyValueStoreId": "kvs_abc123"
  }
}
```

Then fetch the dataset item (or configure a webhook dispatch transform to inline it):

```json
{
  "id": "dQw4w9WgXcQ",
  "source_info": { "requested": "auto", "used": "youtube_native", "cause": null },
  "time_unit": "seconds",
  "made_for_llm": true,
  "duration_ms": 212000,
  "heatmap": {
    "schema": ["time", "attention_score_0_to_100"],
    "duration_total": 212.0,
    "average_attention": 63,
    "points": [[0.0, 45], [15.0, 72], [45.0, 91], [120.0, 58], [195.0, 34], [212.0, 22]]
  }
}
```

This payload is deterministic. Same input produces the same structure. Your downstream parser never encounters missing keys or shape changes.

***

### Anti-Bill-Shock System

**Hard guarantee: a single run will never cost more than $1.00 USD.**

Here is how the pricing works and why it is capped:

| Event | Cost | When charged |
|---|---|---|
| `youtube_native_heatmap` | $0.012 | Native YouTube heatmap found and returned. |
| `mm_attention_model_base` | $0.015 | Multimodal fallback activated (once per run). |
| `minute_processed` | $0.007 per minute | Each minute of audio analyzed in multimodal mode. |

**The cap**: The multimodal engine processes at most 140 billable minutes per run (3 hours of video). At $0.007/minute, that is $0.98 + $0.015 base = $0.995. Under one dollar. Always.

**The free tier**: Your first 7 days are completely free. Unlimited runs. No credit card required to start.

**No charge on failure**: If the Actor fails to deliver a heatmap (video not found, private video, unsupported format), you are not charged. The run terminates with a clear error message and zero cost.

```
Worst-case cost calculation:
  Base:    $0.015
  Minutes: 140 x $0.007 = $0.980
  Total:   $0.995 < $1.00

Best-case cost (native heatmap):
  Total:   $0.012
```

***

### Technical Specifications

| Property | Value |
|---|---|
| Runtime | Python 3.12 (Apify Actor) |
| GPU | Not required |
| Memory | 1024 MB |
| Timeout | 4 hours |
| Max video duration (multimodal) | 3 hours |
| Bucket resolution | 5 seconds |
| Denoising | Savitzky-Golay (window 5-51, polyorder 3) |
| Simplification | Visvalingam-Whyatt with monotonic area enforcement |
| Proxy strategy | Datacenter (fail-fast) then Residential (retry with fresh IP) |
| Anti-bot handling | Automatic IP rotation on 429 / CAPTCHA detection |
| Supported URLs | youtube.com, youtu.be, youtube-nocookie.com (no Shorts) |

***

### Quick Start

```bash
## Via Apify CLI
apify call yt-heatmap-extractor --input='{"url":"https://www.youtube.com/watch?v=dQw4w9WgXcQ","type":"processed","processing":{"time_unit":"seconds","make_for_llm":true}}'
```

```python
## Via Apify Python SDK
from apify_client import ApifyClient

client = ApifyClient("YOUR_API_TOKEN")

run_input = {
    "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
    "type": "processed",
    "source_preference": "auto",
    "processing": {
        "time_unit": "seconds",
        "make_for_llm": True
    }
}

run = client.actor("yt-heatmap-extractor").call(run_input=run_input)

for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["heatmap"])
```

***

### Error Handling

All errors are returned as structured `ValueError` or `RuntimeError` messages. The Actor never fails silently.

| Error | Meaning | Retryable |
|---|---|---|
| `Invalid input: 'url' field is required...` | Missing or empty URL | No |
| `Invalid domain: ... does not belong to YouTube` | Non-YouTube URL | No |
| `YouTube video '...' not found (HTTP 404)` | Video deleted or wrong ID | No |
| `Access forbidden ... (HTTP 403)` | Private / age-restricted / geoblocked | No |
| `Unsupported content: YouTube Shorts` | Shorts URL provided | No |
| `Video too long ... for multimodal fallback` | Over 3 hours, no native data | No |
| `IP_BLOCKED_BY_YOUTUBE` | Rate limited (auto-retried internally) | Yes (automatic) |
| `Failed to fetch YouTube page` | Network failure after all retries | Yes (manual) |

***

### License and Attribution

This Actor is provided as-is on the Apify platform. YouTube is a trademark of Google LLC. This Actor is not affiliated with, endorsed by, or sponsored by YouTube or Google.

***

*Built with pure Python signal processing. No GPUs were harmed in the making of this Actor.*

# Actor input Schema

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

Full URL of the YouTube video (e.g. https://www.youtube.com/watch?v=...). Shorts are not supported.

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

'raw' returns the full bucket list (no post-processing). 'processed' applies smoothing (Savitzky-Golay) and optional simplification.

## `source_preference` (type: `string`):

Prefer YouTube's native heatmap, force multimodal (audio+transcript), or let the actor decide automatically.

## `processing` (type: `object`):

Additional formatting options (only applied when 'type' is 'processed').

## Actor input object example

```json
{
  "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
  "type": "raw",
  "source_preference": "auto",
  "processing": {
    "time_unit": "millis",
    "make_for_llm": false
  }
}
```

# Actor output Schema

## `overview` (type: `string`):

No description

## `heatmap` (type: `string`):

No description

## `downloadCsv` (type: `string`):

No description

## `itemsCount` (type: `string`):

No description

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

// Run the Actor and wait for it to finish
const run = await client.actor("alessiodev-it/ytheatmap").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 = { "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ" }

# Run the Actor and wait for it to finish
run = client.actor("alessiodev-it/ytheatmap").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 '{
  "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
}' |
apify call alessiodev-it/ytheatmap --silent --output-dataset

```

## MCP server setup

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

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/1tYutxeudfnSW3Jao/builds/TRzlIEIYEJaf9MMF2/openapi.json
