# Video Ad Creative Breakdown - TikTok, Reels, YouTube & Files (`kantolabs/video-creative-breakdown`) Actor

Break down public TikTok, Instagram Reels and YouTube videos/Shorts or your own ad files: hook (speech + on-screen text of the first 3 s), shot list with keyframes, transcript, pacing and hook/body/CTA. No LLM inside. $0.025 per video.

- **URL**: https://apify.com/kantolabs/video-creative-breakdown.md
- **Developed by:** [Kanto Labs](https://apify.com/kantolabs) (community)
- **Categories:** Videos, AI
- **Security:** Limited permissions
- **Stats:** 1 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $20.00 / 1,000 videos

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

### What does Video Ad Creative Breakdown do?

**Video Ad Creative Breakdown** turns **public TikTok, Instagram Reels and YouTube videos and Shorts**, or your own **ad and short-form video files**, into a structured **creative breakdown**: the **hook** (what is said and what is written on screen in the first 3 seconds, plus the first frame), a **shot list** with a keyframe, on-screen text and speech for every shot, the full **transcript**, **pacing** numbers (cuts per minute, average shot length, words per minute, how much of the video has text on screen) and a simple **hook / body / CTA** structure. Each video also gets a **contact sheet** image, a **JSON** file and a ready-to-paste **Markdown creative brief**.

Paste **public video links** (a TikTok video, an Instagram reel or post, a YouTube video or Short) or **direct links to video files** (your creatives on Google Drive, Dropbox, S3/R2/GCS, a CDN). One row per video, **$0.025 per video** up to 3 minutes - the same price for links and files.

### Who is it for?

- **Brands and in-house creative teams** auditing their own ad library: which hooks, pacing and CTAs the winning ads share.
- **Performance and UGC agencies** that receive dozens of creator videos a week and need the same teardown for every one.
- **Media buyers** tagging creatives before a test: hook type, cut speed, text density, CTA.
- **Ad and UGC researchers** studying public TikToks, Reels and Shorts: paste the links, get the same teardown for every video.
- **Teams with ad files** saved from ad libraries or their own ad manager (link the files directly).
- **AI workflows**: the brief and JSON are made to be pasted into ChatGPT, Claude or Gemini, or sent through n8n / Make / Zapier.

### What you get for every video

| Part | What is inside |
|---|---|
| **Hook** | speech of the first 3 s, on-screen text of the first 3 s, first-frame image, shots in the hook, time of the first cut, first full sentence |
| **Shot list** | start / end / length, transition (cut, dissolve, fade), keyframe image, on-screen text and speech per shot |
| **Transcript** | Whisper speech recognition with word timing, split into timed sentences, language detected |
| **On-screen text** | every caption, title, offer and end card with start / end time and position; logos and watermarks flagged as persistent |
| **Pacing** | shot count, cuts per minute, average / median / shortest / longest shot, time to first cut, words per minute, speech share, text-on-screen share |
| **Structure** | hook / body / CTA sections with their time ranges, shots, speech and text |
| **CTA** | the closing on-screen text and closing line, plus signals found in them: links, phone numbers, @handles, prices / % off, and CTA phrases (shop now, visit, sign up, download, use code...) |
| **Files** | contact sheet (one keyframe per shot, labelled), full JSON, Markdown creative brief |

### How the breakdown is made (no LLM inside)

Nothing in the output is written by a language model, so the same video always gives the same breakdown:

- **Shots** come from our own cut detector (the one behind [Video Scene Detection](https://apify.com/kantolabs/video-scene-detection)): hard cuts, dissolves and fades, with flash and camera-motion rejection.
- **Speech** is OpenAI Whisper (base model) running inside the Actor, with word timestamps.
- **On-screen text** is PP-OCR (the engine behind [Video OCR](https://apify.com/kantolabs/video-ocr)), read twice a second in the hook, about once a second after that (wider on long videos), and at least once in every shot.
- **Hook / body / CTA** is a fixed rule: the hook is the first 3 seconds (you can change it); the CTA is the closing section: the last on-screen text (end card) and the last spoken sentence, when they start in the closing part of the video (last 35 %, at most the last 30 s). The CTA section starts at the end card, or at the last line when that line leads straight into the card (or when there is no card). Everything in between is the body. `cta.hasSignals` tells you whether a link, phone number, @handle, price or call-to-action phrase was actually found there.

That makes the output a **clean, factual input for your own AI**: paste the Markdown brief (and the contact sheet) into ChatGPT or Claude and ask for a hook rating, hook variations, a script rewrite or a comparison across ads.

### How to break down an ad video

1. Put one or more links in **Video links**, one per line: public **TikTok** videos (including `vm.tiktok.com` share links), **Instagram** reels or single-video posts, **YouTube** videos or **Shorts**, or **direct video file links** (MP4, MOV, WEBM, MKV, M4V...). Google Drive and Dropbox share links work when shared as "anyone with the link".
2. Optional: set the **spoken language** (auto-detect by default), the **on-screen text script** (English by default; Latin, Cyrillic, Korean, Arabic, Devanagari, Greek, Thai available) and the **hook length**.
3. Click **Start**. Each video becomes one row; open **Creative brief (.md)** or the **contact sheet** from the Videos view, or use the Shot list and Transcript views.

### TikTok, Instagram and YouTube links

- **Public videos only.** The Actor visits the link like a logged-out viewer (no account, no cookies) through Apify Proxy. Private, removed, age-restricted, members-only or region-locked videos cannot be read - they become **free** rows with the reason.
- **One video per link.** Channels, profiles, playlists, hashtags, photo slideshows and live streams are refused (free).
- Platforms sometimes ask logged-out visitors to sign in ("confirm you're not a bot"). The Actor retries on other proxy IPs; if the platform still refuses, the row is **free** and says so - run that link again later. This happens most on **YouTube**.
- Video is fetched at up to 1080p (H.264 preferred), so platform videos are billed at the base price.
- Rows from links also carry `platform`, `sourceUrl`, `videoId`, `title`, `author`, `authorHandle`, `caption`, `uploadDate`, `viewCount`, `likeCount` and `commentCount` (whatever the platform shows publicly; empty when it does not).
- Only analyse videos you are allowed to use: your own, your clients', or for research and commentary. Facebook, X, Vimeo, Twitch and other sites stay unsupported.

### Input example

```json
{
  "videoUrls": [
    "https://www.tiktok.com/@brand/video/7300000000000000000",
    "https://www.instagram.com/reel/AbCdEfGhIjK/",
    "https://www.youtube.com/shorts/AbCdEfGhIjK",
    "https://your-bucket.s3.amazonaws.com/ads/summer-sale-v3.mp4",
    "https://drive.google.com/file/d/1AbC.../view?usp=sharing"
  ],
  "language": "auto",
  "ocrLanguage": "en",
  "hookSeconds": 3
}
```

### Output example

One row per video. Real output of the built-in sample (a 30-second public-domain CDC TV ad), shortened - the full row also has every shot, sentence and text segment, plus `structure` with the speech and text of each section:

```json
{
  "fileName": "sample-creative-ad.mp4",
  "status": "ok",
  "durationSeconds": 30.21,
  "aspectRatio": "16:9",
  "orientation": "horizontal",
  "hook": {
    "seconds": 3,
    "speech": "",
    "onScreenText": ["A TIP FROM A
FORMER
SMOKER"],
    "onScreenTextJoined": "A TIP FROM A FORMER SMOKER",
    "frameUrl": "https://api.apify.com/v2/key-value-stores/<store>/records/...-hook-first-frame.jpg?signature=...",
    "shotCount": 1,
    "firstCutSeconds": null,
    "firstSentence": "My name is Brett and I used to smoke."
  },
  "pacing": {
    "shotCount": 4, "cutCount": 3, "cutsPerMinute": 6, "averageShotSeconds": 7.54, "medianShotSeconds": 3.45,
    "shortestShotSeconds": 3.04, "longestShotSeconds": 20.22, "timeToFirstCutSeconds": 3.04,
    "wordCount": 47, "wordsPerMinute": 93, "speechShare": 0.398, "textOnScreenShare": 0.47
  },
  "cta": {
    "found": true,
    "startSeconds": 26.46,
    "onScreenText": "YOU CAN QUIT.
FOR FREE HELP, VISIT
CDC.GOV/TIPS
CDC
Brought to you by the U.S. Department of Health and Human Services
#CDCTips",
    "speech": "For free help visit cdc.gov slash tips",
    "signals": ["link: CDC.GOV/TIPS", "link: cdc.gov", "phrase: visit"],
    "hasSignals": true
  },
  "shots": [
    {
      "index": 2, "start": 3.036, "end": 23.257, "durationSeconds": 20.22, "transition": "cut",
      "keyframeUrl": "https://api.apify.com/v2/key-value-stores/<store>/records/...-shot002-00013000ms.jpg?signature=...",
      "onScreenText": ["Brett, 49
New Mexico"],
      "speech": "My name is Brett and I used to smoke. I quit. But not until I got a serious gum disease. ..."
    }
  ],
  "transcript": {
    "status": "ok", "language": "en", "model": "Whisper base", "wordCount": 47,
    "sentences": [{"start": 3.18, "end": 5.06, "text": "My name is Brett and I used to smoke."}]
  },
  "onScreenText": [
    {"start": 3, "end": 7.5, "text": "Brett, 49
New Mexico", "position": "bottom", "confidence": 0.9731, "isPersistent": false}
  ],
  "contactSheetUrl": "https://api.apify.com/v2/key-value-stores/<store>/records/...-contact-sheet.jpg?signature=...",
  "briefUrl": "https://api.apify.com/v2/key-value-stores/<store>/records/...-creative-brief.md?signature=...",
  "jsonUrl": "https://api.apify.com/v2/key-value-stores/<store>/records/...-breakdown.json?signature=...",
  "billingMultiplier": 1,
  "billedVideos": 1,
  "billedExtraMinutes": 0
}
```

The Console has three views: **Videos** (contact sheet, hook, pacing, CTA, brief link), **Shot list** (one line per shot with its keyframe) and **Transcript**.

### How much does it cost?

Pay per event, no subscription and no start fee beyond Apify's tiny default:

| Event | Price | When |
|---|---|---|
| **Video** | **$0.025** | each video broken down; covers the first 3 minutes |
| **Extra minute** | $0.01 | each started minute beyond 3 minutes |

Worked examples: **100 ads of 15-60 seconds = $2.50**. A 4:30 video = $0.025 + 2 x $0.01 = $0.045. Silver and Gold Apify plans get 10 % and 20 % off.

Videos up to 1080p (including vertical 1080x1920 and 1080x1350) are billed as above. Larger frames cost more to decode, so each started 2.2 megapixels counts once: **1440p = 2 videos, 4K = 4 videos** (the row shows `billingMultiplier` and a note). Export or upload ads at 1080p to pay the base price.

**Free**: private, removed or blocked TikTok / Instagram / YouTube videos, unsupported sites, failed downloads (404, private file, web page, image or audio file instead of a video), unreadable files and files without a picture. Every free row says why, in plain words. Set **Max cost per run** and the Actor stops cleanly before a video that would go over it (that video and the rest get free "skipped" rows). The run **timeout** is respected the same way: a video that cannot finish before your run timeout is not started (or is stopped), gets a free "skipped" row that names the timeout to set, and the run still ends as Succeeded. Skipped rows carry `skipReason`: `max-cost` or `timeout`. Short API / n8n / AI-agent timeouts: allow about 70-100 s for one 30-second video and about 50 s more for each further one.

### Good to know

- **Videos without speech** (music-only ads, silent text ads) are still broken down - shots, on-screen text, pacing - and the transcript says "no-speech" or "no-audio".
- **CTA phrases** are matched in English; links, phone numbers, @handles and prices are found in any language.
- **Whisper base** can misspell brand and product names; **OCR** can misread very stylised fonts, and moving or word-by-word captions may appear as several pieces.
- **Animated titles** that jump between positions can count as cuts; very slow dissolves may be missed.
- Videos up to 15 minutes by default (up to 120 with "Max video length").

### Related Kanto Labs Actors

- [Video OCR](https://apify.com/kantolabs/video-ocr) - every piece of on-screen text in a video, with SRT/VTT.
- [Video Scene Detection & Shot List](https://apify.com/kantolabs/video-scene-detection) - cut timecodes, CSV, EDL and clips for editors.
- [Video to LLM Context](https://apify.com/kantolabs/video-to-llm-context) - keyframes + transcript per scene for AI agents.
- [Video Frame Extractor](https://apify.com/kantolabs/video-frame-extractor) - keyframes, scene frames and thumbnails.

### FAQ

**Can it analyse a TikTok, Reels or YouTube link?**
Yes - public single videos, logged out, at the same price as a file. If the platform will not serve the video to a logged-out visitor, the row is free and says why.

**Does it use ChatGPT or another AI to write the breakdown?**
No. Speech recognition (Whisper) and text recognition (PP-OCR) run inside the Actor; everything else is measured or rule-based. That keeps it cheap and repeatable, and you keep full control of the AI step.

**How accurate is the hook / body / CTA split?**
It is a rule, not an opinion: hook = first N seconds, CTA = closing text + closing line. It is right for typical direct-response ads; for stories without an end card the CTA may be missing (the row says so).

**Is my video stored?**
Only the outputs (images, JSON, brief) are saved in your own Apify storage. The downloaded video is deleted when the run ends.

**Is this an official TikTok, Instagram or YouTube tool?**
No. It is an independent Actor by Kanto Labs and is not affiliated with or endorsed by TikTok, Instagram/Meta or YouTube/Google.

**Something looks wrong?**
Open an issue in the Issues tab with the video link and what you expected - we answer quickly.

# Actor input Schema

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

One per line. PUBLIC TikTok videos, Instagram reels/posts and YouTube videos or Shorts (single videos, no channels or playlists; private, removed, age-restricted or blocked videos are free error rows), or direct links to video FILES (MP4, MOV, WEBM...): your storage, S3/R2/GCS, a CDN, an Apify key-value store, Dropbox or Google Drive share links. Facebook, X and other sites are refused.

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

'auto' detects the language of the voice-over. Or a 2-letter ISO 639-1 code (en, es, de, fr, pt, ja, zh...) - recommended for very short clips, where detection has little speech to go on.

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

Script of the text burned into the video (captions, titles, offers). `en` also reads Chinese and Japanese. Use `latin` for accented languages (FR, DE, ES, PT...), `cyrillic` for RU/UK/BG.

## `hookSeconds` (type: `integer`):

How many opening seconds count as the hook (speech, on-screen text and shots in the hook). 3 s is the usual scroll-stop window.

## `maxDurationMinutes` (type: `integer`):

Longer videos are skipped and not charged. Every started minute beyond the first 3 is billed as an extra minute. Up to 120.

## `maxVideoSizeMb` (type: `integer`):

Larger files are skipped (free).

## `downloadTimeoutSecs` (type: `integer`):

Give up on a video that does not download within this time (free).

## Actor input object example

```json
{
  "videoUrls": [
    "https://api.apify.com/v2/key-value-stores/aXcXwfChlioPWAUJv/records/sample-creative-ad.mp4?signature=1yQpP7ggbJGjS4rGodq21"
  ],
  "language": "auto",
  "ocrLanguage": "en",
  "hookSeconds": 3,
  "maxDurationMinutes": 15,
  "maxVideoSizeMb": 1000,
  "downloadTimeoutSecs": 600
}
```

# Actor output Schema

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

No description

## `files` (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 = {
    "videoUrls": [
        "https://api.apify.com/v2/key-value-stores/aXcXwfChlioPWAUJv/records/sample-creative-ad.mp4?signature=1yQpP7ggbJGjS4rGodq21"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("kantolabs/video-creative-breakdown").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://api.apify.com/v2/key-value-stores/aXcXwfChlioPWAUJv/records/sample-creative-ad.mp4?signature=1yQpP7ggbJGjS4rGodq21"] }

# Run the Actor and wait for it to finish
run = client.actor("kantolabs/video-creative-breakdown").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://api.apify.com/v2/key-value-stores/aXcXwfChlioPWAUJv/records/sample-creative-ad.mp4?signature=1yQpP7ggbJGjS4rGodq21"
  ]
}' |
apify call kantolabs/video-creative-breakdown --silent --output-dataset

```

## MCP server setup

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

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/VPTWXEQguJsSTqoeR/builds/4V1INi6FoRPkZtAu0/openapi.json
