# Facebook Ads Analyzer: hooks, offers, CTAs + synthesis brief (`frameprobe/ad-teardown`) Actor

Paste an advertiser's page: AI tears down their real Meta ads (hook, offer, CTA, pacing, on-screen text) and writes a brief of what works plus 3 angles to test, on your key. Free preview fetches the actual ads, no key needed.

- **URL**: https://apify.com/frameprobe/ad-teardown.md
- **Developed by:** [FrameProbe](https://apify.com/frameprobe) (community)
- **Categories:** Marketing, AI, Social media
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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?

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

## Facebook Ads Analyzer: hooks, offers, CTAs + synthesis brief

Paste an advertiser's Facebook page and get their Meta ads torn down, ad by ad, plus one
cross-ad brief of what is working and 3 concrete angles to test. AI watches the actual
creatives: for video ads it reads scene pacing, hook timing in the opening seconds, and the
on-screen text; for image ads it reads the hook, the offer and the CTA off the pixels. The
text-only ad tools cannot see any of that.

**Try it free first:** run with no API key and the Actor still fetches the advertiser's real,
currently-running ads and returns their measured structure (format, dimensions, duration,
scene count, cut cadence) for the first 3, so you see actual competitor ads pulled before you
pay any model provider anything. Add your own Anthropic key to unlock the full AI teardown and
the synthesis brief.

### What one run gets you

For each creative, one structured record:

- `hookStyle` + `hookNotes`: how the ad grabs attention (7 closed classes, so you can count
  them across an account)
- `offerFraming` + `offerNotes`: how the offer is framed (discount, social proof, urgency,
  risk reversal, and so on)
- `ctaPlacement` and the CTA button label
- `onScreenText`: the overlay text, in order, transcribed off the creative
- `productionStyle`: ugc-style, studio-polished, motion-graphics, product-shot, meme-style
- measured video pacing: `sceneCount`, `cutCadenceSeconds`, `durationSeconds` (measured by
  ffmpeg, never guessed by the model)
- `adScore` 0 to 10 (a harsh "worth copying" grade) and `whyItWorks` in plain language
- the ad's own metadata passed through: advertiser, body text, landing URL, running-since
  date, placements

Then one **synthesis brief** per run: `recurringHooks`, `repeatedOffers`, `ctaPatterns`,
`winningPattern`, and exactly 3 `anglesToTest`. The brief is what you read first; the per-ad
records are the evidence.

Real output, unedited: SoFi's currently-running Meta ads torn down into hook, offer, CTA and
score, one row per creative.

![Teardown records for SoFi's real Meta ads: hook style, offer framing, CTA placement, ad score and why each works](https://api.apify.com/v2/key-value-stores/NBthyLSnYGM1muuPw/records/ad-teardown-results.jpg)

### The 60-second start

1. Open this Actor, paste `https://www.facebook.com/<advertiser>` into **startUrls** (or just
   type the brand into **advertiserName**).
2. Click Start with no key: the free preview fetches their real ads and shows the structure.
3. Paste your Anthropic key (console.anthropic.com/settings/keys) and run again: full
   teardowns plus the brief.

The whole input is one field: the advertiser's page URL.

![The input form with an advertiser page URL pasted into the one-step field](https://api.apify.com/v2/key-value-stores/NBthyLSnYGM1muuPw/records/ad-teardown-input.jpg)

The one-step fetch runs [apify/facebook-ads-scraper](https://apify.com/apify/facebook-ads-scraper)
for you and is billed to your Apify account at that scraper's price for your tier (roughly
$0.006 per fetched ad on the free tier, as of August 2026). The run log states the estimate
before fetching and the run summary reports the exact fetched count.

### Chaining and direct input

Already ran the ads scraper? Paste its **datasetId** (or the run ID; the Actor resolves it).
Chain immediately: Meta's media URLs expire within hours, and an expired link fails with a
reason telling you to re-fetch. Power users can pass direct media file URLs in **mediaUrls**.

Meta ads datasets get full support (nested cards, videos, images, DPA catalog ads, carousels:
the first card is analyzed and `cardCount` says how many the ad carries). Other datasets are
best-effort: a recognizable media URL field is picked up, an unknown extension is tried as
video then image.

### Pricing (this Actor's events)

| Event | Price | Charged when |
|---|---|---|
| Actor start | $0.01 | once per run, after validation passes |
| Video ad analyzed | $0.04 | a video creative produced a full teardown record |
| Image ad analyzed | $0.02 | an image creative produced a full teardown record |
| Synthesis brief | $0.15 | the brief was actually produced (needs at least 5 analyzed creatives) |

Never charged: failed downloads, expired URLs, duplicates (the same creative across placement
rows is analyzed once; repeats get a reference row), rejected images, model-reply parse
failures, skipped briefs, and everything in a keyless preview. Your Anthropic account bills
separately for the model calls, at your rates: with the default model, expect a few tenths of
a cent per image and a cent or two per video, plus roughly a cent for the brief.

### When something fails

Every failed row carries a reason that names the fix: expired media URLs point you at
re-fetching, an out-of-credit Anthropic key points at your billing page and says re-running
will not re-charge analyzed creatives, a page pasted into the wrong field names the right one.
The run summary row (always the last record) totals what was analyzed, skipped, failed and
charged.

### Privacy and security posture

Your API key is used for the run's model calls and is never stored, logged, or written to any
output. Media is downloaded through an SSRF-guarded, size-capped, timeout-bounded pipeline;
scraped ad text and pixels are treated as untrusted data end to end, and every model-derived
field is sanitized and pinned to closed lists before it reaches your dataset.

# Actor input Schema

## `startUrls` (type: `array`):

The one-step input: paste a Facebook Page URL (facebook.com/nike) or an Ad Library URL. This Actor runs apify/facebook-ads-scraper for you and analyzes what it finds. The fetch is billed to YOUR Apify account at that scraper's price for your tier (roughly $0.006 per ad on the free tier, as of Aug 2026); this Actor's own events are listed separately under Pricing.

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

A brand or page name, like 'nike'. It is turned into a page-URL guess and the run log confirms which page it resolved to; if that is the wrong advertiser, use the exact page URL in the field above instead.

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

Already ran apify/facebook-ads-scraper? Pick its dataset here (or paste the run ID; the Actor resolves it). Chain immediately after the scraper finishes: Meta media URLs expire within hours.

## `mediaUrls` (type: `array`):

Direct https links to video or image files, analyzed as ad creatives. Page links do not belong here: put facebook.com pages in the one-step field above.

## `maxCreatives` (type: `integer`):

Hard stop on how many creatives this run analyzes. At $0.04 per video ad and $0.02 per image ad, 50 creatives is at most a $2.00 ceiling on this Actor's events (your model provider bills separately).

## `synthesisBrief` (type: `boolean`):

After the per-ad records, one cross-ad brief: recurring hooks, repeated offers, CTA patterns, what the winners share, and 3 angles to test. $0.15 per brief, charged only when it is produced, and only when at least 5 creatives analyzed successfully.

## `framesPerVideo` (type: `integer`):

How many stills are sampled from each VIDEO creative and sent to the vision model. Image ads always use exactly one. More frames means a more accurate read and a higher bill from your model provider.

## `includeOnScreenText` (type: `boolean`):

Transcribe the text visible in the creative into the onScreenText field. Matters most for image ads, where the overlay text usually carries the offer.

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

The language for the written fields (hookNotes, whyItWorks, the synthesis brief). Classification values such as hookStyle stay in English so they can be filtered and counted.

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

Your own Anthropic API key, from console.anthropic.com/settings/keys. Analysis runs on your account and you pay Anthropic directly, at their rates. The key is used for the run and never stored or logged. Leave this empty for a free preview: the ads are still fetched (the fetch bills at the scraper's normal price) and the first 3 get their measured structure, with no model called and none of this Actor's per-creative events charged.

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

Which API your key belongs to. v1 supports Anthropic only; more providers are planned.

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

Leave blank for claude-sonnet-5, the balanced default. Set claude-opus-5 for the most capable read, or claude-haiku-4-5 to lower your own inference bill. The id is checked against your account before the run starts.

## Actor input object example

```json
{
  "mediaUrls": [
    "https://media.w3.org/2010/05/sintel/trailer.mp4"
  ],
  "maxCreatives": 20,
  "synthesisBrief": true,
  "framesPerVideo": 6,
  "includeOnScreenText": true,
  "language": "en",
  "visionProvider": "anthropic"
}
```

# Actor output Schema

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

One record per creative (hook, offer, CTA, on-screen text, measured pacing, score, why it works), one synthesis record per run (recurring hooks, repeated offers, CTA patterns, angles to test), and a summary row with the run's totals and charges. Failed and duplicate rows carry a specific reason and are never charged.

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

The stills sampled from each video creative (dense over the opening seconds) and the image creatives themselves, keyed frame-<adArchiveId>-NN and referenced by each record's frameKeys.

# 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 = {
    "mediaUrls": [
        "https://media.w3.org/2010/05/sintel/trailer.mp4"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("frameprobe/ad-teardown").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 = { "mediaUrls": ["https://media.w3.org/2010/05/sintel/trailer.mp4"] }

# Run the Actor and wait for it to finish
run = client.actor("frameprobe/ad-teardown").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 '{
  "mediaUrls": [
    "https://media.w3.org/2010/05/sintel/trailer.mp4"
  ]
}' |
apify call frameprobe/ad-teardown --silent --output-dataset

```

## MCP server setup

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

```

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/TpTKRJItLh6IeZ8El/builds/gS89O1biDr3ae4dQD/openapi.json
