Facebook Ads Library scraper: export the ads of any Facebook page, keyword or Meta Ad Library URL (filters kept) without a Facebook account. Ad copy, creatives, CTA, links, platforms, dates, and optionally the products behind each ad. Exact limit, pay per ad.
Successful runs that saved at least one result end their status message with one short, optional invitation to leave a Store review or open an issue (never on failed or empty runs); README section "Feedback and reviews". Output and billing unchanged.
2026-11-08 (visual kit 1.1.0)
Added
Input minDaysRunning (optional, empty = every ad, as before): keeps only ads that ran at least N days
(first and last day included). Skipped ads are not billed and do not count toward the results limit; the
count is shown as a minDaysRunning warning in the report.
Output field daysRunning (integer or null) on every ad row; dataset view Ads shows it.
Input runTag (optional): copied to every row as runTag; searchable and filterable in the report.
Visual report on kit 1.1.0: at most 3 creatives per ad (the badge still shows the real number), live
progress while the run works (URLs and keywords done / total, ads so far), data-quality panel, failed inputs
grouped by code, and a final FAILED page when the run stops on an unexpected error.
2026-11-08
Added
Visual report (REPORT record, Output tab → Visual report): ad cards with page picture, status, ad text,
creative preview and gallery (up to 4 creatives), headline, call to action, platforms and dates; search-total
cards; key figures, charts, search, filters, sorting, table view, full-size viewer and CSV/JSON export of the
filtered rows. Built by the shared src/visual_kit.py (v1.0.0) with this Actor's src/report.py. At most
1,000 ads are kept in memory for it; it is never billed and a report error is only logged as a warning.
Dataset view Ads shows the page picture.
Top-level thumbnailImage (first creative picture of the ad: image → card image → video preview frame, taken
only from the ad's own snapshot; null for text-only ads) and pageProfilePicture (= snapshot.pageProfilePictureUrl).
Ads view: Ad image is the first column, Page picture the second; Products view starts with the ad image.
The visual report shows the same thumbnailImage first.
Dataset schema: pageId / adArchiveId are no longer declared next to pageID / adArchiveID (keys differing
only by case broke case-insensitive JSON readers such as PowerShell). Rows still carry both spellings, unchanged.
Output schema: Visual report link; key-value store schema: REPORT (text/html).
Fixed
Picture links of snapshot (pageProfilePictureUrl, and originalImageUrl / resizedImageUrl /
watermarkedResizedImageUrl / videoPreviewImageUrl of cards, images and videos) keep only http(s) links: a
blank, relative or data: value is now null; //host/… becomes https://host/…. Valid links are unchanged.
2026-11-07
Reliability
Network failures (timeout, connection reset, DNS or proxy error) raised by the HTTP client were not
retried: they ended the whole URL as "Unexpected error". They are now treated like a refusal, so the
existing retries (new session, new address) apply to them, also for the extra ad details and the
About tab.
When Facebook changed its query ids and re-reading them failed, the URL ended as "Unexpected error";
it now ends as "blocked" with the reason, like other refusals.
Retries wait with an exponential, jittered backoff (2 s, 4 s… for a URL; 5 s, 10 s… for a page of
results) instead of fixed steps, and no longer sleep after the last attempt.
A proxy given in the input that cannot be set up ends the run with a clear message instead of a
stack trace.
Data
facebookUrl is always a www.facebook.com address (Facebook answers some countries with
web.facebook.com / m.facebook.com links).
true/false in the URL or keyword lists is ignored instead of being searched as a page called
"True".
Every row is checked against the types declared in .actor/dataset_schema.json before it is saved
(lossless conversions only; a value that does not fit is kept and reported once in the log).
Dataset schema: the 8 keys the Actor outputs but did not declare are now declared (isAaaEligible,
pageIsDeleted, hideDataStatus, regionalRegulationData, totalActiveTime, stateMediaRunLabel,
fevInfo, menuItems), and fields that are usually empty (spend, reach, currency, countries,
report count, adId) say so.
Resources
Default memory in actor.json: 256 MB instead of 1024 MB. Measured peak of the whole process:
72 MB (15 ads) and 81 MB (2 inputs with extra details, About tab and e-commerce enrichment);
× 1.5 rounded to a power of two gives 256 MB.
Input schema: the proxy field states its default (no proxy) explicitly.
Maintenance
Unit tests in tests/ (offline: a saved Ad Library page, items of a past Apify run, HTTP layer
mocked): python -m pytest -q. .dockerignore added (tests, local storage, caches stay out of the
image).