# Changelog of Facebook Ads Library Scraper (`badr_xba/facebook-ads-library-scraper`) Actor

- **URL**: https://apify.com/badr_xba/facebook-ads-library-scraper/changelog.md
- **Full Actor documentation**: https://apify.com/badr_xba/facebook-ads-library-scraper.md

## Changelog

### 2026-11-09 (review invitation)

- 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).
