How to search the Meta Ad Library and follow an advertiser
Three endpoints, in this order: Meta Ad Library search to find ads by keyword, Meta advertiser ads to list what one Facebook page runs, and Meta Ad Library ad detail to re-check single ads later. The worked example is 7 calls and costs $0.028; a failed call is not charged.
Updated
The job: you are entering a product category and want to see how the brands already in it advertise on Facebook and Instagram. Which hooks do they use, which offers, which landing pages, and how long do their ads keep running? Search the library by keyword to see who advertises, open the most active advertiser's full list, and save a shortlist of ads to check again later. You end with three tables: ads for the keyword, one advertiser's ads, and the status of your shortlist a week on.
No Meta developer app, access token or Facebook login is involved. Each call is an HTTPS request with your key.
Step by step
Step 1: search ads by keyword
Call Meta Ad Library search with query. Four filters narrow it:
country: an ISO code such asUSorDE; all countries when left out.status:active(the default, ads running now),inactive(stopped ads) orall.media:all(the default),imageorvideo.sort:impressions(the default, most impressions first) ornewest.
One call returns up to 30 ads for $0.004. Each ad has id (the library ID), url, advertiser (the Facebook page's id, name and url), title, the copy in text, format (image, video, carousel, text or dynamic), images, videos, landing_url, cta, platforms (such as facebook and instagram), active and first_shown_at.
curl -G "https://api.okdata.online/v1/meta/ads/search" \
-H "Authorization: Bearer $OKDATA_API_KEY" \
--data-urlencode 'query=electrolyte powder' \
-d country=US -d status=active -d media=video -d sort=impressions
For the second page, send page.next_cursor back as cursor, unchanged. Then count the rows by advertiser.id to see who spends the most attention on the keyword.
Step 2: list one advertiser's ads
Pass the page ID from step 1 to Meta advertiser ads as id, or the brand's name as it appears on its Facebook page as name. Give one, not both; both or neither is invalid_params, which is not charged. The same country, status, media and sort filters apply, and each page holds up to 30 ads for $0.004.
curl -G "https://api.okdata.online/v1/meta/advertisers/ads/list" \
-H "Authorization: Bearer $OKDATA_API_KEY" \
-d id=107364829105372 -d country=US -d status=active -d sort=newest
sort=newest puts the ads the brand launched most recently on top, which is what you want for a weekly look. The ID is safer than the name: several pages can share a name, and the ID points at exactly one.
Step 3: re-check single ads
Meta Ad Library ad detail takes a library id or the ad's link as url and returns one ad for $0.004, with active, first_shown_at and last_shown_at. Save the IDs of the ads you shortlist; a week later, three detail calls tell you which of them still run and for how long they have run. An ad that has been running for months is usually one that works.
curl -G "https://api.okdata.online/v1/meta/ads/get" \
-H "Authorization: Bearer $OKDATA_API_KEY" \
-d id=1290374658821904 \
-d fields=id,active,first_shown_at,last_shown_at,landing_url
Step 4: in Python
import collections
import os
import requests
API = "https://api.okdata.online"
HEADERS = {"Authorization": f"Bearer {os.environ['OKDATA_API_KEY']}"}
def pages(path, n, **params):
cursor = None
for _ in range(n):
body = requests.get(API + path, headers=HEADERS, params={**params, **({"cursor": cursor} if cursor else {})}, timeout=60).json()
if body["code"] != "ok":
raise RuntimeError(f"{body['code']}: {body['message']} (charged: {body['charged']})")
yield from body["data"]["items"]
if not body["page"]["has_more"]:
return
cursor = body["page"]["next_cursor"]
ads = list(pages("/v1/meta/ads/search", 2, query="electrolyte powder", country="US", status="active", media="video"))
top_page = collections.Counter(a["advertiser"]["id"] for a in ads if a["advertiser"]["id"]).most_common(1)[0][0]
for a in pages("/v1/meta/advertisers/ads/list", 2, id=top_page, country="US", status="active", sort="newest"):
print(a["first_shown_at"], a["format"], a["landing_url"], (a["text"] or "")[:80])
Good to know
- What Meta's own API returns. Meta's Ads Archive reference says ads that did not reach any location in the EU only come back if they are about social issues, elections or politics. For political ad research, or EU ads with Meta's own breakdowns, its API is the right tool. The endpoints here read the public library pages, so ordinary commercial ads in the US or Asia come back too.
- Spend and reach. The library publishes no spend or reach for commercial ads outside the EU, so
reachis null there. For EU-delivered ads it carries Meta's EU reach figure. - Stopped ads.
status=inactivereturns only the stopped ads the public library still shows, and for commercial ads outside the EU that is often few. If you want a brand's history, save each week's rows yourself. - Dynamic ads. Catalog ads assembled per viewer have
formatset todynamicand often no copy, because the library shows a template instead of text. - Extras.
view=fulladdsext.variants(how many versions of the ad the library groups together),ext.page_likes,ext.categoriesand, for EU ads,ext.eu_payerandext.eu_beneficiary. - Save creatives you keep. Image and video links point at Meta's servers and can expire.
- Retries without paying twice. Send an
Idempotency-Keyheader with each call; the same request with the same key within 24 hours returns the stored answer and no new charge.
For what the official API costs and requires, see the Meta Ad Library API answer. To track one competitor on Meta, Google and TikTok together, see competitor ads on three libraries.
What the example costs
Two pages of active US video ads for electrolyte powder, two pages of one advertiser's active ads, then three shortlisted ads checked again a week later
| Endpoint | Calls | Price | Subtotal |
|---|---|---|---|
| Meta Ad Library search | 2 | $0.004 | $0.008 |
| Meta advertiser ads | 2 | $0.004 | $0.008 |
| Meta Ad Library ad detail | 3 | $0.004 | $0.012 |
| Total | 7 | $0.028 |
Prices in force when this page was built. A failed call costs nothing.
In your AI (MCP)
Connect the MCP server once, then say what you want. The agent finds these endpoints, reads their prices and calls them on your balance.
SaySearch the Meta Ad Library for active video ads about electrolyte powder shown in the US, most impressions first, two pages. Tell me which advertisers appear most, then list two pages of active ads from the top one with copy, landing page and start date.
What the agent sends to the call tool first
{
"endpoint": "meta.ads.search",
"params": {
"query": "portable espresso"
},
"idempotency_key": "<a new UUID>"
}