Meta Ad Library
Meta Ad Library Advertiser API
Pull every Facebook and Instagram ad one advertiser is running, by numeric Page id: ad copy, headline, CTA, landing-page URL, image and video creative, publisher platforms and run dates. 30 ads on page 1, then 10 per cursor page, walking has_next_page until you have the competitor's whole catalogue. Public logged-out data, no Meta access token. Costs 1 credit per page.
Authorizations
AuthorizationstringheaderrequiredBearer authentication header of the form Bearer <token>, where <token> is your Scavio API key (e.g. Bearer sk_live_your_key).
Body
application/jsonpage_idstringrequiredThe advertiser's numeric Facebook Page id - 3 to 25 DIGITS, sent as a string. Not a vanity handle: "nike" is a 400, 15087023444 is the Page. The cheapest way to get one is /api/v1/meta-ads/search, whose every row carries page_id for the advertiser that ran it.
Example: 15087023444
countrystringdefault:USWhich country's ad library to read, as an ISO 3166-1 alpha-2 code. EXACTLY 2 CHARACTERS - "USA" is a 400. A global advertiser runs different creative per market, so this genuinely changes the catalogue you get back.
Example: GB
active_statusenum<string>default:allWhether the ad is still running.
all— Running and finished ads (default).active— Only what this advertiser is spending on right now.inactive— Only ads they have stopped - what they tested and dropped.
ad_typeenum<string>default:allWhich library to read. The only way to get spend, reach and impressions: Meta publishes them for political and issue ads and for nothing else.
all— The advertiser's full library (default). spend, reach_estimate, impressions and disclosure are null on commercial ads - expected, not a bug.political_and_issue_ads— Political and issue ads only, with spend, reach_estimate, impressions, the paid-for-by byline and the disclosure populated.
media_typeenum<string>Restrict to one creative format. No default - unset means no media filter at all.
all— Explicitly no media filter.image— Image creatives.video— Video creatives.meme— Meme-format creatives.image_and_meme— Images and memes together.none— Ads with no media attached.
cursorstringThe next_cursor from your previous response. Page 1 returns 30 ads; every cursor page after it returns 10. THE OTHER FILTERS ARE IGNORED WHEN A CURSOR IS PRESENT - the cursor already carries the page_id, country and every filter from the call that produced it, so paging is stateless. To change a filter, start again without a cursor.
Example: AQHSXCxoiABMQQX34MySjtzEfFQcNW9dCdDnzlvlQqdFgASaz3WkyA1re2xn7xspF1EK
Request
from scavio import ScavioClient
client = ScavioClient(api_key="sk_live_your_key")
# Step 1: a keyword search hands you the advertiser's numeric Page id
hits = client.meta_ads.search(query="nike", country="US")
page_id = hits["data"]["ads"][0]["page_id"]
# Step 2: walk that advertiser's whole catalogue
page = client.meta_ads.advertiser(page_id, country="US", active_status="active")
ads = list(page["data"]["ads"])
while page["data"]["has_next_page"]:
page = client.meta_ads.advertiser(page_id, cursor=page["data"]["next_cursor"])
ads.extend(page["data"]["ads"])
print(page_id, len(ads), "ads")Response