Meta ad library API (/api/v1/adlibrary)
This supplements the API reference. Use GET /api/v1/adlibrary (or the legacy alias GET /api/adlibrary) with your API key to search Meta (Facebook / Instagram) ads programmatically.
Path note:
/api/v1/adlibraryis canonical;/api/adlibraryis a supported metered alias. Examples below use/api/v1/...for programmatic calls. Tracked brands over an API key (GET /api/v1/brands) and session brand-detail tabs are documented in Brands & Brand tracker.
MCP tool (Meta ads)
search_facebook_ads— same underlying flow asGET /api/v1/adlibrary(keyword, country, page, sort).find_winning_products— adlibrary-backed winning-products view (forces winning-score + product-page filters for product discovery).creative_inspiration_pack— adlibrary-backed shortlist with hooks, landing pages, and media mix.brief_competitor— quick competitor brief from adlibrary + store/tracker signals.scan_ad— ad-id/url scanner with hook + scaling verdict from available signals.
For MCP sort_by on search_facebook_ads and find_winning_products, use:
relevance, datefound, lastseen, adspend, longestrunning, reach, adsetamount, consistency, monthlyvisits, pageactiveads.
Aliases created, date, recent, last_ad_started, and first_ad_started are normalized automatically by MCP.
Setup: MCP. Parameters: MCP · tools reference. For list_tracked_brands, see Brands.
Shared auth and metering (/api/v1/adlibrary, /api/v1/brands)
Both /api/v1/adlibrary and /api/v1/brands share the same metering and auth rules. Brand detail routes under /api/brands/* are session-only — see Brands.
- API key:
X-API-Key,Authorization: Bearer, or?api_key=(see API reference). - Session: If the visitor is already logged in, the same routes can run without a key; the active user must still meet the API access check below.
- Plan: Account must include API access. Otherwise
403. - Credits / rate limit: Monthly API credits and 60 requests per minute per user. On exhaustion,
429with a JSON error and optionalcreditsobject.
When you use an API key without an existing browser session, the server still resolves the key to your account for that request so behavior matches the dashboard.
GET /api/v1/adlibrary — Meta / Facebook winning ads list
Alias: GET /api/adlibrary — same metering and parameters.
UI equivalent: The in-app Meta ads views call /api/fb-ads (or /api/fb-ads-winning) with the same query shape. For integration, copy a working request from your browser’s network tab and swap the path to /api/v1/adlibrary (or /api/adlibrary), keeping the same query keys and values.
Entry guard (required query keys)
The endpoint only returns its main JSON payload when all of the following keys are present in the query string (empty string is allowed):
searchkeyword, keyword, min, countries, from, to, sorting, mediafilter, page, max, scroll, scaling, niches, fromlastseen, tolastseen, activestatus
If any are missing, the response is not the usual list payload (avoid this in clients).
Additionally, the inner gate requires trial set in the query or a logged-in / API-key–resolved session. With a valid X-API-Key, you meet the session requirement automatically. Otherwise the response is:
{"error":"logged_out"}
All query parameters
Everything below is read from the query string (same shape as /api/fb-ads in the app).
isset gate (must be present; empty string is OK)
Same list as Entry guard above:
searchkeyword, keyword, min, max, countries, from, to, sorting, mediafilter, page, scroll, scaling, niches, fromlastseen, tolastseen, activestatus.
Strongly recommended on every request
The handler reads website, languages, pagetypefilter, sortdirection, apps, and themes without going through that isset list. Mirror the dashboard and send at least website=All, languages=All, pagetypefilter= (empty = all page types), sortdirection=desc, apps=All, themes=All so behavior matches the UI and optional keys are always defined.
Auth / session / trial
| Parameter | Role |
|---|---|
| (none) | With X-API-Key, the request runs as your account (same effect as being logged in). |
trial |
If set (e.g. trial=true), anonymous winning-finder rules apply; combined with filters on page ≠ 0 can return {"message":"need_account"}. |
Pagination, cursor, de-duplication
| Parameter | Notes |
|---|---|
page |
0-based page index. |
scroll |
Deep-pagination cursor from search; empty on first request, then copy the scroll value from the previous JSON response. |
nextscrapetime |
Unix seconds; used when sorting=datefound (first page often uses an anchor timestamp from the UI). |
search_token |
Optional [a-zA-Z0-9_-]+; ties ads_per_brand caps to a tab/search in session. |
Keyword search
| Parameter | Allowed / typical values |
|---|---|
keyword |
Free text; URL-encoded; lowercased server-side. |
searchkeyword |
'', All, landingurl, pagename, adtext, productname (must be in this set when keyword is non-empty). |
Active ads, scaling, media, page type, adscore, impressions
| Parameter | Values (match the in-app Meta ads filters) |
|---|---|
activestatus |
String true or false (active-only vs include inactive). |
scaling |
'' (none), nodownscaling, upscaling, downscaling. |
mediafilter |
'' / omit effect = all formats; else videos, images, carousel, dco (maps to display_format in ES). |
pagetypefilter |
'' = all; products, collections, funnels, nofunnels. |
adscorefilter |
'' = none; winning (Established), scaling (Has Potential), testing (Unestablished). Requires the matching in-app tier. |
lowimpressions |
'' = show all; hide = exclude very low impression rows (same behavior as the dashboard filter). |
Sorting
| Parameter | Values |
|---|---|
sortdirection |
asc or desc (empty defaults to desc). |
sorting |
'' (None → random order when not shuffling), trending, adsetamount, lastseen, datefound, adspend, longestrunning, reach, daysrunning. daysrunning may not apply a dedicated sort branch (behaves like None unless the product changes). |
Spend, reach (with window), traffic, days running, price, copy, video length, active ads on page
| Parameter | Notes |
|---|---|
minadspend, maxadspend |
Integers / numeric strings. maxadspend=999999 is treated as “no upper cap” for the adspend-in-use check. Meaningful adspend filtering may require a higher in-app tier. |
adspendtimeframe |
all, 7, 30, 90 — window for adspend filter (invalid values → all). |
minreach, maxreach |
Integers. May require a higher in-app tier. |
reachtimeframe |
Same allowed set as adspendtimeframe. |
mintraffic, maxtraffic |
May require a higher in-app tier. |
mindays, maxdays |
Days-running range filter; may require a higher in-app tier. |
minprice, maxprice |
|
mincopylength, maxcopylength |
|
minvideolength, maxvideolength |
|
minactiveads, maxactiveads |
“Active ads on page” style cap (trial flow may inject minactiveads server-side). |
Dates (strings, typically Y-m-d)
| Parameter | Meaning |
|---|---|
from, to |
Ad creation (started). |
fromlastseen, tolastseen |
Last seen (updated_at). |
product_from, product_to |
Product creation window. |
pagefrom, pageto |
Page creation window. |
Open-ended date ranges are normalized server-side (e.g. only from set → to may be set to match from).
Catalog: countries, stores, niches, languages, apps, themes, excludes
| Parameter | Format |
|---|---|
countries |
All or comma-separated ISO 3166-1 alpha-2 codes (e.g. US,GB). |
website |
All or comma-separated store codes (e.g. SH = Shopify — see dashboard website filter). |
niches |
All or comma-separated segments (preset codes, /Path/... paths from the niche catalog, or numeric ids — normalized server-side). If the list contains shopping, the server appends product. |
languages |
All or comma-separated language codes (as in dashboard #language). |
apps |
All or comma-separated numeric app ids (same values as the in-app apps filter). |
themes |
All or comma-separated theme strings (discover via GET /api/themes). |
excludeCountries, excludeWebsites, excludeLanguages, excludeNiches, excludeApps |
Same comma-separated patterns as the include side; niches normalized like niches. |
Deep links, shuffle, caps
| Parameter | Notes |
|---|---|
pageidfromurl |
Facebook page_id — narrows to one page’s ads. |
productid |
Shopify product id. |
shuffle |
Any non-empty truthy → shuffle mode (random sort; optional last-seen default window). |
ads_per_brand |
1, 2, 3, 5, 10 only — max ads per brand when shuffling / per-brand cap logic runs. |
Validation / errors
Invalid min or max (outside 0…90000000) can yield {"error":"value_not_in_range_1"} or _2, or a non-JSON false body in edge cases.
Discovering valid niches and themes (no API key on these routes)
Integrations should learn allowed values from discovery JSON, not from guessing:
| Endpoint | Purpose |
|---|---|
GET /api/niche-counts |
Returns { "niches": [ { "code": "…", "count": N }, … ], "total_with_niche": … } built from the same Meta ads index the product uses. Use the code strings (comma-separated in niches / excludeNiches) as the ground-truth set that actually appears in ads data. Optional: refresh=1 bypasses disk cache for one request. |
GET /api/themes |
Returns a JSON object with a themes list (top values + counts, cached ~1h). Optional query: q — if length ≥ 2, substring search for typeahead. Optional: limit (default 200, max 500). Use returned theme names/keys exactly as themes= comma-separated values on /api/adlibrary. |
Preset niche shortcuts (UI “chips”): The app also exposes human-facing two-letter codes (e.g. CG, BY, SP) in the niche filter. Those are valid segments in niches= the same way the browser sends them.
Path / numeric niche segments: Values are resolved using the server’s niche catalog (paths like /Apparel/... and numeric ids map to canonical names in search). If a segment is unknown, it may be passed through as-is — prefer /api/niche-counts over inventing strings.
Note: /api/niche-counts and /api/themes are discovery helpers and do not consume Meta ad library API credits the same way /api/adlibrary does. Your deployment may still require login or other policy in front of these routes.
Feature gates (upgrade JSON)
For accounts without the required product tier, certain filters or sort modes return 200 with a small JSON body (upgrade prompt is in-band, not always HTTP 403):
| Response | Condition (simplified) |
|---|---|
{"upgrade":"standard_adspend"} |
Ad spend filter used, or sorting=adspend |
{"upgrade":"standard_reach"} |
Reach filter or sorting=reach |
{"upgrade":"standard_traffic"} |
Traffic filter |
{"upgrade":"standard_daysrunning"} |
Days-running filter or sorting=daysrunning |
{"upgrade":"standard_adscore"} |
adscorefilter set |
Other notable responses
{"message":"need_account"}— Anonymoustrialrequests beyond allowed usage (trial flow guardrails).- Success payload: JSON object including
data(ads), optionalmessage(human-readable filter hints),totalwhen computed on first page, and free-tier credit fields when applicable.