SocialAPIs
Sources
PricingBlogFAQContact
← Back to blog
facebook-ads-librarymeta-adscompetitor-researchpythonn8n

Facebook Ads Library API Without App Review: Search Competitors' Ads and Track New Ones

Meta's Ad Library API covers political and EU ads and needs identity confirmation. Here's how to search any advertiser's Facebook and Instagram ads by keyword or page with one API key, read the response, and get alerted when a competitor launches a new ad.

SocialAPIs Team·October 28, 2026

Yes, you can read the Facebook Ads Library (Meta Ad Library) through an API without Meta's app review: SocialAPIs' /facebook/ads/search returns the active ads for any keyword or any advertiser's page as JSON, with one API key and no Meta developer app. Meta's own Ad Library API is narrower than most people expect: it requires identity confirmation and a developer app, and it covers ads about social issues, elections or politics, plus ads shown in the EU. For ordinary commercial ads elsewhere, the official API has no answer. This guide shows the calls, the response, and a small script that tells you when a competitor launches a new ad.

Meta's Ad Library API vs. a data API

Meta Ad Library APISocialAPIs
SetupIdentity confirmation, a Meta developer app, an access tokenOne API key, sign up in a minute
Ads coveredSocial-issue, election and political ads; ads shown in the EUWhat the public Ad Library website shows: active and inactive ads, any category, any country it supports
Search by keywordYes, within its coverageYes
Search by advertiserYes, within its coverageYes (ad_page_id)
Spend and impressionsFor political / issue adsOnly where Meta publishes them (mostly political and EU ads); null otherwise
FormatGraph APIPlain JSON over HTTPS, MCP server for AI agents

If you only need political ads or EU transparency data, Meta's API is free and official, use it. If you want to see what a brand is running in the US, Brazil or anywhere else, read on.

The endpoints

EndpointWhat it's forRequired
/facebook/ads/searchAds by keyword or by advertiser, with country, active / inactive, date and sort filtersquery or ad_page_id
/facebook/ads/keywordsFind advertisers for a keyword (gives their page_id)query
/facebook/ads/page-detailsAn advertiser's Ads Library profilepage_id
/facebook/ads/archive-detailsOne ad's full detailsad_archive_id (and pass its page_id)
/facebook/ads/countriesSupported country codes—

Each call costs 1 credit. The free plan includes 200 calls a month (pricing).

Search ads by keyword

bash
1curl -G "https://api.socialapis.io/facebook/ads/search" \
2  -H "x-api-token: $SOCIALAPIS_API_KEY" \
3  --data-urlencode "query=running shoes" \
4  --data-urlencode "country=US" \
5  --data-urlencode "activeStatus=Active" \
6  --data-urlencode "sort_data=impressions"
ParameterValues
queryany keyword
ad_page_idan advertiser's ID, instead of query
countryISO code (US, GB, DE…) or ALL
activeStatusActive, Inactive or ALL
after_time, before_timeYYYY-MM-DD
sort_dataimpressions or recent
end_cursornext page, from page_info.end_cursor

Search one competitor's ads

You need the advertiser's ad_page_id. Two ways to get it:

  • From their page URL: /facebook/pages/details with link=https://www.facebook.com/<brand> returns the page's ad_page_id.
  • From a keyword: /facebook/ads/keywords with query=<brand name> lists matching advertisers and their page_id.

Then:

bash
1curl -G "https://api.socialapis.io/facebook/ads/search" \
2  -H "x-api-token: $SOCIALAPIS_API_KEY" \
3  --data-urlencode "ad_page_id=<the page id>" \
4  --data-urlencode "country=ALL" \
5  --data-urlencode "activeStatus=Active"

Reading the response

Two things trip people up the first time:

1. Ads come in groups. data.ads is a list of groups, and each group is a list of near-identical ads (the same creative run several times). Take the first ad of each group unless you need every copy:

json
1{
2  "data": {
3    "page_info": { "end_cursor": "AQHT…", "has_next_page": true },
4    "ads": [
5      [
6        {
7          "ad_archive_id": "1339489931715040",
8          "page_id": "71740214065",
9          "page_name": "KURU Footwear",
10          "is_active": true,
11          "start_date": 1783321200,
12          "end_date": 1793084400,
13          "publisher_platform": ["FACEBOOK", "INSTAGRAM", "AUDIENCE_NETWORK"],
14          "spend": null,
15          "snapshot": {
16            "display_format": "DCO",
17            "title": "{{product.name}}",
18            "body": { "text": "{{product.brand}}" },
19            "cta_text": "Shop now",
20            "link_url": "https://www.kurufootwear.com/pages/outbreak",
21            "cards": [
22              {
23                "title": "Get Comfort in the Chaos",
24                "body": "You saw the apocalypse. The zombies were scary. Now meet the shoes that make foot pain the real casualty…",
25                "link_url": "https://www.kurufootwear.com/pages/outbreak"
26              }
27            ]
28          }
29        }
30      ]
31    ]
32  }
33}

2. Catalog ads have placeholders. Dynamic product ads (display_format DPA or DCO) show template text like {{product.name}} in snapshot.title and snapshot.body.text. The real copy is in snapshot.cards: use the first card's title and body for those.

Dates are Unix seconds. spend, impressions and reach are null for most commercial ads, because Meta doesn't publish them outside political and EU ads; no tool can give you a competitor's real budget.

Track a competitor's new ads (Python)

This checks one or more advertisers once a day, remembers which ads it has seen, and prints (or sends) only the new ones.

python
1import json
2import os
3import re
4from datetime import datetime, timezone
5from pathlib import Path
6
7import requests
8
9API_KEY = os.environ["SOCIALAPIS_API_KEY"]
10ADVERTISERS = [p.strip() for p in os.environ["AD_PAGE_IDS"].split(",") if p.strip()]
11SEEN_FILE = Path("seen_ads.json")
12PLACEHOLDER = re.compile(r"\{\{.*?\}\}")
13
14
15def search_ads(ad_page_id):
16    response = requests.get(
17        "https://api.socialapis.io/facebook/ads/search",
18        headers={"x-api-token": API_KEY},
19        params={"ad_page_id": ad_page_id, "country": "ALL", "activeStatus": "Active"},
20        timeout=90,
21    )
22    response.raise_for_status()
23    groups = response.json().get("data", {}).get("ads", [])
24    return [group[0] for group in groups if group]
25
26
27def ad_copy(ad):
28    snap = ad.get("snapshot") or {}
29    card = (snap.get("cards") or [{}])[0]
30    title = snap.get("title") or ""
31    text = (snap.get("body") or {}).get("text") or ""
32    if not title or PLACEHOLDER.search(title):
33        title = card.get("title") or ""
34    if not text or PLACEHOLDER.search(text):
35        text = card.get("body") or ""
36    return title, " ".join(text.split())[:300]
37
38
39def main():
40    seen = set(json.loads(SEEN_FILE.read_text())) if SEEN_FILE.exists() else set()
41    first_run = not seen
42
43    for page_id in ADVERTISERS:
44        for ad in search_ads(page_id):
45            ad_id = ad["ad_archive_id"]
46            if ad_id in seen:
47                continue
48            seen.add(ad_id)
49            if first_run:
50                continue
51            title, text = ad_copy(ad)
52            started = datetime.fromtimestamp(ad["start_date"], timezone.utc).date()
53            print(f"New ad from {ad['page_name']} (since {started}): {title}\n{text}\n"
54                  f"https://www.facebook.com/ads/library/?id={ad_id}\n")
55
56    SEEN_FILE.write_text(json.dumps(sorted(seen)))
57
58
59if __name__ == "__main__":
60    main()

Run it once by hand (the first run records the ads already live), then daily with cron, and replace the print with a Slack webhook, an email or a Google Sheets row. One advertiser checked daily is about 30 calls a month.

The same without code (n8n)

In n8n: Schedule Trigger (daily) → HTTP Request (GET https://api.socialapis.io/facebook/ads/search, Header Auth x-api-token, query ad_page_id or query, country, activeStatus=Active) → Set a field ads = {{ $json.data.ads.map(group => group[0]) }} → Split Out ads → Remove Duplicates (Remove items processed in previous executions, value {{ $json.ad_archive_id }}) → Google Sheets append and/or Slack. Add an AI step (Gemini, OpenAI…) before Slack to summarize the hooks and calls to action across the new ads.

With an AI assistant

The SocialAPIs MCP server exposes all of this as the facebook_ads tool, so in Claude, ChatGPT or Cursor you can ask "what ads is KURU running right now, and what hooks do they use?" and the assistant calls the Ads Library for you.

Is this allowed?

The Ads Library exists so anyone can see who is advertising what; the data here is what the public Ad Library website shows, without logging in. Use it for research, monitoring and analysis; don't present a competitor's creative as your own, and keep an eye on the terms of the platforms you republish to.

Common questions

Can I see how much a competitor spends? No. Meta only publishes spend ranges for political / issue ads and some EU data; for other ads spend is null everywhere. Long-running ads and many variants of one creative are the usual signals of what's working.

Does it include Instagram ads? Yes. Ads that run on Instagram appear with INSTAGRAM in publisher_platform, along with Facebook, Messenger, Audience Network and Threads.

Inactive ads? Set activeStatus=Inactive or ALL, and use after_time / before_time for a date range.

How do I open the ad on Facebook? https://www.facebook.com/ads/library/?id=<ad_archive_id>.

Get an API key and try it on your own competitors: the free plan includes 200 calls a month (sign up).

Try the SocialAPIs platform

Build on Facebook + Instagram data today (TikTok, X, LinkedIn, and YouTube on the roadmap). 200 free calls / month, no card.

Create a free account

More from the blog

How to Get Facebook Marketplace Listings with Python (Search, Filter, Track Prices)

Meta has no public Marketplace API and BeautifulSoup scripts break. Search Facebook Marketplace by keyword, lo…

How to Monitor a Facebook Group's New Posts Automatically (After the Groups API Shutdown)

Meta removed the Groups API on April 22, 2024. Here's how to watch a public Facebook group for new posts and s…

SocialAPIs

Public Facebook & Instagram data API, built for developers and AI agents.

Now in MCP Registry

Product

  • Get Started
  • API Reference
  • Pricing
  • Documentation
  • Blog
  • Compare vs others
  • Integrations
  • Social Search
  • Social Tracker

Free Tools

  • Facebook Follower Count
  • Network Inspector
  • Visual Selector
  • JSON Selector
  • cURL Converter
  • Leads Generator
  • Hashtag Generator
  • JSON Formatter
  • URL Encoder

Libraries & SDKs

  • Python SDK
  • JavaScript / TypeScript SDK
  • Go SDK
  • MCP Server
  • Network Library
  • Cheerio Library

Resources

  • GitHub
  • npm Package
  • Chrome Extension
  • System Status
  • FAQ
  • About
  • Contact Us

Legal

  • Privacy Policy
  • Terms of Service

© 2026 SocialAPIs. All rights reserved.