SFOLN

Catalogue / Ad intelligence

Ad Library Scraper – Meta, Google & TikTok

sfoln/ad-library-scraper · Social media, Lead generation, Ecommerce On request

One brand in, its ads across the Meta (Facebook, Instagram), Google Ads Transparency, TikTok and LinkedIn ad libraries out: ad counts, active and new ads, running time, top creatives, CTAs, landing domains and offers per platform. $12 per 1,000 platform snapshots.

About

What it does

See what a brand advertises across the Meta (Facebook and Instagram), Google Ads Transparency, TikTok and LinkedIn ad libraries in one run: for each library you get one snapshot with the number of ads and how many are running, new ads in the last 7 and 30 days, first and last seen, the longest and median running time, formats, calls to action, landing domains, countries, offer phrases and the top creatives with their text and media URLs; plus a cross-platform summary with the landing domains the brand pushes everywhere. Built for performance marketers, agencies preparing pitches and AI agents doing competitor research. $0.012 per library snapshot, so all four libraries cost $0.048 per brand.

Price

Price per delivered record

EventPricePer 1,000Charged for
Ad platform snapshot
platform-snapshot
$0.012$12 / 1,000Charged once per brand and ad library (Meta, Google Ads Transparency, TikTok or LinkedIn) whose snapshot was delivered: the first page of the brand's ads summarized into counts, activity, running time, formats, CTAs, landing domains, offers and top creatives. A library that fails, or answers 'no ads' without the provider billing it, is not charged.
Extra ad library page
extra-page
paid plans only
$0.007$7 / 1,000Paid plans only. Charged once per additional page of ads fetched for a platform snapshot (pagesPerPlatform above 1), and once for the Google advertiser lookup of a brand given without a website domain. A page that fails, or comes back empty without the provider billing it, is not charged.

Apify free plan: up to 5 items per run. Paid plans have no per-run cap; set maxTotalChargeUsd to cap spend.

Run

Call it from code, an agent or the Console

This API is private on Apify for now. Email us and we will publish it for you, usually within two working days. The calls below work once it is public. contact@sfoln.com

curl · synchronous run, rows in the response
curl -X POST "https://api.apify.com/v2/actors/sfoln~ad-library-scraper/run-sync-get-dataset-items" \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"brands":["Nike | nike.com"]}'
mcp.json · hosted Apify MCP server
{
  "mcpServers": {
    "apify": {
      "type": "http",
      "url": "https://mcp.apify.com?tools=sfoln/ad-library-scraper"
    }
  }
}
asynchronous run · for large batches
POST https://api.apify.com/v2/actors/sfoln~ad-library-scraper/runs?maxTotalChargeUsd=1
GET  https://api.apify.com/v2/actor-runs/{runId}            # until status is SUCCEEDED
GET  https://api.apify.com/v2/datasets/{defaultDatasetId}/items?format=json

Authenticate with an Apify API token. MCP clients can instead sign in with OAuth when prompted. Example prompt for an agent: Which ad libraries is Nike (nike.com) advertising in right now? Compare its ads on Meta, Google, TikTok and LinkedIn: how many are running, since when, and which landing pages and offers they push.

Input

Input fields

Brands or advertisers to look up in the Meta, Google Ads Transparency, TikTok and LinkedIn ad libraries. Each brand gets one snapshot record per library plus one cross-platform summary record.

FieldTypeDefaultMeaning
brands *arrayrequiredBrands
One brand per line: a name ("Nike"), its website domain ("nike.com") or both ("Nike | nike.com"). Optional ids after more "|": facebook:<page ID>, linkedin:<company ID or name>, tiktok:<advertiser name>, google:<AR… advertiser ID>. API callers can pass objects { name, domain, facebookPageId, linkedinCompanyId, tiktokAdvertiser, googleAdvertiserId }. Accounts on the Apify free plan can process up to 5 brands per run (one page per library).
platformsarray["meta","google","tiktok","linkedin"]Ad libraries
Which ad libraries to search. Each one is a separate snapshot (and charge). Default: all four.
countrystring—Country
2-letter country code (US, GB, DE …) to restrict Meta, Google and LinkedIn to ads shown there. Empty = all countries. The TikTok library is always searched as a whole.
activeOnlybooleanfalseActive ads only
Meta: request only ads that are running now. Google, TikTok and LinkedIn have no status filter: ads last shown more than 7 days ago are left out of their snapshots.
pagesPerPlatforminteger1Pages per library
Pages of ads to read per library (about 20–30 ads per page). The first page is part of the snapshot; every further page is charged as extra-page. Paid plans only above 1. Range 1–5.
maxCreativesPerPlatforminteger5Top creatives per library
How many creatives each snapshot lists in topCreatives (running ads first, then the longest running). Range 1–25.
includeAllAdsbooleanfalseInclude every ad
Also list every ad of the fetched pages in each snapshot (field ads). No extra charge; larger records.
maxBrandsinteger100Max brands
Safety cap on the number of brands processed in this run; brands beyond it are ignored and counted as inputItemsDropped in the OUTPUT record. Range 1–1000.
concurrencyinteger3Concurrency
How many brands are processed in parallel (the libraries of one brand are always searched in parallel). Range 1–8.
itemTimeoutSecsinteger600Per-brand timeout (seconds)
Time limit per brand. No new page is started in its last 90 seconds. Range 120–3600.
includeRawbooleanfalseInclude raw upstream response
Attach the unmodified ad library pages to each snapshot record (much larger output).
Example input
{
  "brands": [
    "Nike | nike.com"
  ]
}

Output

One record per input item

Records land in the run's dataset; a synchronous call returns them directly. Each has a status (ok is charged; everything else is free) and a charged flag. The run's OUTPUT record totals items and charges.

FieldTypeMeaning
inputobject | stringInput
The brand as parsed: { name, domain?, facebookPageId?, linkedinCompanyId?, linkedinCompany?, tiktokAdvertiser?, googleAdvertiserId? }.
recordTypestringRecord type
platform = one ad library's snapshot (charged platform-snapshot); summary = the brand's cross-platform summary (not charged). e.g. "platform"
brandstringBrand
Brand name. e.g. "Nike"
platformstringAd library
meta | google | tiktok | linkedin (platform records).
platformNamestringLibrary name
Meta (Facebook, Instagram) | Google Ads Transparency | TikTok | LinkedIn.
queryobjectQuery
{ by, value }: what the library was asked (pageId, companyName, advertiserId, domain, advertiserName, companyId or company).
resolvedAdvertisersarrayGoogle advertisers
Google only, for a brand given by name: the advertisers the lookup found ({ advertiserId, name, region, adsEstimate }).
advertiserstring | nullAdvertiser
Advertiser name as the library shows it on the ads.
advertiserIdstring | nullAdvertiser ID
Facebook page ID, Google advertiser ID or LinkedIn company ID when the snapshot was fetched by ID.
adsFetchedintegerAds fetched
Distinct ads on the fetched pages.
totalAdsEstimateinteger | nullTotal ads (library)
The library's own total for the query, when it reports one.
adsFoundintegerAds in snapshot
Ads the snapshot covers (adsFetched minus ads known to be over when activeOnly is on).
activeAdsintegerActive ads
Meta: the library's active flag. Google, TikTok, LinkedIn: last shown within 7 days.
newAds7dintegerNew ads (7 days)
Ads first seen in the last 7 days.
newAds30dintegerNew ads (30 days)
Ads first seen in the last 30 days.
firstSeenstring | nullFirst seen
Earliest start date among the ads (ISO).
lastSeenstring | nullLast seen
Latest date an ad was shown (ISO; now for ads Meta marks active).
longestRunningDaysinteger | nullLongest running (days)
Platform records: the longest an ad has run, in days.
medianDaysRunninginteger | nullMedian running (days)
Median running time of the ads, in days.
formatsobjectFormats
Ads per format: image, video, carousel, text, dynamic, other, unknown.
ctasobjectCalls to action
Ads per call-to-action text (Meta, LinkedIn).
landingDomainsarrayLanding domains
[{ domain, ads }]: the registrable domains the ads link to, most used first.
countriesarrayCountries
Countries as the library reports them (ISO codes on Meta and Google, names on LinkedIn).
publisherPlatformsarrayPlacements
Meta placements: facebook, instagram, messenger, audience_network.
offersarrayOffers
[{ offer, ads }]: offer phrases found in the ad copy (e.g. 30% off, free shipping, free trial, sale, price mention).
topCreativesarrayTop creatives
Running ads first, then the longest running: { adId, url, advertiser, format, text, headline, cta, landingUrl, landingDomain, imageUrls, videoUrls, thumbnailUrl, firstSeen, lastSeen, isActive, daysRunning, countries, publisherPlatforms, reach }.
adsarrayAll ads
Every ad of the fetched pages, same shape as topCreatives (only with includeAllAds).
pagesFetchedintegerPages fetched
Library pages read for this snapshot.
extraPagesChargedintegerExtra pages charged
extra-page events charged for this library (further pages and the Google advertiser lookup).
domainstring | nullDomain
Summary records: the brand's website domain.
platformsRequestedarrayLibraries requested
Summary records.
platformsWithAdsarrayLibraries with ads
Summary records: libraries where the brand has ads.
platformsFailedarrayLibraries failed
Summary records: libraries without a snapshot (error or budget; not charged).
totalAdsFoundintegerTotal ads
Summary records: ads across all snapshots.
totalActiveAdsintegerTotal active ads
Summary records.
longestRunningobject | nullLongest running
Summary records: { platform, days } of the longest-running ad.
sharedLandingDomainsarrayShared landing domains
Summary records: [{ domain, ads, platforms }] for domains the brand advertises on more than one library.
topLandingDomainsarrayTop landing domains
Summary records: [{ domain, ads }] across all libraries.
platformsobjectPer library
Summary records: { <library>: { status, adsFound, activeAds, charged } }.
snapshotsChargedintegerSnapshots charged
Summary records: platform-snapshot events charged for the brand.
statusstringStatus
ok | not_found | error | skipped_budget | skipped_free_plan | skipped_unmetered e.g. "ok"
chargedbooleanCharged
Whether this record incurred a pay-per-event charge.
chargedEventstringCharged event
platform-snapshot (or extra-page when only the Google lookup was charged); present only when charged.
chargedCountintegerEvents charged
Events actually charged when a charge came back short (present only then).
sourcestringSource
scrapecreators
fetchedAtstringFetched at
ISO timestamp.
errorobjectError
{ code, message, provider?, status? } when status is not ok: no_ads, no_advertiser, not_found, budget_exhausted, aborted; upstream codes: auth, insufficient_credits, rate_limit, timeout, network, server, client, invalid_response; runtime codes: free_plan_limit, unmetered, charge_failed, record_too_large, invalid_record, dataset_push_failed.
warningsarrayWarnings
E.g. a library skipped for the budget, paging stopped by the time limit, the free-plan page limit.
rawstringRaw pages
The unmodified library pages (only with includeRaw).
Example record
{
  "input": {
    "name": "Nike",
    "domain": "nike.com"
  },
  "recordType": "platform",
  "brand": "Nike",
  "platform": "meta",
  "platformName": "Meta (Facebook, Instagram)",
  "query": {
    "by": "companyName",
    "value": "Nike"
  },
  "advertiser": "Nike",
  "advertiserId": null,
  "adsFetched": 30,
  "totalAdsEstimate": 1200,
  "adsFound": 30,
  "activeAds": 22,
  "newAds7d": 4,
  "newAds30d": 11,
  "firstSeen": "2026-01-15T08:00:00.000Z",
  "lastSeen": "2026-10-14T10:00:00.000Z",
  "longestRunningDays": 272,
  "medianDaysRunning": 46,
  "formats": {
    "image": 14,
    "video": 11,
    "carousel": 5
  },
  "ctas": {
    "Shop now": 21,
    "Learn more": 9
  },
  "landingDomains": [
    {
      "domain": "nike.com",
      "ads": 27
    },
    {
      "domain": "fb.com",
      "ads": 3
    }
  ],
  "countries": [],
  "publisherPlatforms": [
    "facebook",
    "instagram"
  ],
  "offers": [
    {
      "offer": "free shipping",
      "ads": 6
    },
    {
      "offer": "30% off",
      "ads": 2
    }
  ],
  "topCreatives": [
    {
      "adId": "1001",
      "url": "https://www.facebook.com/ads/library?id=1001",
      "advertiser": "Nike",
      "format": "image",
      "text": "Up to 30% off running shoes. Free shipping on orders over $50.",
      "headline": "Nike",
      "cta": "Shop now",
      "landingUrl": "https://www.nike.com/running",
      "landingDomain": "nike.com",
      "imageUrls": [
        "https://scontent.xx.fbcdn.net/1001.jpg"
      ],
      "videoUrls": [],
      "thumbnailUrl": "https://scontent.xx.fbcdn.net/r_1001.jpg",
      "firstSeen": "2026-07-01T00:00:00.000Z",
      "lastSeen": "2026-10-14T00:00:00.000Z",
      "isActive": true,
      "daysRunning": 106,
      "countries": [],
      "publisherPlatforms": [
        "facebook",
        "instagram"
      ],
      "reach": null
    }
  ],
  "pagesFetched": 1,
  "extraPagesCharged": 0,
  "status": "ok",
  "charged": true,
  "chargedEvent": "platform-snapshot",
  "source": "scrapecreators",
  "fetchedAt": "2026-10-14T10:00:00.000Z"
}

FAQ

Questions

What does one snapshot contain, and how many ads does it cover?
A snapshot summarizes the first page of the brand's ads in one library (about 20–30 ads): how many ads and how many are active, new ads in the last 7 and 30 days, first and last seen, the longest and median running time, formats, calls to action, landing domains, countries, Meta placements, offer phrases and the top creatives with text and media URLs. Set pagesPerPlatform to read up to 5 pages per library (extra-page, paid plans).
Am I charged when a library has no ads for the brand?
Only when the data provider billed that empty answer; otherwise the platform record has status not_found and charged false. A library that errors or times out is never charged, and the other libraries of the brand are not affected.
Why add the website domain?
Google's Ads Transparency Center is searched by the advertiser's website domain. Without one, the Actor first looks the advertiser up by name, which is one more upstream request (charged as extra-page on paid plans) and can pick a namesake.

Machine-readable

This page for agents