Ads Intelligence Cluster
Answer natural language questions about ads, ad libraries, and competitor advertising activity by routing to the right Apify Actor and delivering a synthesized answer.
CLI rules: Always pass
--user-agent apify-awesome-skills/apify-ads-intelligence
,
(or the relevant
flag on
), and
. The
flag is critical for telemetry — never omit it.
Note on platform coverage
- Meta, Google, TikTok, LinkedIn: real public ad libraries with rich data (creatives, targeting, dates, reach where disclosed).
- X (Twitter): no public ad library exists. Coverage is a best-effort workaround that scrapes a brand's tweets and flags items with non-empty field or containing "Ads" as likely promoted. Always include the caveat in synthesis output.
Note on overlap with
That skill has an
intent that routes to
apify/facebook-ads-scraper
for shallow Meta-ad lookups. This skill is the deep dive across all five platforms. If you only need Meta ads as a side detail of an ecommerce question, stay in
. If ads are the main task, use this skill.
Prerequisites
(No need to check it upfront)
- Apify CLI v1.5.0+ ()
- (recommended for response parsing and filtering; on macOS, on Linux)
- Authentication via one of:
- (OAuth, opens browser)
- env variable (e.g. or file)
- Token from Apify Console → Settings → Integrations
Verify auth:
apify info --user-agent apify-awesome-skills/apify-ads-intelligence
— should show username and userId.
Workflow
Copy this checklist and track progress:
Task Progress:
- [ ] Step 1: Detect intent and select Actor(s)
- [ ] Step 2: Fetch Actor schema
- [ ] Step 3: Ask user preferences (output format, result count, country)
- [ ] Step 4: Run the Actor (or Actors in parallel for cross-platform-audit) and fetch results
- [ ] Step 5: Synthesize a direct answer (not a data dump)
Step 1: Detect Intent and Select Actor
Classify the user's message into an intent, then pick the right Actor.
Intent signals:
| Signals in user message | Intent |
|---|
| "what ads is X running", "competitor [brand] ads", "[brand] FB/Google/TikTok/LinkedIn/X/Twitter ads", "show ads from [page]", "promoted tweets from [brand]" | |
| "ads about [topic]", "find [keyword] ads", "ads for [vertical]", "fitness/fintech/saas ads" | |
| "trending ads", "winning ads", "top ads", "best performing", "long-running ads", "creative inspiration" | |
| "where do these ads go", "landing pages from ads", "click destinations", "ad funnels" | |
| "compare X's ads across platforms", "all ads from [brand]", "cross-platform ad audit" | |
If multiple intents detected, ask: "Do you want [intent A] or [intent B]?"
Actor routing — always try Primary first, switch to Fallback only if it fails or returns 0 results:
| Intent | Platform | Primary Actor | Fallback Actor |
|---|
| Meta (FB/IG) | apify/facebook-ads-scraper
| brilliant_gum/facebook-ads-library-scraper
|
| Google | dz_omar/google-ads-scraper
| solidcode/ads-transparency-scraper
|
| TikTok | brilliant_gum/tiktok-ads-library-scraper
() | silva95gustavo/tiktok-ads-scraper
|
| LinkedIn | silva95gustavo/linkedin-ad-library-scraper
| dz_omar/linkedin-ads-scraper
|
| X (workaround) | apidojo/twitter-scraper-lite
(twitterHandles: [<brand>]
) + heuristic filter | |
| Meta | brilliant_gum/facebook-ads-library-scraper
| apify/facebook-ads-scraper
|
| Google | apify/google-search-scraper
() | — |
| TikTok | brilliant_gum/tiktok-ads-library-scraper
| — |
| LinkedIn | silva95gustavo/linkedin-ad-library-scraper
| — |
| X (workaround) | apidojo/twitter-scraper-lite
() + heuristic filter | |
| Meta | brilliant_gum/facebook-ads-library-scraper
(rank by ) | — |
| TikTok | (sort by CTR / impressions / likes) | brilliant_gum/tiktok-ads-library-scraper
() |
| Google | n/a — fall back to route, filter to active ads | — |
| LinkedIn | n/a — fall back to route, rank by reach | — |
| X | n/a in v1 — no reliable promoted-content signal across timelines | — |
| Meta | brilliant_gum/facebook-ads-library-scraper
(resolveSnapshotUrls: true
) | — |
| Google | apify/google-search-scraper
(, ) | dz_omar/google-ads-scraper
() |
| X | n/a in v1 — heuristics not reliable enough for landing-page extraction | — |
| All five | Run Meta + Google + TikTok + LinkedIn primaries in parallel; X workaround runs separately with caveat. Merge by advertiser. | — |
X (Twitter) heuristic filter — after scraping, flag a tweet as likely promoted if any of the following hold:
- field is non-empty (website cards / CTAs are commonly attached to promoted tweets)
- field contains "Ads" (e.g. "Twitter Ads")
Surface results with the explicit caveat: "X has no public ad library; results below are tweets from the brand's own timeline that match promoted-content heuristics. They will miss promoted-only ads that appear in other users' feeds."
Step 2: Fetch Actor Schema
Fetch the Actor summary, input schema, and README:
bash
# Summary (title, description, pricing, stats)
apify actors info "ACTOR_ID" --user-agent apify-awesome-skills/apify-ads-intelligence --json 2>/dev/null
# Input schema (required and optional parameters; schema lives in
# .taggedBuilds.latest.build.inputSchema as an escaped JSON string)
apify actors info "ACTOR_ID" --user-agent apify-awesome-skills/apify-ads-intelligence --input --json 2>/dev/null
# README (capabilities, examples, gotchas)
apify actors info "ACTOR_ID" --user-agent apify-awesome-skills/apify-ads-intelligence --readme 2>/dev/null
Replace
with the selected Actor (e.g.,
apify/facebook-ads-scraper
).
Step 3: Ask User Preferences
Before running, ask:
-
Output format:
- Quick answer (default) — synthesized answer in chat, no file saved
- CSV — full export saved to disk
- JSON — full export saved to disk
-
Result count — defaults by intent:
| Intent | Default count |
|---|
| 30 |
| 30 |
| 20 |
| 50 |
| 15 per platform |
-
Country — default
. For TikTok library specifically, default
(EU-only) and warn the user; for global TikTok use
. X routes are global by handle/keyword, no country parameter.
Cost safety: Always set a sensible result limit in the Actor input (e.g.,
,
, or the equivalent field per Actor schema). Warn the user before runs of 500+ ads —
apify/facebook-ads-scraper
charges per ad and X primaries charge per tweet.
Step 4: Run the Actor and Fetch Results
Two steps: run the Actor (blocks until done), then fetch dataset items in the requested format.
Run the Actor — returns run metadata as JSON; extract
for the next step:
bash
apify actors call "ACTOR_ID" -i 'JSON_INPUT' \
--user-agent apify-awesome-skills/apify-ads-intelligence --json 2>/dev/null
From the output use
(run ID),
(should be
), and
.
Fetch results — pick the variant based on the user's preference:
bash
# Quick answer: total count + fields + top 5 in chat (no file)
apify datasets info DATASET_ID --json \
--user-agent apify-awesome-skills/apify-ads-intelligence 2>/dev/null \
| jq '{itemCount, fields, consoleUrl}'
apify datasets get-items DATASET_ID --limit 5 \
--user-agent apify-awesome-skills/apify-ads-intelligence --format json 2>/dev/null
# CSV file
apify datasets get-items DATASET_ID \
--user-agent apify-awesome-skills/apify-ads-intelligence --format csv 2>/dev/null > YYYY-MM-DD_filename.csv
# JSON file
apify datasets get-items DATASET_ID \
--user-agent apify-awesome-skills/apify-ads-intelligence --format json 2>/dev/null > YYYY-MM-DD_filename.json
Other
options:
,
,
,
,
. Use
to paginate large datasets.
Tip: for anything more than a quick peek, save the dataset to a local file first (with
/
) and run further analysis from disk.
always streams over the network, so piping it straight into
re-downloads the whole thing every iteration.
Cross-platform audit (parallel runs): For
, kick off Meta + Google + TikTok + LinkedIn primaries in parallel by backgrounding each
invocation with
and calling
before fetching results. Example:
bash
apify actors call "apify/facebook-ads-scraper" -i '<META_INPUT>' \
--user-agent apify-awesome-skills/apify-ads-intelligence --json 2>/dev/null > meta_run.json &
apify actors call "dz_omar/google-ads-scraper" -i '<GOOGLE_INPUT>' \
--user-agent apify-awesome-skills/apify-ads-intelligence --json 2>/dev/null > google_run.json &
apify actors call "brilliant_gum/tiktok-ads-library-scraper" -i '<TIKTOK_INPUT>' \
--user-agent apify-awesome-skills/apify-ads-intelligence --json 2>/dev/null > tiktok_run.json &
apify actors call "silva95gustavo/linkedin-ad-library-scraper" -i '<LINKEDIN_INPUT>' \
--user-agent apify-awesome-skills/apify-ads-intelligence --json 2>/dev/null > linkedin_run.json &
wait
# Then extract each .defaultDatasetId and fetch items per platform; X workaround runs separately with caveat.
Combining with for quick extraction:
Treat
as a complement to
, not a replacement: server-side
/
/
keeps cost and bandwidth down. Use
on a sample item or on a file you already saved.
bash
# Discover real field names from one sample item (Actor outputs vary —
# use this before composing further jq queries)
apify datasets get-items DATASET_ID --limit 1 --format json \
--user-agent apify-awesome-skills/apify-ads-intelligence 2>/dev/null \
| jq '.[0]'
# X heuristic filter on a saved tweets file: keep items with non-empty card
# or source containing "Ads"
jq '[.[] | select((.card != null and .card != "") or (.source != null and (.source | contains("Ads"))))]' \
YYYY-MM-DD_x_tweets.json
Step 5: Analyze Results and Deliver Answer
Synthesize, don't dump. Patterns by intent:
| Intent | What the synthesis surfaces |
|---|
| Total ads found, active vs inactive split, top creative formats, top 5 ad copy snippets, list of unique landing-page domains. For X specifically: total tweets scraped, count flagged as likely-promoted, top 5 flagged tweets with the heuristic-detection caveat. |
| Top 5 advertisers running ads on this keyword, total ads, country split |
| Top 5 by (Meta) or CTR (TikTok), with creative summary, link to Ad Library entry |
| List of unique landing URLs, grouped by domain, with ad counts pointing at each |
| Per-platform ad count and tone summary, then a "where they're spending most" inference |
Suggested follow-ups — keyed off the intent that just ran:
| If user just ran… | Suggest next |
|---|
| (Meta) | Stack with apify-competitor-intelligence
to add their FB Page posts, IG profile, and Google Maps reviews |
| (any) | Stack with ( intent) to detect the platform behind the landing pages, or with to enrich destination domains with contact info |
| (TikTok / Meta) | Stack with apify-influencer-discovery
if any creatives are influencer collabs |
| (Google / Meta) | Stack with to see whether the keyword is rising or falling on Google Trends / Instagram / TikTok |
| Stack with for the brand's organic content side; combined paid + organic picture |
Quirks
- TikTok keyword search is loose. Searching "Nike" can return ads from unrelated advertisers (Interactive Brokers, Shopify in our test). Always post-filter by matching the user's intended brand; warn the user if zero matches after filter.
- TikTok Ads Library is EU/EEA/UK only. The source needs an EU country code (DE / FR / IT / ES / NL / PL / SE etc.). For US/global coverage, switch to source — different fields (CTR, impression ranges, no targeting data).
dz_omar/google-ads-scraper
requires . Smaller values fail validation. Always set 10+ even for small intents.
apify/facebook-ads-scraper
takes URLs, not keywords. For : build https://www.facebook.com/<PageName>
from the brand name. For : build a Meta Ad Library URL with .
apify/google-search-scraper
paid-ads mode has a built-in retry (up to 3) when no paid results are found — sometimes a query genuinely has no paid results. Treat empty as a valid answer, not an error.
- LinkedIn Ad Library URL construction: company URL
https://www.linkedin.com/company/<slug>/
is allowed but slow and ignores filters. For use https://www.linkedin.com/ad-library/search?accountOwner=<slug>&countries=<XX>
. For use ?keyword=<term>&countries=<XX>
.
- X has no public ad library. Coverage is heuristic only. The route uses
apidojo/twitter-scraper-lite
to scrape a brand's own tweets (or keyword search results), then flags items with non-empty field or containing "Ads" as likely promoted. This will miss promoted-only tweets that never appear in the brand's own timeline.
- X session sensitivity. If the primary X Actor returns only sentinels, switch to the fallback before declaring zero results.
- Pricing. Most primaries are FREE in our pricing tier;
apify/facebook-ads-scraper
charges per ad ($0.001 - $0.0058); X primaries charge per tweet (~$0.0004 / 1k). Default counts (30 / 20 / 50) keep cost negligible. Warn before runs of 500+ ads.
Error Handling
- Auth error → run , or set env var
- → check Actor ID against the routing table
- Run status → open the console URL ( from run metadata) for logs
- Timeout / very long run → pass to , or reduce result count
- 0 results → switch to the Fallback Actor; if still 0, try a different country code
- TikTok library: no EU country supplied → default to and warn the user
dz_omar/google-ads-scraper
: validation error on → bump to 10+
- X scraper: only sentinels → switch to the fallback X Actor
- error → add
"proxy": {"useApifyProxy": true}
to the input