Search Facebook's public Ads Library and return structured advertisement records for a Page, app, brand, or keyword. Extract advertiser identity, archive IDs, copy, rendered images and videos, raw date labels, platforms, disclosures, destination and landing-page URLs, visible calls to action, ordering, and collection-sweep status. Optionally capture metadata or a recording artifact for visible ad videos, without visiting external landing pages. This is read-only: never authenticate, contact advertisers, click CTAs, visit external destinations, or mutate Facebook.
Use Cases
- The caller asks whether a brand, Page, app, or phrase is running active or historical ads.
- The caller wants to compare ads or landing-page destinations across
{brand-list}. - The caller wants ad copy, creative media, dates, platforms, payer disclosures, archive IDs, CTA labels, or destination URLs.
- The caller requests all available ads or a depth target such as
{target-cards}cards. - A known ad archive ID must be inspected directly.
- The caller explicitly requests video inspection or a screen-recording artifact of a rendered ad video.
Automation Flow
Construct an Ads Library URL directly; do not visit the Facebook homepage, type into the search box, or click filters.
Page-scoped search:
https://www.facebook.com/ads/library/?active_status={active-status}&ad_type=all&country={country-code}&view_all_page_id={page-id}&media_type=all&search_type=pageKeyword/name search:
https://www.facebook.com/ads/library/?active_status={active-status}&ad_type=all&country={country-code}&q={urlencoded-query}&search_type=keyword_unordered&media_type=allKnown archive ID:
https://www.facebook.com/ads/library/?id={ad-archive-id}If only a brand or Page name is supplied, use the keyword URL, inspect rendered advertiser links containing
view_all_page_id, and select the exact advertiser by visible name, branding, domain, and ad context. Read the numeric Page ID from the observedhref, then navigate to the page-scoped URL. Never guess a Page ID or archive ID. For multiple brands, repeat the direct URL flow in the same browser session and retain the source brand on every record.Optional URL modifiers are
active_status=active|inactive|all,country=ALL|{country-code},media_type=all|image|video|carousel,is_targeted_country=false,search_type=page|keyword_unordered|keyword_exact_phrase, andsort_data[mode]=chronological|total_impressionswithsort_data[direction]=desc|asc. Default to active ads, all countries, and all media unless specified.In one browser-agent call, navigate directly, wait 6–9 seconds, then repeatedly scroll to the bottom and wait 1.8–4 seconds. Continue until
{target-cards}is reached, loading stabilizes, results are exhausted, or Facebook shows a login, consent, challenge, or sparse-rendering wall. Merge batches by observedad_archive_id, preserving document order and source brand.After the final sweep, run the evaluator below on the loaded page. It extracts cards bounded by visible
See ad detailscontrols, destination URLs, landing-page URLs, CTA labels, image/video metadata, dates, disclosures, platforms, and archive IDs.
(() => {
const clean=v=>(v||'').replace(/\s+/g,' ').trim();
const uniq=a=>[...new Set(a.filter(Boolean))];
const includeVideoUrls=false; // set true only when the requested artifact needs the signed CDN URL
const body=clean(document.body?.innerText||''), u=new URL(location.href);
const ctas=/^(learn more|sign up|download|install now|shop now|buy now|book now|contact us|apply now|get offer|get quote|send message|watch more|listen now|play game|see menu|order now|subscribe|join now|start now|try now|open link|more info)$/i;
const controls=[...document.querySelectorAll('button,[role="button"]')].filter(x=>/^see (ad details|summary details)$/i.test(clean(x.innerText)));
const ads=[],seen=new Set();
for(const control of controls){
let node=control,card=null;
for(let i=0;i<12&&node;i++,node=node.parentElement){
const t=clean(node.innerText||'');
if(t.length>=100&&t.length<=18000&&/(started running on|running since|library id|ad archive id|see ad details)/i.test(t)){card=node;break;}
}
if(!card)continue;
const rawText=card.innerText||'';const text=clean(rawText);
const archive=text.match(/(?:library id|ad archive id)\s*[:#]?\s*(\d{5,})/i)?.[1]||card.innerHTML.match(/(?:ad_archive_id|archive_id)[^0-9]{0,30}(\d{5,})/i)?.[1]||null;
const started=text.match(/(?:started running on|running since)\s+([^\n]+?\d{4})/i)?.[1]||null;
const anchors=[...card.querySelectorAll('a[href]')].map(a=>({href:a.href,text:clean(a.innerText).slice(0,300)})).filter(x=>x.href);
const advertiser=anchors.find(x=>/facebook\.com\//i.test(x.href)&&x.text&&!/see ad details/i.test(x.text))?.text||null;
const lines=uniq(rawText.split(/\r?\n+/).map(clean).filter(Boolean));
const copy=lines.filter(x=>x.length>12&&!/see ad details|started running on|running since|library id|ad archive id|sponsored|paid for by|amount spent|budget|impressions?|platforms|^active$/i.test(x)).slice(0,25);
const ctaLabels=uniq([...card.querySelectorAll('button,[role="button"]')].map(x=>clean(x.innerText)).concat(anchors.map(x=>x.text)).filter(x=>ctas.test(x))).slice(0,10);
const destinations=anchors.map(x=>{try{const h=new URL(x.href);if(/l\.facebook\.com/i.test(h.hostname)){const target=h.searchParams.get('u');return target||x.href}return x.href}catch{return x.href}}).filter(h=>/^(https?:)?\/\/(?!www\.facebook\.com|facebook\.com)/i.test(h));
const images=[...card.querySelectorAll('img')].map(img=>({src:img.currentSrc||img.src||null,alt:clean(img.alt),width:img.naturalWidth||img.width||null,height:img.naturalHeight||img.height||null})).filter(x=>x.src&&!/emoji|icon|avatar|profile/i.test((x.alt||'')+' '+x.src)).slice(0,12);
const videos=[...card.querySelectorAll('video')].map((v,i)=>({index:i,...(includeVideoUrls?{src:v.currentSrc||v.src||null}:{}),poster:v.poster||null,width:v.videoWidth||v.width||null,height:v.videoHeight||v.height||null,duration_seconds:Number.isFinite(v.duration)?v.duration:null,visible:(()=>{const r=v.getBoundingClientRect();return r.width>0&&r.height>0})()})).slice(0,8);
const payer=text.match(/(?:paid for by|pago por|pagado por|sponsored by|funded by)\s*[::]?\s*([^\n|]{2,180})/i)?.[1]?.trim()||null;
const disclosure=text.match(/(?:spend|amount spent|budget|gasto|orçamento|importe gastado|impressions?)\s*[::]?\s*([^\n|]{1,180})/i)?.[1]?.trim()||null;
const platforms=['Facebook','Instagram','Audience Network','Messenger'].filter(p=>new RegExp('\\b'+p.replace(/ /g,'\\s+')+'\\b','i').test(text));
const key=archive||[advertiser||'',started||'',copy.slice(0,3).join('|'),destinations[0]||''].join('|');
if(seen.has(key))continue; seen.add(key);
ads.push({position:ads.length+1,ad_archive_id:archive,advertiser,started_running_on:started,declared_payer:payer,spend_or_budget_disclosure:disclosure,platforms,copy,cta_labels:ctaLabels,images,videos,has_video:videos.length>0,destination_links:uniq(destinations).slice(0,15),document_top:Math.round(card.getBoundingClientRect().top+scrollY),raw_text:text.slice(0,7000)});
}
const all=body+' '+document.title;
return {url:location.href,title:document.title,requested_page_id:u.searchParams.get('view_all_page_id'),query:u.searchParams.get('q'),active_status:u.searchParams.get('active_status'),country:u.searchParams.get('country'),search_type:u.searchParams.get('search_type'),media_type:u.searchParams.get('media_type'),sort:{mode:u.searchParams.get('sort_data[mode]'),direction:u.searchParams.get('sort_data[direction]')},ads_sampled:ads.length,ads,no_results:/no ads match|no results|0 results|hasn't shown any results/i.test(body),login_required:/log in to facebook|log in or sign up|create an account/i.test(all),blocked:/security check|challenge|required to continue/i.test(all),consent_present:/cookie|consent|decline optional|only allow essential/i.test(body),scroll:{scroll_y:scrollY,viewport_height:innerHeight,document_height:Math.max(document.body?.scrollHeight||0,document.documentElement?.scrollHeight||0)}};
})()For a known archive ID, navigate directly to
https://www.facebook.com/ads/library/?id={ad-archive-id}. After about 8 seconds, inspectdiv[role="dialog"]and its visiblevideoelements. If necessary, activate only the visibleSee ad detailscontrol. Do not click advertiser profiles, CTAs, outbound links, or landing pages.For requested screen recordings or video inspection, capture only the rendered Facebook ad video or its surrounding card using the browser's supported media/screenshot artifact. Record poster-only, missing-source, autoplay-blocked, or non-finite-duration results as partial; never claim a recording when no video was visibly rendered. If seeking is supported, mute the video and seek to approximately 40–50% of a finite duration before capture. Do not download media or reconstruct signed CDN URLs.
Treat extracted external URLs as observed destinations only. Compare landing-page URL patterns, domains, paths, and query parameters across brands without navigating to them. Any separate landing-page audit must be explicitly requested and handled as a new navigation task.
Return the requested brands, URL parameters, scroll-batch count, stabilization state, ad records, landing-page URL comparison, media artifacts when requested, and partial/blocking reasons. Never include credentials, payment data, access tokens, or transient signed URLs beyond what is visibly required for the requested artifact.
Possible Friction Points
/ads/library/is the durable route. Page searches useview_all_page_id={page-id}; keyword searches useq={urlencoded-query}andsearch_type=keyword_unorderedorkeyword_exact_phrase.- A keyword search can expose advertiser links containing the opaque
view_all_page_id; read that ID from the actualhrefrather than guessing it. - Bracketed parameters such as
sort_data[mode]=total_impressions&sort_data[direction]=descare valid. Impression sorting changes order but does not expose exact impression totals. See ad detailsis the most reliable visible card boundary, but nested controls cause duplicates; deduplicate every batch by archive ID or a composite fallback key.- Facebook progressively renders results. A 6–9 second initial wait followed by repeated bottom sweeps is normally required for collections.
- Outbound destinations commonly use
l.facebook.comredirects.URLSearchParams.get('u')already decodes the target, so use it directly and preserve the observed target; do not infer that it is safe, canonical, live, or affiliated. - CDN image and video URLs may be signed and temporary. Preserve them only as observed, and do not reconstruct or reuse expired URLs.
- Video elements may be poster-only, lazy-loaded, or unable to autoplay. Missing
srcor poster is not proof that an ad has no video. - A visible landing-page URL is not permission to visit that destination. Compare URLs in the Ads Library unless the caller separately requests navigation.
- If a login wall, consent wall, challenge, sparse shell, or failed lazy load appears, return collected records with
partial:true; do not authenticate or bypass it. - Use a consistent residential proxy when available. Do not use authenticated GraphQL endpoints or reconstruct internal requests.