# Ad Headline and CTA Intelligence (`danielmichalek/ad-headline-cta-intelligence`) Actor

Find repeated headlines and phrases across up to 500 supplied ads. The report compares CTA labels and character counts, with original ad IDs in the CSV. Send your ad JSON to the Actor to inspect the copy before your next creative test.

- **URL**: https://apify.com/danielmichalek/ad-headline-cta-intelligence.md
- **Developed by:** [Daniel Michałek](https://apify.com/danielmichalek) (community)
- **Categories:** Marketing
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$30.00 / 1,000 ad copy corpus reports

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

## Ad Headline and CTA Intelligence

Compare supplied ad copy before preparing a client report or the next creative brief. The Actor groups repeated headlines and text, shows shared phrases with exact ad IDs, and compares explicit CTA labels across a corpus.

### What it does

It processes one to 500 supplied advertisements without opening websites, connecting ad accounts or calling an AI model. One complete dataset report contains per-ad rows, original text, Unicode character and word counts, duplicate groups, repeated phrases, supplied CTA distribution and custom-limit findings.

Repetition is descriptive. It does not establish creative fatigue, expected CTR, Quality Score, policy compliance or sales. Missing CTA means that no nonblank CTA label was supplied; it is not inferred from the ad text. The product does not scrape Meta, Google or TikTok.

### Input

Use the example below or examples/input.json. Each ad needs a unique ASCII id of 1 to 64 characters starting with a letter or digit and a nonblank headline. Optional text and cta must be strings; explicit null is rejected. Unknown properties are rejected.

Headline, body and CTA are bounded at 300, 2,000 and 80 Unicode code points respectively. The complete serialized input is limited to1,500,000 UTF8 bytes and100,000 Unicode words. Oversized inputs fail before artifact or dataset delivery. Character counts use original code points rather than UTF16 units or grapheme clusters.

limits can contain headlineCharacters, textCharacters and ctaCharacters. Only supplied limits are checked; equality is allowed. These limits belong to the customer and are not advertised platform rules. maxPhrases caps the returned phrase list at 1..100; phraseLength is 2..4 words; minPhraseAds is 2..500. maxItems is fixed at 1 report.

### Output

AD_COPY_AUDIT and the dataset row contain rows, findings, duplicateHeadlines, duplicateTexts, ctaDistribution and phrases. Every duplicate or repeated phrase lists the corresponding ad IDs. Counts cover all accepted ads; complete:true does not mean a full market sample. matchingPhraseCount and phrasesTruncated disclose the top-phrase cap.

Duplicate keys use NFKC, lowercase, whitespace collapse and trim; punctuation remains significant. Original supplied strings remain in JSON. Words use Unicode letters/numbers with internal apostrophes. Repeated phrases count distinct ads, do not cross headline/body boundaries and exclude CTA labels. There is no stopword removal or semantic grouping. Stable ties use code-unit lexical order.

AD_COPY_CSV has one row per ad and quotes all cells, including newlines. Formula-like cells receive an apostrophe; numeric-only IDs also receive an apostrophe to preserve zeros and precision in spreadsheet imports. Other accepted IDs, such as dates or scientific-looking strings, can also be auto-converted by spreadsheets. Import the ID column as text; automatic spreadsheet interpretation is not guaranteed. JSON IDs remain exact. Automated CSV consumers must account for these intentional spreadsheet-safety prefixes.

REPORT is a brief Markdown summary. Private input and ad text are stored in the run's dataset/KV outputs, so configure retention and access appropriately. Text is not sent to an LLM or a third-party analyzer and is not written to logs.

### Pricing and cost control

The active Store price is USD 0.03 for one copy-audit-report covering up to 500 supplied ads. A controlled cloud run with four supplied ads delivered one report and confirmed one priced event. That run recorded USD 0.000394 of platform usage, while production cost depends on input and storage. Send your ad JSON to compare the supplied copy in a complete report.

PRICING.json preserves the original engineering proposal and its USD 0.002 planning allowance. The historical USD 0.003 adjacent benchmark refers to base ad extraction events. Current prices are shown in the Store, and production margin requires further cost measurements.

There is one dataset emission and intended named event per complete report. Artifacts are saved before emission, and data before the shared charger. A paid event must be confirmed before the delivery marker is complete. Invalid input, failed storage or uncertain delivery do not trigger an automatic retry. Local unmonetized SDK runs have zero named charges.

### Limitations and fair use

The supplied corpus is not independently verified advertising data or a representative sample. Only process text you may use. The Actor does not send campaigns, change budgets, fetch links, contact leads, or validate advertising claims. Custom-limit findings are checks against your input, not platform approval.

Unknown pricing stops delivery before the PREPARED marker or emission. A definite SDK charging model and pricing fingerprint are required; a pricing change cannot reuse a completed delivery. A PREPARED marker or unconfirmed paid event requires reconciliation. Completed same-input replay verifies the runtime fingerprint, JSON/CSV/report hashes and the physical single dataset row, then performs no emission. Changed source/input or missing output requires reconciliation. Do not clear storage to force retry. A new intentional run has separate storage.

### Example

```json
{
  "ads": [
    {
      "id": "meta-1",
      "headline": "Free delivery for teams",
      "text": "Compare annual plans and see included services.",
      "cta": "Compare plans"
    },
    {
      "id": "meta-2",
      "headline": "  FREE delivery for teams ",
      "text": "Compare annual plans with included support.",
      "cta": "Compare plans"
    },
    {
      "id": "search-1",
      "headline": "Try the workflow demo today",
      "text": "See the demo before you choose a plan.",
      "cta": "Book a demo"
    },
    {
      "id": "search-2",
      "headline": "Try the workflow demo today",
      "text": "See the demo before you choose a plan."
    }
  ],
  "limits": {
    "headlineCharacters": 24
  },
  "maxPhrases": 20,
  "minPhraseAds": 2,
  "phraseLength": 2,
  "maxItems": 1
}
```

The example input was verified in a public Apify build with one complete dataset report, JSON, CSV and a readable summary. A separate local SDK capacity check covered 500 supplied ads. The repository includes an icon, schemas, a pinned dependency lock, a Dockerfile and offline tests. Customer revenue verification is tracked separately from the owner's acceptance runs.

# Actor input Schema

## `ads` (type: `array`):

1 to500 objects with unique id, nonblank headline and optional text/cta strings. Use only data you may process. No URL is fetched. Hard caps: headline300, text2000, CTA80 Unicode code points; serialized input1.5MB and total100000 words.

## `limits` (type: `object`):

Optional headlineCharacters1..300, textCharacters1..2000, ctaCharacters1..80. These are your constraints, not platform rules. Omit a key to skip that check.

## `maxPhrases` (type: `integer`):

Return the top1 to100 phrases; all ads are still analyzed. Inspect phrasesTruncated and matchingPhraseCount.

## `minPhraseAds` (type: `integer`):

A phrase must occur in at least this many distinct ads; repetition within one ad counts once.

## `phraseLength` (type: `integer`):

Exact phrase length2 to4. Headline and body are separate; CTA labels are excluded.

## `maxItems` (type: `integer`):

Fixed at1. One dataset row contains the entire corpus report, with up to500 per-ad rows.

## Actor input object example

```json
{
  "ads": [
    {
      "id": "meta-1",
      "headline": "Free delivery for teams",
      "text": "Compare annual plans and see included services.",
      "cta": "Compare plans"
    },
    {
      "id": "meta-2",
      "headline": "  FREE delivery for teams ",
      "text": "Compare annual plans with included support.",
      "cta": "Compare plans"
    },
    {
      "id": "search-1",
      "headline": "Try the workflow demo today",
      "text": "See the demo before you choose a plan.",
      "cta": "Book a demo"
    },
    {
      "id": "search-2",
      "headline": "Try the workflow demo today",
      "text": "See the demo before you choose a plan."
    }
  ],
  "limits": {
    "headlineCharacters": 24
  },
  "maxPhrases": 20,
  "minPhraseAds": 2,
  "phraseLength": 2,
  "maxItems": 1
}
```

# Actor output Schema

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

No description

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

No description

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

No description

## `report` (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 = {
    "ads": [
        {
            "id": "meta-1",
            "headline": "Free delivery for teams",
            "text": "Compare annual plans and see included services.",
            "cta": "Compare plans"
        },
        {
            "id": "meta-2",
            "headline": "  FREE delivery for teams ",
            "text": "Compare annual plans with included support.",
            "cta": "Compare plans"
        },
        {
            "id": "search-1",
            "headline": "Try the workflow demo today",
            "text": "See the demo before you choose a plan.",
            "cta": "Book a demo"
        },
        {
            "id": "search-2",
            "headline": "Try the workflow demo today",
            "text": "See the demo before you choose a plan."
        }
    ],
    "limits": {
        "headlineCharacters": 24
    },
    "maxPhrases": 20,
    "minPhraseAds": 2,
    "phraseLength": 2,
    "maxItems": 1
};

// Run the Actor and wait for it to finish
const run = await client.actor("danielmichalek/ad-headline-cta-intelligence").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 = {
    "ads": [
        {
            "id": "meta-1",
            "headline": "Free delivery for teams",
            "text": "Compare annual plans and see included services.",
            "cta": "Compare plans",
        },
        {
            "id": "meta-2",
            "headline": "  FREE delivery for teams ",
            "text": "Compare annual plans with included support.",
            "cta": "Compare plans",
        },
        {
            "id": "search-1",
            "headline": "Try the workflow demo today",
            "text": "See the demo before you choose a plan.",
            "cta": "Book a demo",
        },
        {
            "id": "search-2",
            "headline": "Try the workflow demo today",
            "text": "See the demo before you choose a plan.",
        },
    ],
    "limits": { "headlineCharacters": 24 },
    "maxPhrases": 20,
    "minPhraseAds": 2,
    "phraseLength": 2,
    "maxItems": 1,
}

# Run the Actor and wait for it to finish
run = client.actor("danielmichalek/ad-headline-cta-intelligence").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 '{
  "ads": [
    {
      "id": "meta-1",
      "headline": "Free delivery for teams",
      "text": "Compare annual plans and see included services.",
      "cta": "Compare plans"
    },
    {
      "id": "meta-2",
      "headline": "  FREE delivery for teams ",
      "text": "Compare annual plans with included support.",
      "cta": "Compare plans"
    },
    {
      "id": "search-1",
      "headline": "Try the workflow demo today",
      "text": "See the demo before you choose a plan.",
      "cta": "Book a demo"
    },
    {
      "id": "search-2",
      "headline": "Try the workflow demo today",
      "text": "See the demo before you choose a plan."
    }
  ],
  "limits": {
    "headlineCharacters": 24
  },
  "maxPhrases": 20,
  "minPhraseAds": 2,
  "phraseLength": 2,
  "maxItems": 1
}' |
apify call danielmichalek/ad-headline-cta-intelligence --silent --output-dataset

```

## MCP server setup

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

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/TxK2dkF3EYUhTRfkF/builds/TqLppk5Qsl7htcbnH/openapi.json
