LurkAPI
Docs · Search Ad Library ads

Search Ad Library ads

Find ads in Meta's Ad Library by keyword, with their copy, media, landing page and run dates.

1 credit per callFresh within 30 minutesMCP tool: facebook_search_ads
GEThttps://api.lurkapi.com/v1/facebook/adLibrary/search/ads

Example response

When to use this

Use it to see how a whole market advertises: the angles, offers and formats that keep running. For one advertiser's ads, use company ads (facebook_company_ads) instead. Stopped ads are included unless you pass status=active.

It searches every ad in Meta's Ad Library, the same as typing into the search box at facebook.com/ads/library. Filter by country, status, format, language and date range. The most-seen ads come first by default.

Pagination. The first page has up to 30 ads and searchResultsCount, the estimated total. For more, pass cursor back with the same filters. Later pages have up to 10 ads (Facebook's own page size), and searchResultsCount is null there. cursor is null on the last page.

Example request

Copy one and replace YOUR_API_KEY with your API key (get one free). The code versions read the key from the LURKAPI_API_KEY environment variable instead.

Language

Parameters

Add them to the URL after a ?, as in ?query=running+shoes&country=US&status=active. Join several with &.

ParameterDescription
query
stringrequired
Keywords to search ad text for, e.g. running shoes.
Example
running shoes
search_type
stringoptional
keyword_unordered matches all words in any order; keyword_exact_phrase matches the phrase as typed.
One of
keyword_unorderedkeyword_exact_phrase
Default
keyword_unordered
country
stringoptional
The country the ad ran in, as a 2-letter code (US, GB, DE…), or ALL for every country. One country per request.
Default
ALL
Example
US
status
stringoptional
Only ads that are running now (active), only stopped ads (inactive), or both (all).
One of
allactiveinactive
Default
all
Example
active
media_type
stringoptional
Ad format. meme is Facebook's name for an image with text. none means text-only ads.
One of
allimagevideomemeimage_and_memenone
Default
all
ad_type
stringoptional
Ad category. Political and issue ads also show spend and impression ranges.
One of
ALLPOLITICAL_AND_ISSUE_ADSEMPLOYMENT_ADSHOUSING_ADSCREDIT_ADS
Default
ALL
language
stringoptional
Language of the ad text as an ISO 639-1 code, e.g. en or es.
sort_by
stringoptional
total_impressions puts the most-seen ads first. relevancy_monthly_grouped puts the newest first.
One of
total_impressionsrelevancy_monthly_grouped
Default
total_impressions
start_date
stringoptional
Only ads shown on or after this date (YYYY-MM-DD). The ad itself may have launched earlier.
end_date
stringoptional
Only ads shown on or before this date (YYYY-MM-DD).
cursor
stringoptional
The cursor from the previous response, to get the next page. Keep every other param the same.
trim
booleanoptional
true leaves out image and video URLs, including those in snapshot.cards, for a much smaller response. All text stays.
One of
truefalse
Default
false

Example response

A real response, captured from the live API. Lists are trimmed to a couple of items, and long text such as media links is cut short with ….

Show the example response (7 KB)
{
  "success": true,
  "credits_remaining": 999,
  "credits_charged": 1,
  "searchResults": [
    {
      "ad_archive_id": "1279489107485101",
      "ad_id": null,
      "categories": [
        "UNKNOWN"
      ],
      "collation_count": 3,
      "collation_id": "955107020207956",
      "contains_digital_created_media": false,
      "contains_sensitive_content": false,
      "currency": "",
      "end_date": 1790751600,
      "end_date_string": null,
      "fev_info": null,
      "gated_type": "ELIGIBLE",
      "has_user_reported": false,
      "hide_data_status": "NONE",
      "impressions_with_index": {
        "impressions_text": null,
        "impressions_index": -1
      },
      "is_aaa_eligible": false,
      "is_active": true,
      "menu_items": [],
      "page_id": "103032366405927",
      "page_is_deleted": false,
      "page_name": "HOKA",
      "publisher_platform": [
        "FACEBOOK",
        "INSTAGRAM"
      ],
      "reach_estimate": null,
      "regional_regulation_data": {
        "finserv": {
          "is_deemed_finserv": false,
          "is_limited_delivery": false
        },
        "tw_anti_scam": {
          "is_limited_delivery": false
        }
      },
      "report_count": null,
      "snapshot": {
        "additional_info": null,
        "branded_content": null,
        "brazil_tax_id": null,
        "byline": null,
        "caption": "HOKA.com",
        "cards": [],
        "country_iso_code": null,
        "cta_text": "Learn more",
        "cta_type": "LEARN_MORE",
        "disclaimer_label": null,
        "display_format": "IMAGE",
        "ec_certificates": [],
        "event": null,
        "extra_images": [],
        "extra_links": [],
        "extra_texts": [],
        "extra_videos": [],
        "images": [
          {
            "image_crops": [],
            "original_image_url": "https://scontent-sin11-2.xx.fbcdn.net/v/t39.35426-6/660610392_9315190863…",
            "resized_image_url": "https://scontent-sin6-3.xx.fbcdn.net/v/t39.35426-6/658157694_15247761589…",
            "watermarked_resized_image_url": ""
          }
        ],
        "is_reshared": false,
        "link_description": "From first summits to 5Ks, HOKA designs running shoes and gear built to …",
        "link_url": "https://www.hoka.com/en/us/fly-human-fly/?utm_source=facebookinstagram&u…",
        "page_categories": [
          "Outdoor & Sporting Goods Company"
        ],
        "page_id": "103032366405927",
        "page_is_deleted": false,
        "page_like_count": 1218804,
        "page_name": "HOKA",
        "page_profile_picture_url": "https://scontent-sin2-1.xx.fbcdn.net/v/t39.35426-6/661719637_14799039102…",
        "page_profile_uri": "https://facebook.com/hoka",
        "root_reshared_post": null,
        "title": "FLY HUMAN FLY™",
        "videos": [],
        "body": {
          "text": "The legs give out. The heart doesn’t. Together We Fly Higher."
        }
      },
      "spend": null,
      "start_date": 1775026800,
      "start_date_string": null,
      "state_media_run_label": null,
      "targeted_or_reached_countries": [],
      "total_active_time": null,
      "url": null
    },
    {
      "ad_archive_id": "1672311423863159",
      "ad_id": null,
      "categories": [
        "UNKNOWN"
      ],
      "collation_count": null,
      "collation_id": null,
      "contains_digital_created_media": false,
      "contains_sensitive_content": false,
      "currency": "",
      "end_date": 1790751600,
      "end_date_string": null,
      "fev_info": null,
      "gated_type": "ELIGIBLE",
      "has_user_reported": false,
      "hide_data_status": "NONE",
      "impressions_with_index": {
        "impressions_text": null,
        "impressions_index": -1
      },
      "is_aaa_eligible": false,
      "is_active": true,
      "menu_items": [],
      "page_id": "9062006483",
      "page_is_deleted": false,
      "page_name": "REI",
      "publisher_platform": [
        "FACEBOOK",
        "INSTAGRAM"
      ],
      "reach_estimate": null,
      "regional_regulation_data": {
        "finserv": {
          "is_deemed_finserv": false,
          "is_limited_delivery": false
        },
        "tw_anti_scam": {
          "is_limited_delivery": false
        }
      },
      "report_count": null,
      "snapshot": {
        "additional_info": null,
        "branded_content": null,
        "brazil_tax_id": null,
        "byline": null,
        "caption": "rei.com",
        "cards": [],
        "country_iso_code": null,
        "cta_text": "Shop now",
        "cta_type": "SHOP_NOW",
        "disclaimer_label": null,
        "display_format": "VIDEO",
        "ec_certificates": [],
        "event": null,
        "extra_images": [],
        "extra_links": [],
        "extra_texts": [],
        "extra_videos": [],
        "images": [],
        "is_reshared": false,
        "link_description": "Trail-running gear",
        "link_url": "https://www.rei.com/c/trail-running-shoes?cm_mmc=sm_fbig_76501-_-hero_tr…",
        "page_categories": [
          "Outdoor & Sporting Goods Company"
        ],
        "page_id": "9062006483",
        "page_is_deleted": false,
        "page_like_count": 2113405,
        "page_name": "REI",
        "page_profile_picture_url": "https://scontent-sin6-3.xx.fbcdn.net/v/t39.35426-6/753881065_89714526678…",
        "page_profile_uri": "https://facebook.com/REI",
        "root_reshared_post": null,
        "title": "Get your gear for the trail",
        "videos": [
          {
            "video_hd_url": "https://video-sin2-2.xx.fbcdn.net/o1/v/t2/f2/m366/AQNiLrO3Mqup5to6yp2shI…",
            "video_preview_image_url": "https://scontent-sin11-1.xx.fbcdn.net/v/t39.35426-6/753145822_1584018949…",
            "video_sd_url": "https://video-sin11-1.xx.fbcdn.net/o1/v/t2/f2/m412/AQOUVO8Jd7h4piW3xQtRw…",
            "watermarked_video_hd_url": null,
            "watermarked_video_sd_url": null
          }
        ],
        "body": {
          "text": "Shoes required. Fun type may vary."
        }
      },
      "spend": null,
      "start_date": 1785481200,
      "start_date_string": null,
      "state_media_run_label": null,
      "targeted_or_reached_countries": [],
      "total_active_time": null,
      "url": null
    }
  ],
  "searchResultsCount": 37660,
  "cursor": "AQHTNrjEvTw_VUW75_xQkUb5_yjH682WwzNgh8oZPUIyUZdNkk1m_9q-4jP_law5Dv9p"
}

Response fields

Every field in a successful response, and what it means. A name with [] belongs to each item of a list: searchResults[].ad_archive_id is the ad_archive_id of every item in searchResults. null means there's no value.

Show all 104 fields
FieldTypeDescription
successtrueAlways true here; errors have success: false.
credits_remainingnumberYour balance after this call. On anonymous playground calls: free tries left today.
credits_chargednumberCredits this call cost; 0 on free endpoints. On anonymous playground calls: tries used (1).
searchResultsobject[]Matching ads.
searchResults[].ad_archive_idstringcan be nullThe Ad Library id. Pass it as id to the ad details endpoint.
searchResults[].ad_idstringcan be nullMeta's internal ad id. null in every response we've seen; use the Ad Library id.
searchResults[].page_idstringcan be nullThe advertiser's Facebook page id. Pass it as pageId to the company ads endpoint.
searchResults[].page_namestringcan be nullThe advertiser's page name.
searchResults[].is_activebooleanWhether the ad is running now.
searchResults[].start_datenumbercan be nullThe day the ad started running, as Unix seconds at midnight Pacific time.
searchResults[].end_datenumbercan be nullThe day the ad stopped, or its latest delivery day if still running, as Unix seconds at midnight Pacific time.
searchResults[].start_date_stringstringcan be nullThe same date as ISO 8601 text when Facebook includes it; null in every response we've seen, so use the Unix seconds field.
searchResults[].end_date_stringstringcan be nullThe same date as ISO 8601 text when Facebook includes it; null in every response we've seen, so use the Unix seconds field.
searchResults[].total_active_timenumbercan be nullSeconds the ad has run, when Facebook reports it; usually null.
searchResults[].publisher_platformstring[]Where it ran, uppercase: FACEBOOK, INSTAGRAM, AUDIENCE_NETWORK, MESSENGER, THREADS.
searchResults[].collation_countnumbercan be nullHow many near-identical versions are grouped under this ad. A high count means the advertiser is scaling it; null for a single version.
searchResults[].collation_idstringcan be nullId shared by the grouped versions of this ad; null for a single version.
searchResults[].snapshotobjectThe creative: copy, media, landing page and advertiser details.
searchResults[].snapshot.page_idstringcan be nullThe advertiser's page id (same as the ad's).
searchResults[].snapshot.page_namestringcan be nullThe advertiser's page name as shown on the ad.
searchResults[].snapshot.page_profile_uristringcan be nullThe advertiser's Facebook page URL.
searchResults[].snapshot.page_profile_picture_urlstringcan be nullThe advertiser's profile picture, 60×60 px.
searchResults[].snapshot.page_like_countnumbercan be nullFollowers of the advertiser's page.
searchResults[].snapshot.page_categoriesstring[]Page categories, e.g. ["Outdoor & Sporting Goods Company"].
searchResults[].snapshot.page_is_deletedbooleanWhether the advertiser's page has been deleted.
searchResults[].snapshot.titlestringcan be nullHeadline under the creative; null when there is none.
searchResults[].snapshot.captionstringcan be nullDisplay link under the creative, usually the domain (e.g. HOKA.com).
searchResults[].snapshot.link_urlstringcan be nullLanding page the ad sends people to, UTM tags included.
searchResults[].snapshot.link_descriptionstringcan be nullText under the headline; null when there is none.
searchResults[].snapshot.cta_textstringcan be nullButton label, e.g. "Shop now".
searchResults[].snapshot.cta_typestringcan be nullButton type, e.g. SHOP_NOW, LEARN_MORE, NO_BUTTON.
searchResults[].snapshot.display_formatstringcan be nullCreative format, e.g. IMAGE, VIDEO, DCO (dynamic creative), DPA (dynamic product ad) or MULTI_IMAGES. For DCO and DPA ads the creative is in cards.
searchResults[].snapshot.imagesobject[]Image creatives. Empty for video and card-based ads, and with trim=true.
searchResults[].snapshot.images[].original_image_urlstringcan be nullFull-size image URL. Facebook's links expire after a few days, so download what you need.
searchResults[].snapshot.images[].resized_image_urlstringcan be null600 px image URL, good for thumbnails.
searchResults[].snapshot.images[].watermarked_resized_image_urlstringWatermarked copy; empty or null in every response we've seen.
searchResults[].snapshot.images[].image_cropsany[]Facebook internal field; an empty array in every response we've seen. Crop hints for the image.
searchResults[].snapshot.videosobject[]Video creatives. Empty for image and card-based ads, and with trim=true.
searchResults[].snapshot.videos[].video_hd_urlstringcan be nullHD video file URL; null if Facebook has no HD version.
searchResults[].snapshot.videos[].video_sd_urlstringcan be nullSD video file URL.
searchResults[].snapshot.videos[].video_preview_image_urlstringcan be nullPoster frame (still image) URL.
searchResults[].snapshot.videos[].watermarked_video_hd_urlstringcan be nullWatermarked copy; empty or null in every response we've seen.
searchResults[].snapshot.videos[].watermarked_video_sd_urlstringcan be nullWatermarked copy; empty or null in every response we've seen.
searchResults[].snapshot.cardsobject[]Carousel, dynamic-creative and product versions, each with its own copy and media. With trim=true the text stays and media URLs are null.
searchResults[].snapshot.cards[].titlestringcan be nullThis version's headline.
searchResults[].snapshot.cards[].bodystringcan be nullThis version's primary text.
searchResults[].snapshot.cards[].captionstringcan be nullThis version's display link, usually the domain.
searchResults[].snapshot.cards[].cta_textstringcan be nullButton label, e.g. "Shop now".
searchResults[].snapshot.cards[].cta_typestringcan be nullButton type, e.g. SHOP_NOW, LEARN_MORE.
searchResults[].snapshot.cards[].link_urlstringcan be nullThis version's landing page URL.
searchResults[].snapshot.cards[].link_descriptionstringcan be nullText under the headline; null when there is none.
searchResults[].snapshot.cards[].original_image_urlstringcan be nullFull-size image URL; null for video versions.
searchResults[].snapshot.cards[].resized_image_urlstringcan be null600 px image URL; null for video versions.
searchResults[].snapshot.cards[].watermarked_resized_image_urlstringWatermarked copy; empty or null in every response we've seen.
searchResults[].snapshot.cards[].image_cropsany[]Facebook internal field; an empty array in every response we've seen. Crop hints for the image.
searchResults[].snapshot.cards[].video_hd_urlstringcan be nullHD video file URL; null for image versions.
searchResults[].snapshot.cards[].video_sd_urlstringcan be nullSD video file URL; null for image versions.
searchResults[].snapshot.cards[].video_preview_image_urlstringcan be nullPoster frame URL; null for image versions.
searchResults[].snapshot.cards[].watermarked_video_hd_urlstringcan be nullWatermarked copy; empty or null in every response we've seen.
searchResults[].snapshot.cards[].watermarked_video_sd_urlstringcan be nullWatermarked copy; empty or null in every response we've seen.
searchResults[].snapshot.bylinestringcan be nullWho paid for the ad (the "Paid for by" name) on political and issue ads; null on most others.
searchResults[].snapshot.disclaimer_labelstringcan be nullThe disclaimer kind on political and issue ads, e.g. PAID_FOR_BY (the name is in byline); null otherwise.
searchResults[].snapshot.country_iso_codestringcan be nullFacebook internal field; null in every response we've seen. A country code on some ads.
searchResults[].snapshot.is_resharedbooleanWhether the ad reshares an existing post (see root_reshared_post).
searchResults[].snapshot.root_reshared_postanyThe original post when is_reshared is true; null otherwise.
searchResults[].snapshot.branded_contentanyPaid partnership details when the ad is branded content; null otherwise.
searchResults[].snapshot.additional_infoanyFacebook internal field; null in every response we've seen.
searchResults[].snapshot.brazil_tax_idanyAdvertiser tax id shown on ads under Brazil's rules; null otherwise.
searchResults[].snapshot.ec_certificatesany[]Facebook internal field; an empty array in every response we've seen.
searchResults[].snapshot.eventanyEvent details on event ads; null otherwise.
searchResults[].snapshot.extra_imagesany[]More images from dynamic creative; usually empty.
searchResults[].snapshot.extra_linksany[]More landing page URLs from dynamic creative; usually empty.
searchResults[].snapshot.extra_textsany[]More text variations from dynamic creative; usually empty.
searchResults[].snapshot.extra_videosany[]More videos from dynamic creative; usually empty.
searchResults[].snapshot.bodyobjectcan be nullPrimary text as { text }; null when the copy is only in cards.
searchResults[].snapshot.body.textstringcan be nullThe ad's primary text.
searchResults[].categoriesstring[]Special ad categories Facebook assigned. ["UNKNOWN"] for ordinary ads.
searchResults[].targeted_or_reached_countriesstring[]Countries the ad targeted or reached, when Facebook discloses them; empty in most results.
searchResults[].currencystringCurrency code of spend (e.g. USD) when spend is reported; an empty string otherwise.
searchResults[].spendanySpend range as Facebook shows it, e.g. "$600K - $700K" or ">$1M" (see currency). Political and issue ads only; null otherwise.
searchResults[].impressions_with_indexobjectImpressions range, reported for political and issue ads only.
searchResults[].impressions_with_index.impressions_textstringcan be nullThe range as Facebook shows it; null when not reported.
searchResults[].impressions_with_index.impressions_indexnumberFacebook's bucket number for the range; -1 when not reported. Ad details usually omit it.
searchResults[].reach_estimateanyEstimated reach as Facebook shows it, e.g. ">1M", on political and issue ads and some EU-delivered ads; null otherwise.
searchResults[].urlstringcan be nullAd Library link for this ad. null in every response we've seen; build it as https://www.facebook.com/ads/library/?id=<Ad Library id>.
searchResults[].contains_digital_created_mediabooleanWhether Facebook labels the creative as AI-generated or digitally altered.
searchResults[].contains_sensitive_contentbooleanFacebook internal field; false in every response we've seen. Facebook's sensitive-content flag.
searchResults[].gated_typestringFacebook internal field; ELIGIBLE (lowercase in ad details) in every response we've seen.
searchResults[].has_user_reportedbooleanWhether the viewer reported the ad. Always false, since we read the Ad Library logged out.
searchResults[].hide_data_statusstringFacebook internal field; NONE in every response we've seen.
searchResults[].is_aaa_eligiblebooleanFacebook flag, true on a few ads; meaning undocumented. Ad details' aaa_info holds the related data when present.
searchResults[].menu_itemsany[]Facebook internal field; an empty array in every response we've seen.
searchResults[].page_is_deletedbooleanWhether the advertiser's page has been deleted.
searchResults[].regional_regulation_dataobjectDelivery limits under regional ad rules.
searchResults[].regional_regulation_data.finservobjectFinancial-services ad rules.
searchResults[].regional_regulation_data.finserv.is_deemed_finservbooleanFacebook treats the ad as a financial-services ad.
searchResults[].regional_regulation_data.finserv.is_limited_deliverybooleanDelivery is limited under financial-services rules.
searchResults[].regional_regulation_data.tw_anti_scamobjectTaiwan's anti-scam ad rules.
searchResults[].regional_regulation_data.tw_anti_scam.is_limited_deliverybooleanDelivery is limited under Taiwan's anti-scam rules.
searchResults[].report_countnumbercan be nullTimes the ad was reported, when disclosed; null in every response we've seen.
searchResults[].fev_infoanyFacebook internal field; null in every response we've seen.
searchResults[].state_media_run_labelanyLabel for ads run by state-controlled media; null otherwise.
searchResultsCountnumbercan be nullTotal matching ads, as Facebook estimates it. Only on the first page; null on cursor pages.
cursorstringcan be nullPass as cursor for the next page. null on the last page.

Pagination

Results come in pages. For the next page, repeat the call with the same parameters and add the response's cursor as cursor. On the last page, cursor is null. Each page costs 1 credit.

The first five pages, in JavaScript
let cursor = null;for (let page = 1; page <= 5; page++) {const params = new URLSearchParams({ query: "running shoes", country: "US", status: "active" });if (cursor) params.set("cursor", cursor);const res = await fetch(`https://api.lurkapi.com/v1/facebook/adLibrary/search/ads?${params}`, {headers: { "x-api-key": process.env.LURKAPI_API_KEY },});const data = await res.json();if (!data.success) throw new Error(`${data.code}: ${data.error}`);console.log(data.searchResults);cursor = data.cursor;if (!cursor) break; // last page}

Cost and freshness

Each call costs 1 credit. Calls turned away before they reach the platform (invalid parameters, a key or balance problem, a rate limit) are free, and so are 5xx errors. A not_found is charged, since the platform was checked. How charging works.

Answers are cached for up to 30 minutes. A repeat call within that time can return the same data, and costs the same.

Errors

A failed call returns success: false, an error message, a code your program can check, and a docs link. These are the errors this endpoint can return. All error codes.

StatusCodeMeaning
401missing_api_keyNo API key was sent. Put it in the x-api-key header.
401invalid_api_keyThis key doesn't exist or was revoked.
402insufficient_creditsNot enough credits for this call. Buy a pack, or wait for tomorrow's free top-up.
402over_max_creditsThis call would cost more than the max_credits you set. The error says what it needs; raise max_credits to allow it. Not charged.
403account_suspendedThis account is suspended. Email support@lurkapi.com. Not charged.
405method_not_allowedEndpoints only take GET, with parameters in the URL.
429rate_limitedToo many calls in a short time. Wait as many seconds as the retry-after header says, then retry.
500internal_errorSomething broke on our side. Retry. Not charged.
502upstream_errorThe platform didn't send a usable answer. Retry. Not charged.
503upstream_busyAll our connections to the platform are busy. Retry in a few seconds. Not charged.
504upstream_timeoutThe platform took over 30 seconds to answer (90 for web pages). Retry. Not charged.

Use it in Claude

Connect LurkAPI to Claude and ask in plain English. This endpoint is the facebook_search_ads tool. For example:

Ask Claude
Use LurkAPI's facebook_search_ads tool with query "running shoes", country "US", status "active", then summarize what you find.

Claude calls facebook_search_ads with arguments like these, and each call costs 1 credit:

Tool arguments
{"query": "running shoes","country": "US","status": "active"}

To save tokens, tool results leave out empty fields, keep one URL per image or video, and give transcripts as plain text unless you pass timestamps=true. A result over 45,000 characters comes in parts. Its _note gives the offset for the next part, and later parts are free for 15 minutes. Pass trim=false for the full response.

From the blog

All posts