Facebook Ads Competitor Monitor & Tracker avatar

Facebook Ads Competitor Monitor & Tracker

Pricing

from $100.00 / 1,000 competitor checkeds

Go to Apify Store
Facebook Ads Competitor Monitor & Tracker

Facebook Ads Competitor Monitor & Tracker

Track competitor Facebook and Instagram ads. Detect new, stopped, reactivated, copy, creative, and landing-page changes with before/after evidence.

Pricing

from $100.00 / 1,000 competitor checkeds

Rating

0.0

(0)

Developer

Ula Ula

Ula Ula

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

12 days ago

Last modified

Share

Know when a competitor launches, stops, reactivates, or changes an ad - without manually comparing Meta Ad Library results every day.

This Actor turns public Facebook and Instagram ad observations into a persistent competitor monitor. It remembers the last complete observation and emits clean, auditable change events with before-and-after evidence.

Try the live demo in 60 seconds

  1. Keep the prefilled Nike example.
  2. Click Start.
  3. Open the Dataset tab to inspect current ads, copy, creatives, landing pages, and source links.

The demo uses all_current, so the first run returns useful data immediately. It is a one-time snapshot and does not modify a monitoring baseline.

{
"monitorId": "nike-demo-snapshot",
"competitors": [
{
"id": "nike-us",
"name": "Nike",
"ad_library_url": "https://www.facebook.com/ads/library/?active_status=all&ad_type=all&country=US&view_all_page_id=15087023444"
}
],
"country": "US",
"maxAdsPerCompetitor": 10,
"outputMode": "all_current",
"maxUpstreamChargeUsd": 0.05
}

What you get

  • NEW - a previously unseen ad ID
  • COPY_CHANGED - ad text or call to action changed
  • CREATIVE_CHANGED - creative URLs or creative fingerprint changed
  • LANDING_PAGE_CHANGED - the normalized destination changed
  • REACTIVATED - a previously stopped ad returned
  • STOPPED - an ad disappeared from repeated complete observations or was explicitly observed as inactive
  • Before-and-after records for audit and reporting
  • Stable event IDs for deduplication in Make, Zapier, n8n, or your own system
  • A run summary even on quiet days when no changes are found

Set up daily monitoring

1. Use an exact advertiser locator

Open the advertiser in Meta Ad Library and copy the filtered URL. Put that URL in ad_library_url. A numeric page_id or Facebook page_url also works.

Use search_term only as a fallback: keyword search can include similarly named or unrelated advertisers.

2. Create the baseline

Change outputMode to changes, choose a stable monitorId, and start the Actor. The first successful run creates a silent baseline. The run summary will say that the baseline was created; an empty Dataset is expected at this stage.

For monitoring, set maxAdsPerCompetitor above the advertiser's likely total. If a result exactly reaches the cap, the Actor treats it as partial and protects the existing baseline from false stopped-ad alerts.

{
"monitorId": "client-acme-daily",
"competitors": [
{
"id": "competitor-one-us",
"name": "Competitor One",
"ad_library_url": "PASTE_THE_EXACT_META_AD_LIBRARY_URL_HERE"
}
],
"country": "US",
"maxAdsPerCompetitor": 250,
"outputMode": "changes",
"stoppedAfterMissingRuns": 2,
"maxUpstreamChargeUsd": 1
}

3. Save and schedule

Click Save as a new task, then add a daily Schedule. Keep the same monitorId; that is the key connecting today's observation with future runs.

4. Deliver alerts

Attach a run-succeeded webhook or integration to the saved task. The default Dataset is your event feed. The OUTPUT key-value-store record is the compact run summary for health checks and dashboards.

Useful destinations include Slack, Microsoft Teams, Make, Zapier, n8n, Google Sheets, Airtable, a CRM, a warehouse, or your own endpoint.

Output modes

ModeBest forBaseline behaviorDataset on first run
all_currentDemo, research, one-time exportDoes not read or update itCurrent ads
changesRecurring monitoringCreates or updates it safelyEmpty baseline run
allMonitoring plus initial inventoryCreates or updates it safelyInitial ads as NEW

Example change event

{
"event_id": "evt_...",
"event_type": "COPY_CHANGED",
"monitor_id": "client-acme-daily",
"competitor_id": "competitor-one-us",
"competitor_name": "Competitor One",
"ad_id": "1234567890",
"changed_fields": ["copy"],
"before": {
"ad_id": "1234567890",
"copy": "Old promotional message",
"landing_url": "https://example.com/offer"
},
"after": {
"ad_id": "1234567890",
"copy": "New promotional message",
"landing_url": "https://example.com/offer"
},
"detected_at": "2026-07-15T12:00:00Z",
"source_url": "https://www.facebook.com/ads/library/?id=1234567890"
}

The event contains both normalized evidence and a source URL when the upstream provider exposes one. A STOPPED event is an observation signal, not proof that Meta formally ended an ad.

Reliability safeguards

Ad monitoring is only valuable when partial scrapes do not create false alerts. This Actor therefore:

  • Never updates state after a failed upstream run
  • Rejects capped or explicitly partial observations for baseline updates
  • Protects the baseline when observed inventory collapses below 50% of the previous complete snapshot
  • Requires at least two complete absences before emitting STOPPED
  • Normalizes whitespace and common tracking parameters before comparison
  • Emits REACTIVATED once when an absent ad returns
  • Produces deterministic event IDs for downstream deduplication
  • Honors the user's maximum run charge before starting paid competitor checks

State is isolated in a named key-value store derived from monitorId. Use a different ID for each client, market, or independent monitor.

Inputs

Every competitor needs a stable id and one locator:

  • ad_library_url - recommended exact Meta Ad Library URL with filters
  • page_id - exact numeric Facebook page ID
  • page_url - public Facebook page URL
  • search_term - broad keyword fallback

Other controls:

  • country - two-letter Meta Ad Library country code
  • maxAdsPerCompetitor - requested records per competitor, 1-500
  • outputMode - all_current, changes, or all
  • stoppedAfterMissingRuns - complete absences required before STOPPED
  • maxUpstreamChargeUsd - hard combined ceiling for child Actor calls
  • upstreamActorId - advanced replacement for the default data provider
  • upstreamInputOverrides - advanced provider-specific JSON

Pricing

You pay:

  • $0.01 per Actor start
  • $0.10 per successfully processed competitor
  • The selected upstream Meta Ad Library provider separately, within your maxUpstreamChargeUsd limit
  • No additional platform-usage charge for this Actor

Examples for this Actor's event fees:

WorkloadActor fee per runApprox. Actor fee per 30 days
1 competitor, daily$0.11$3.30
5 competitors, daily$0.51$15.30
10 competitors, daily$1.01$30.30

Upstream charges are extra and depend on returned records and the provider's current pricing. Start with one advertiser and a small snapshot, inspect Usage, then increase the monitoring limit. The hard upstream ceiling prevents an unexpected child-run bill, but setting it too low can stop a child run early.

Use from JavaScript

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor(
'dataforge_labs/facebook-ad-change-intelligence'
).call({
monitorId: 'client-acme-daily',
competitors: [{
id: 'competitor-one-us',
name: 'Competitor One',
page_id: '1234567890',
}],
country: 'US',
maxAdsPerCompetitor: 250,
outputMode: 'changes',
maxUpstreamChargeUsd: 1,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);

Use from Python

import os
from apify_client import ApifyClient
client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor(
"dataforge_labs/facebook-ad-change-intelligence"
).call(run_input={
"monitorId": "client-acme-daily",
"competitors": [{
"id": "competitor-one-us",
"name": "Competitor One",
"page_id": "1234567890",
}],
"country": "US",
"maxAdsPerCompetitor": 250,
"outputMode": "changes",
"maxUpstreamChargeUsd": 1,
})
events = client.dataset(run["defaultDatasetId"]).list_items().items
print(events)

Limitations

  • Meta controls public availability; results may not represent an advertiser's complete portfolio
  • Search terms can include unrelated advertisers
  • Creative URLs can be temporary or missing
  • Meta and upstream providers can change fields or pagination behavior
  • This Actor does not log in, bypass access controls, or use Meta's official API
  • A capped snapshot can still be useful for research, but capped observations are not allowed to mutate a monitoring baseline

FAQ

Why is my first monitoring Dataset empty?

changes needs two observations to detect a change. The first complete run creates the baseline and writes a clear explanation to the run summary. Use all_current for instant results or all to emit the baseline as NEW events.

Why was my baseline not updated?

The observation was likely failed, capped, explicitly partial, or suspiciously smaller than the previous snapshot. This is intentional protection against false STOPPED alerts. Increase maxAdsPerCompetitor or inspect the child run.

How often should I schedule it?

Daily is a practical default. Higher frequency costs more and does not guarantee that Meta exposes updates sooner.

Can I monitor multiple clients?

Yes. Use a separate monitorId for each client or independent monitor. You can include up to 100 competitors in one input, subject to your maximum run charge.

Does it download creative files?

No. It stores normalized fields, URLs, and fingerprints rather than creative binaries.

Start now

Run the prefilled snapshot to see the output. Then replace Nike with your exact competitor, switch to changes, save the input as a Task, and schedule it daily.