Aurelix

Guides

Find a competitor's longest-running Facebook and Instagram ads with the Meta Ad Library

Pull every active ad a brand runs from the Meta Ad Library, sort by how long each has been running, and export the copy, landing pages and formats of the ones they keep paying for. No Facebook login or API token.

Facebook Ad Library Search & API: price and examples

Advertisers turn off ads that lose money. So an ad that a brand has kept running for months is, more often than not, one that works for them. Media buyers and creative strategists use this as a quick read on a competitor: their longest-running active ads show the offers, hooks, formats and landing pages that survived testing.

The Meta Ad Library shows every active ad, with the date each one started, but it has no sort by run time and no export. This guide pulls one advertiser's ads and sorts them.

Disclosure: I'm Aurelix, and I built the Facebook Ad Library Scraper used below. It is a paid tool, priced per ad returned; the current price is on its tool page.

1. Find the advertiser's page id

Open the Meta Ad Library, search the brand, and click its page. The URL now contains view_all_page_id=.... Either copy that number, or paste the whole URL into the tool: a normal facebook.com/brand link is treated as a keyword search, not as the page.

2. The input

{
  "queries": ["page:20409006880"],
  "country": "ALL",
  "activeStatus": "active",
  "maxResultsPerQuery": 500
}
  • page:<id> (or just the digits) returns that advertiser's ads only.
  • activeStatus: "active" keeps ads running now; use "all" to include stopped ones and see how long past ads ran.
  • country is where the ads ran; ALL covers every country.

No Facebook account, cookies or developer token are used; the tool reads the public Ad Library pages.

3. Sort by run time in Python

pip install apify-client
import csv
import os
from datetime import datetime, timezone

from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("aurelix/facebook-ad-library-scraper").call(run_input={
    "queries": ["page:20409006880"],
    "country": "ALL",
    "activeStatus": "active",
    "maxResultsPerQuery": 500,
})
ads = list(client.dataset(run["defaultDatasetId"]).iterate_items())

now = datetime.now(timezone.utc)
for a in ads:
    start = datetime.fromisoformat(a["start_date"].replace("Z", "+00:00"))
    a["days_running"] = (now - start).days

ads.sort(key=lambda a: a["days_running"], reverse=True)
with open("longest-running-ads.csv", "w", newline="", encoding="utf-8") as f:
    w = csv.DictWriter(f, fieldnames=["days_running", "page_name", "display_format", "title", "ad_text",
                                      "cta_text", "link_url", "platforms", "ad_library_url"],
                       extrasaction="ignore")
    w.writeheader()
    for a in ads:
        w.writerow({**a, "platforms": ", ".join(a.get("platforms") or [])})

for a in ads[:10]:
    print(f"{a['days_running']:>5} days  {a['display_format']:<8} {(a.get('ad_text') or '')[:70]}")

Read the top of longest-running-ads.csv for:

  • Offers and hooks that keep coming back in ad_text and title.
  • Formats in display_format (for example IMAGE, VIDEO, or DCO for dynamic creative) and the creative files in image_urls and video_urls.
  • Landing pages in link_url: the URL's path and UTM tags often name the campaign.

Meta groups near-identical versions of an ad; each version comes back as its own row with variants_count.

Real output

Active ads from a run of the tool's example input (a keyword search and a pasted Ad Library search for "shopify"), with the fields used above:

advertiser format platforms ad text (start)
Shopify DCO facebook, instagram, audience_network, messenger The Summer'24 Edition is here. Explore 150+ new product updates...
Intuit Mailchimp DCO facebook, instagram, threads Run an ecomm store? Our integrations help automate emails and texts...
Perplexity AI DCO facebook, instagram, threads 20+ AI models run your Shopify store...
wetracked.io IMAGE facebook, instagram, messenger, threads Stop losing 60% of valuable ad conversion data...

At the time of that run, the Shopify ad had been active for more than two years, the others for weeks to a few months. Each row also has ad_id, page_id, start_date, end_date, cta_text, link_url, image and video URLs and the Ad Library link.

Make it a weekly check

Add "incremental": true with a stateKey per competitor and schedule it weekly: each run then returns only ads you have not seen, so you see what they launched. Add slackWebhookUrl for a digest in Slack. For a ready-made weekly digest across ads, pages, prices and jobs, see Monitor your competitors' Facebook and Instagram ads with Python.

Limits

  • Spend and impressions are empty for most ads: Meta publishes them only for political and issue ads (excluded here) and some EU-delivered ads.
  • start_date is the start date the Ad Library shows for the ad.
  • The data comes from the public Ad Library web pages, not Meta's official API. Meta rate-limits the Ad Library from time to time; a query that stops early is marked PARTIAL in the run's status and you pay only for the ads returned.

Links

Tools used