# Meta Ad Intelligence (`deepmine/meta-ad-intelligence`) Actor

Meta Ad Intelligence is a Meta ads scraper for the Facebook Ad Library: discover who is advertising in your niche across Facebook ads, rank competitors by dominance score and share of voice, flag new entrants, and reveal top ad formats, CTAs, hooks and creative trends for market research.

- **URL**: https://apify.com/deepmine/meta-ad-intelligence.md
- **Developed by:** [DeepMine](https://apify.com/deepmine) (community)
- **Categories:** Social media, Lead generation, Other
- **Features:** Limited permissions
- **Stats:** 10 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.34 / 1,000 ads

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

## Meta Ad Intelligence: Competitor Ads & Creatives

Type a market keyword and see who advertises for it on Facebook and Instagram, ranked, with every ad and free tables by advertiser, landing domain, format and what changed since your last run.

| Advertiser | Ads (of 30) | Share | Avg days running | Formats | Landing domain |
|---|---|---|---|---|---|
| KURU Footwear | 7 | 23.33% | 98 | Dynamic creative (7) | kurufootwear.com |
| HOKA | 4 | 13.33% | 156 | Image (3), Catalog (1) | hoka.com |
| Cholesterol Relief Community | 4 | 13.33% | 142 | Image (4) | shop.pipitea.com |
<sub>Collected 2026-10-18 with the prefilled input (running shoes and protein powder, country US, 30 ads each): the Advertisers table for running shoes.</sub>

**$0.45 per 1,000 ads** on Starter ($0.55 Free, $0.39 Scale, $0.34 Business), ad details included. No start fee, and the five tables are free. The prefilled run (up to 60 ads) costs about $0.03.

### What you can do with it

- **Map a market.** Who advertises for a keyword, product or niche, how many ads each runs, for how long, in which formats and to which sites.
- **Rank competitors.** The Advertisers table orders them by a dominance score (ads × average days running) and their share of the ads found.
- **See where the traffic goes.** The Domains table groups the ads by landing site; the Formats table by image, video, carousel, dynamic creative or catalog.
- **Track changes.** Run it again (or schedule it) and the Changes table lists the ads that are new, back, stopped or no longer listed since the last run of the same search.
- **Study creatives.** Every ad's text, headline, button, landing page, picture, video and cards, with each variant on request.
- **Analyse a competitor.** Paste its Facebook page and get the same tables for its own ads.

No login, no cookies, no API key: the data comes from Meta's public Ad Library, and every table is counted from the rows, with no AI or estimate.

### Input

| Field | What it does |
|---|---|
| 🔎 Market keywords (`searchQueries`) | Ad Library keyword searches, one per line. Each is one market in the tables. |
| 🏢 Competitors (`advertisers`) | Facebook page links (facebook.com/gymshark), page ids, Ad Library links of an advertiser, or page names exactly as they show on the ads. |
| 🌍 Country (`country`) | Where the ads ran: a two-letter code (US, GB, DE, ...) or ALL. Default US. |
| 💯 Max ads (`maxAdsPerQuery`) | Ads per keyword, competitor or URL (default 200), most impressions first. You pay per ad, so this caps the cost. |
| 🧬 Every variant (`includeVariants`) | Also get each creative's other ads (other versions, start dates, placements). Each is an ad row. |
| 📋 Details (`includeAdDetails`) | The advertiser's verification, Instagram account and followers; for ads shown in the EU or UK, reach, reach by country, ages, gender, payer and beneficiary. |
| 🔔 Only new ads (`onlyNewAds`) | Only ads started since the last run that weren't returned before; the tables then count only those. |
| Filters | Ad Library URLs (`startUrls`), ad IDs (`adIds`), ad status, media type, ad category, exact phrase, dates shown. |
| Row layout (`rowLayout`) | Current, or the earlier version's (see below). |

Example: `{"searchQueries": ["running shoes", "trail running shoes"], "country": "US", "maxAdsPerQuery": 200, "rowLayout": "current"}`

### Output

**Ads** (the dataset, one row per ad, the only thing you pay for). The Console shows it as tables; export as JSON, CSV or Excel.

- `pageName`, `pageUrl`, `pageId`: the advertiser.
- `adText`, `headline`, `ctaText`, `landingUrl`, `image`, `videoUrl`, `cards`: the creative (Meta's media links expire about 5 days after the run).
- `creativeHidden`: `true` when Meta shows the ad's picture or video only to logged-in Facebook users (often creator profiles); the row keeps its text, advertiser and dates, with `image` and `videoUrl` empty.
- `format`, `platforms`: Image, Video, Carousel, Dynamic creative, Catalog; Facebook, Instagram, Messenger, ...
- `startedAt`, `lastShownAt`, `daysActive`, `isActive`: the day the ad started and the last day it ran (from the Ad Library), the days between, and whether it still runs.
- `firstObservedAt`, `previouslyObservedAt`: when this Actor first found the ad for the same search, and the last run before this one that did (empty for an ad seen for the first time). These are our observations, not Meta's dates.
- `collationCount`, `adsCount`, `adArchiveId`, `adLibraryUrl`, `rank`, `totalCount`, `searchInput`, and Meta's full record after them.

**Free tables** (Output tab; records in the run's key-value store, not dataset rows, so never charged):

- `MARKET`: one row per search. `totalAds`, `totalAdvertisers`, `topAdvertiser`, `topAdvertiserSharePct`, `topDominanceScore`, `topFormat`, `topCta`, `topAdvertisers`, `topHooks` (most used opening lines), `battleTestedCount`, `gainingTractionCount`, `newTestCount` (ads started 30+, 14-29 and under 14 days ago), `avgAdAgeDays`, `longestRunningAdId`, `topLandingDomain`, `formats`; `scope` says what the counts are of, with `metaCount` (Meta's count for the search), `sampleComplete` and `foundByEarlierSearches`; and the changes: `newAds`, `backAds`, `stoppedAds`, `noLongerListed`, `notInThisSample`, `previousRunAt`.
- `ADVERTISERS`: per search and advertiser page. `advertiserAdCount`, `shareOfVoicePct`, `advertiserAvgAdAgeDays`, `dominanceScore`, `isNewEntrant`, `advertiserTopFormat`, `advertiserTopCta`, then `activeAds`, `oldestStartedAt`, `newestStartedAt`, `longestRunningDays`, `formats`, `topLandingDomain`, `landingDomains`, `creativeGroups`, `platforms`, `firstObservedAt`, `newAds`, `noLongerListed`.
- `DOMAINS`: per search and landing domain: `ads`, `sharePct`, `advertisers`, `topAdvertisers`, `formats`, `activeAds`, `avgAdAgeDays`, start dates.
- `FORMATS`: per search and format: the same counts, with the age bands.
- `CHANGES`: one row per ad that is `new`, `back`, `stopped` or `no longer listed` since the last run of the same search, with its dates.

The rows of `ADVERTISERS`, `DOMAINS` and `FORMATS` add up to the search's `totalAds` (ads without a landing page or format have a row of their own).

#### Signals, and what they mean

- `daysActive` and the age bands: how long the advertiser kept the ad running. `avgAdAgeDays` and `advertiserAvgAdAgeDays` count days since the ad started.
- `dominanceScore`: an advertiser's ads × their average days running, among the ads found. Many ads that keep running score high.
- `shareOfVoicePct`, `sharePct`: the advertiser's (domain's, format's) share **of the ads this run returned for the search**, not of the market, impressions or spend. A search stopped at Max ads counts its most-impressions-first sample; `scope` says which.
- `isNewEntrant`: the advertiser's oldest ad found started at most 30 days ago.
- `collationCount` (same creative) and `creativeGroups`: how many ads use the same creative and text, and how many different creatives an advertiser runs.

These are signals of what advertisers keep running, not results: Meta publishes no sales, clicks, conversions or return on ad spend for commercial ads. Spend and impressions ranges exist only for ads about social issues, elections or politics (set *Ad category*); EU and UK reach comes with *Details* for ads shown there.

#### Tracking changes

Every run keeps a snapshot of each search (the keywords or competitor plus country, status, category, media type, dates and exact phrase: change one and it is another market) in the key-value store `meta-ad-intelligence-snapshots` of your account, and compares with the last run of the same search:

- `new`: not found by any earlier run of the search. `back`: found before, missing from the last run, found again.
- `stopped`: was active, now stopped (only searches with *Ad status* Active and inactive see stopped ads).
- `no longer listed`: the last run found it, this run read the whole search and didn't. A search stopped at Max ads never calls an ad gone; it counts it in `notInThisSample` instead.

The first run of a search is its baseline: it lists no changes. Each ad is one row per run, charged once, even when several searches find it; the later searches count it in `foundByEarlierSearches`.

### Saved inputs from the earlier version

Tasks, schedules and API calls made for the earlier version keep working:

- `searchQueries` is read as before; `advertiserNames` is read as keyword searches, as before (used only when `searchQueries` is empty), and `maxAdsPerAdvertiser` as Max ads (when `maxAdsPerQuery` isn't given).
- A run with one of those fields and no `rowLayout` gets the earlier rows: its keys first, in the same order and form (`rowType`, `query`, `advertiser`, `adArchiveId`, `adAgeDays`, `trendLabel`, `body`, `headline`, `cta` as SHOP_NOW, `format` as VIDEO or DCO, `imageUrl`, `linkUrl`, `startDate` and `endDate` as ISO dates, ...), then the fields above. `rowLayout` set to `current` or `earlier` chooses either way.
- The `market_summary` and `advertiser_summary` rows are now the free `MARKET` and `ADVERTISERS` tables, with the same keys first, instead of paid rows in the dataset.
- Max ads is now exact, and each ad is one row even when two searches find it.

### Pricing

| | Free | Starter | Scale | Business |
|---|---|---|---|---|
| Ad | $0.55 | $0.45 | $0.39 | $0.34 |

Prices per 1,000 ads; ad details included; no start fee. You pay only for ad rows: the five tables, ads not found and failed searches are free. With *Only new ads* you pay only for the new ads. Set a maximum charge in the run options to cap a run.

### Limitations

- The tables count the ads a run returns: raise Max ads for a bigger sample. Meta lists most impressions first, and counts no more than 50,000 ads per search.
- Meta shows commercial ads' spend and reach only for ads shown in the EU or UK (with Details), and spend ranges only for political and issue ads.
- An advertiser given by name must match its page name exactly; a page link or id always works.
- Image and video links expire about 5 days after the run: download what you want to keep.
- About 3 minutes per 1,000 ads.

### FAQ

**Do I need a Facebook account, cookies or an AI key?** No. It reads Meta's public Ad Library, logged out, and counts the tables itself.

**How do I watch a market every week?** Schedule the run with the same input. Each run's `CHANGES` table lists what changed since the one before.

**Does it show which ads make money?** No one can from the Ad Library: Meta publishes no sales or return on ad spend. Long-running ads and creatives used in many ads are the public signals; this Actor gives you both, with their definitions above.

### Related Actors

- [Meta Ad Library API](https://apify.com/deepmine/meta-ad-library): the same ads for any search, with the Ad Library's own filters.
- [Meta Ad Spy](https://apify.com/deepmine/meta-ad-spy): every ad of a competitor, with variants and single ads by ID.
- [Facebook Ads Monitor](https://apify.com/deepmine/facebook-ads-monitor): monitor advertisers for new ads.

### Feedback

Something missing or wrong? Open an issue in the Issues tab and we'll look at it. If Meta Ad Intelligence helps you, a short review on the Store helps others find it.

# Actor input Schema

## `searchQueries` (type: `array`):

Keywords, products or niches, as you'd type them in the Ad Library search box (e.g. running shoes). Each is one search; the tables rank who advertises for it. One per line.

## `advertisers` (type: `array`):

Advertisers to analyse: a Facebook page link (facebook.com/gymshark), a page id, an Ad Library link of the advertiser, or the page name exactly as it shows on its ads. One per line.

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

Two-letter country code where the ads ran (US, GB, DE, ...), or ALL for every country. Each country is its own market: its tables and changes are kept apart.

## `maxAdsPerQuery` (type: `integer`):

Stop each keyword, competitor or URL after this many ads. You pay per ad, so this caps the cost of each one; the tables count these ads (most impressions first). About 3 minutes per 1,000 ads.

## `includeVariants` (type: `boolean`):

Meta groups ads that use the same creative and text, and its results often show one ad per group. Turn this on to also get the group's other ads (other versions, start dates, placements), each as its own ad row, which you pay for like any ad. They count toward Max ads.

## `includeAdDetails` (type: `boolean`):

Also fetch each ad's public details: the advertiser's verification and Instagram account and followers and, for ads shown in the EU or UK, the reach, reach by country, targeted ages and gender, plus the payer and beneficiary. Included in the ad price; each ad needs one more request, so runs are slower.

## `onlyNewAds` (type: `boolean`):

Return only ads started since the last run that this Actor hasn't returned before for the same search, reading up to 1,000 ads past Max ads to find them. You pay only for the new ads, and the tables then count only them. To track a market's changes, schedule the run without it: every run compares with the last one anyway.

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

Searches copied from facebook.com/ads/library in your browser. The filters in the link (country, active status, media type, dates) are used.

## `adIds` (type: `array`):

Single ads: the Library ID the Ad Library shows on each ad, or the ad's link (facebook.com/ads/library/?id=...). Every country and status.

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

Only active ads, only ads that stopped, or both. With both, the changes table also shows ads that stopped since the last run.

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

Only ads with this kind of media.

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

All ads, or only ads about social issues, elections or politics (these carry spend and impressions ranges).

## `exactPhrase` (type: `boolean`):

Match the keywords as an exact phrase instead of any order.

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

Only ads that were shown on or after this date (YYYY-MM-DD), whatever day they started. This is the Ad Library's own date filter.

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

Only ads that were shown on or before this date (YYYY-MM-DD).

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

Current: the rows described in the README. Earlier version's: each row starts with the earlier version's fields (rowType, query, adAgeDays, trendLabel, format as VIDEO, cta as SHOP_NOW, ...), then the current ones. Left empty (tasks, schedules and API calls made for the earlier version), an input with searchQueries, advertiserNames or maxAdsPerAdvertiser gets the earlier layout.

## `proxyConfiguration` (type: `object`):

Residential proxies are the default and recommended: Meta refuses most datacenter IPs past the first page of results.

## Actor input object example

```json
{
  "searchQueries": [
    "running shoes",
    "protein powder"
  ],
  "country": "US",
  "maxAdsPerQuery": 30,
  "includeVariants": false,
  "includeAdDetails": false,
  "onlyNewAds": false,
  "activeStatus": "active",
  "mediaType": "all",
  "adType": "all",
  "exactPhrase": false,
  "rowLayout": "current",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}
```

# Actor output Schema

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

No description

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

No description

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

No description

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

No description

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

No description

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

No description

## `summary` (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 = {
    "searchQueries": [
        "running shoes",
        "protein powder"
    ],
    "country": "US",
    "maxAdsPerQuery": 30,
    "rowLayout": "current",
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "US"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("deepmine/meta-ad-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 = {
    "searchQueries": [
        "running shoes",
        "protein powder",
    ],
    "country": "US",
    "maxAdsPerQuery": 30,
    "rowLayout": "current",
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "US",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("deepmine/meta-ad-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 '{
  "searchQueries": [
    "running shoes",
    "protein powder"
  ],
  "country": "US",
  "maxAdsPerQuery": 30,
  "rowLayout": "current",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}' |
apify call deepmine/meta-ad-intelligence --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,deepmine/meta-ad-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/ek9y54d5g5kCMYcIR/builds/uaCHe1qKYChvG2zYk/openapi.json
