Bibliothèque publicitaire Meta
GET /api/v1/adlibrary searches Meta (Facebook / Instagram) ads with your API key. Alias: /api/adlibrary. Same auth and metering as the rest of /api/v1. MCP: search_facebook_ads.
/api/v1/adlibrarySearch Meta ads. Entry-guard keys are defaulted server-side on this route.
Clé X-API on every request.Query parameters
mot-cléstringsearchkeywordstringAll | landingurl | pagename | adtext | productname. Auto All when keyword is set.
countriesstringMeta ad-targeting markets only (US, GB, …). Unsupported codes (e.g. MX) return HTTP 400 — use store_based_in for store HQ.
store_based_instringAlias storebasedin. Store HQ / Based In (shop_origin_country). Use for MX, CO, CL, AR, PE.
sortingstringe.g. reach, lastseen, datefound, mostrecent, adspend, trending, …
sortdirectionstringpageinteger0-based page index.
scrollstringOpaque cursor; empty on first request.
limiteintegerPage size first request only; max 50. Aliases: page_size, pageSize, size.
mediafilterstringactivestatusstringfromstringAd created from (Y-m-d).
tostringAd created to (Y-m-d).
fromlastseenstringtolastseenstringnichesstringminstringMin duplicates.
maxstringMax duplicates.
curl -sS \
-H "X-API-Key: $WH_API_KEY" \
"{origin}/api/v1/adlibrary?keyword=skincare&searchkeyword=string&countries=US&store_based_in=MX&sorting=relevance&sortdirection=desc&page=0&scroll=string&limit=20&mediafilter=string&activestatus=string&from=string&to=string&fromlastseen=string&tolastseen=string&niches=string&min=string&max=string"const res = await fetch(`${ORIGIN}/api/v1/adlibrary?keyword=skincare&searchkeyword=string&countries=US&store_based_in=MX&sorting=relevance&sortdirection=desc&page=0&scroll=string&limit=20&mediafilter=string&activestatus=string&from=string&to=string&fromlastseen=string&tolastseen=string&niches=string&min=string&max=string`, {
method: 'GET',
headers: {
'X-API-Key': process.env.WH_API_KEY,
},
});
const data = await res.json();import os, requests
res = requests.get(
f"{ORIGIN}/api/v1/adlibrary?keyword=skincare&searchkeyword=string&countries=US&store_based_in=MX&sorting=relevance&sortdirection=desc&page=0&scroll=string&limit=20&mediafilter=string&activestatus=string&from=string&to=string&fromlastseen=string&tolastseen=string&niches=string&min=string&max=string",
headers={"X-API-Key": os.environ["WH_API_KEY"]},
)
data = res.json(){
"data": [
{
"productid": "1284756102394857",
"page_id": "108897621643793",
"pageName": "Glow Beauty Co.",
"countries": [
"US",
"CA"
],
"started": "2024-05-12",
"lastSeen": "2026-03-14",
"caption": "This serum changed my skin in 14 days…",
"copy": "This serum changed my skin in 14 days…",
"urlStore": "https://glowbeautyco.com/products/vitamin-c-serum",
"daysrunning": 128,
"countActive": 12,
"total_active_ads_on_page": 47,
"ad_rank": 3,
"adscore": "Winning",
"total_adspend": 8420,
"saved": false,
"hidden": false
}
],
"total": 240,
"total_relation": "eq",
"scroll": "sa:opaque-cursor-token",
"limit": 20,
"adspend_cpm": 11,
"nextscrapetime": 1781686740,
"message": null
}{
"success": false,
"error": "Invalid or expired scroll token"
}{
"success": false,
"error": "Unauthorized"
}{
"success": false,
"error": "WinningHunter Basic plan or higher required"
}{
"success": false,
"error": "Rate limit exceeded. Max 60 requests per minute."
}curl -sS -G -H "X-API-Key: $WH_API_KEY" "{origin}/api/v1/adlibrary" \
--data-urlencode "keyword=skincare" \
--data-urlencode "countries=US" \
--data-urlencode "sorting=reach" \
--data-urlencode "page=0"
On /api/v1/adlibrary, missing entry-guard keys are filled from Dashboard::fbAdsPublicBaseQuery() — you do not need to send every dashboard gate key. When mot-clé is set and searchkeyword is empty, searchkeyword=All is applied.
Niche code discovery (no API-key metering on this path): GET /api/niche-counts → { "niches":[{ "code":"CG","count":… }, …], "total_with_niche":… }. Optional ?refresh=1.
Pagination: page=0, empty scroll first; pass returned scroll with the same filters. Stop when scroll is null. Prefer stable sorts for deep paging.
Snake_case aliases
Public aliases normalize to internal keys (internal name wins if both are sent):
| Alias | Internal |
|---|---|
pays / countries, exclude_countries |
countries, excludeCountries |
niche / niches |
niches |
technology / technologies |
website |
language / languages, exclude_languages |
languages, excludeLanguages |
theme / themes, apps, exclude_apps |
themes, apps, excludeApps |
media_type, page_type, ad_score, low_impressions, rank_growth_filter |
mediafilter, pagetypefilter, adscorefilter, lowimpressions, rankgrowthfilter |
ad_created_from / ad_created_to |
from / to |
last_seen_from / last_seen_to |
fromlastseen / tolastseen |
product_created_from / product_created_to |
product_from / product_to |
page_created_from / page_created_to |
pagefrom / pageto |
min_duplicates / max_duplicates |
min / max |
dépense_publicitaire_minimale / dépense_max_en_publicité, ad_spend_timeframe |
minadspend / maxadspend, adspendtimeframe |
min_reach / max_reach, reach_timeframe |
minreach / maxreach, reachtimeframe |
min_monthly_visits / max_monthly_visits |
mintraffic / maxtraffic |
min_days_running / max_days_running |
mindays / maxdays |
min_active_ads / max_active_ads |
minactiveads / maxactiveads |
min_active_ads_growth / max_active_ads_growth, active_ads_growth_period |
minactiveadsgrowth / maxactiveadsgrowth, activeadsgrowthperiod |
min_reach_growth / max_reach_growth, reach_growth_period |
minreachgrowth / maxreachgrowth, reachgrowthperiod |
min_ad_rank / max_ad_rank |
minadrank / maxadrank |
sort_by / sort_order |
sorting / sortdirection |
active_status |
activestatus |
More filters match the in-app Meta ads dashboard (/api/fb-ads query shape). Copy a working Network tab request and swap the path to /api/v1/adlibrary when unsure.
Response notes:
- 200 bodies are pas wrapped in
{ "success": true }. - Each
data[]item is a full dashboard card fromUtils::process_ads(same shape as Magic AI / brand ads). Key fields includeproductid,pageName(notpage_name),daysrunning(notdays_running),urlStore,adscore,saved,hidden. There is no top-levelid/ad_url/platform/headlineon these cards — useproductideturlStore. Many more enrichment fields may appear (total_adspend, reach histories, Shopify fields, …). - Active-ads timeline (high signal):
total_active_ads_on_pageis the current count. Sibling fieldstotal_active_ads_on_page_growth_1w,_14d,_1m,_3msont absolute historical counts (how many active ads the page had at those lookbacks), not percentages — use them as a 5-point scaling curve. - Traffic history: when store enrichment is present,
store_traffic.monthly_visits_historical(or top-levelmonthly_visits_historicalon some store rows) is ~6 months of monthly visits — prefer it over a singlemonthly_visitssnapshot. - Top-level may also include
nextscrapetimeetmessage(oftennull). - Scroll errors:
"Invalid or expired scroll token"ou"Scroll token does not match current query filters". - Credit exhaustion also returns 429 with the credits + purchase envelope (same as other v1 routes).
- Plan gates may return HTTP 200 avec
{ "upgrade": "standard_reach", "message": "…" }instead of ads. - Unsupported Meta ad-targeting countries (e.g. MX, CO, CL) return HTTP 400
{ "success": false, "error": "invalid_argument", "allowed_values": [...], "hint": "…" }— not an empty 200. For stores based in those markets, passstore_based_in/storebasedin(filtersshop_origin_country). Spain (ES) and Brazil (BR) remain valid targeting markets. - Detected tech stack on cards:
technologies(codes such asSH,KV,TA) when enrichment is present.