All notable changes to this actor will be documented in this file.
- Replaced 30-cap DOM-scroll path with GraphQL cursor pagination. The previous implementation called
page.mouse.wheel() (which fired at default mouse position (0,0), not over Meta's virtualized scroll container) plus a 3-iteration stable-count early-exit. The result was a hard cap at ~30 ads per query regardless of maxAdsPerQuery. The new path opens one Playwright page per query, intercepts the first /api/graphql/ request to harvest lsd / fb_dtsg / doc_id / cookies, closes the browser, and walks AdLibrarySearchPaginationQuery over HTTP using got-scraping until has_next_page === false or the user-supplied cap is reached. maxAdsPerQuery: 500 now actually returns up to 500 ads for high-volume advertisers.
doc_id sniffed at bootstrap, not hardcoded. Meta rotates query identifiers on every build; we read whatever is current from the live request rather than embedding a value that goes stale.
forwardCursor / cursor treated as opaque. The captured variables object is reused verbatim with only the cursor key mutated per page.
- Retry-with-exponential-backoff on 5xx / 429 / FB rate-limit error codes (1675004 / 1357004). Three attempts per cursor, capped at 30s backoff, then a hard error so the run fails loudly instead of silently truncating.
- 800–2000ms jitter between pagination calls to stay under Meta's anonymous-session throttle.
nextPageCursor surfaced on the final dataset row when the run was stopped by maxAdsPerQuery (or maxAds) before the connection ended. Opaque resume token; absent when pagination ran to completion.
extractAdObjects(), findJsonObjectEnd(), scrapeRenderedAds(), MAX_SCROLLS_PER_SOURCE, SCROLL_WAIT_MILLIS — DOM HTML-grep and scroll-loop helpers no longer needed.
- Added vitest with 41 tests covering URL parsing (
test/url.test.ts), token extraction (test/token-extraction.test.ts), paginator cursor loop / retry / dedup / schema drift (test/paginator.test.ts), and runner orchestration (test/runner.test.ts). Live Meta calls remain a manual smoke test via apify run.
- Added
query as the headline input — accepts keywords (crochet), page handles (@ZapierApp), page URLs, or full Ad Library URLs.
- Renamed flat fields:
country, activeStatus, adType, mediaType, period, sortBy, maxAds, maxAdsPerQuery. The old scrapePageAds.*, urls, count, and limitPerSource keys are still accepted for backwards compatibility.
- Added
mediaType and adType filters that map to Ad Library URL parameters.
period filter now applies to keyword searches too (not only page-ad scrapes).
- Each output row now includes a
query field so multi-search runs can be split apart later.
- Initial compatible actor for
curious_coder/facebook-ads-library-scraper.
- Copied the public input schema, memory sizing, Store metadata, dataset shape, and pay-per-ad pricing event.
- Added a direct Playwright scrape path for rendered Meta Ad Library results.