# Changelog of Shopify Store Leads — Catalog Size, Apps, Contacts (`autofacts/shopify-store-leads-catalog-size-apps-contacts`) Actor

- **URL**: https://apify.com/autofacts/shopify-store-leads-catalog-size-apps-contacts/changelog.md
- **Full Actor documentation**: https://apify.com/autofacts/shopify-store-leads-catalog-size-apps-contacts.md

## Changelog

### 0.2 — 2026-09-16

#### Fixed

- The prefilled store list now exercises every field. It was `gorjana.com`, `allbirds.com`
  and `kith.com` — three large brands that between them returned `announcementBarText:
  null` and `policies: []` on all three rows, so the sample output a buyer sees first
  showed both fields permanently empty. It is now `gorjana.com` (US, 976 products, emails
  and apps), `tentree.com` (US, 6,498 products, an English announcement bar) and
  `ayvii.com` (AT, 13 products, five policies and a German bar). That also drops the
  slowest store: `kith.com` carries 25,001 products and took most of the daily test's
  runtime.
- The README no longer claims a rotating bar returns only its first message. Whether it
  does depends on the theme: `tentree.com` puts every slide in one element and returns all
  four promotions joined. The new prefill surfaced this, and the field's behaviour is
  documented as it is rather than as intended.
- The README no longer implies that eight in ten stores of every kind publish canonical
  policy URLs. Measured across nine well-known direct-to-consumer brands, one did.
- The default proxy no longer names a group. It was `["DATACENTER"]`, which is validated
  against the *caller's* account — and on current Apify plans the shared datacenter pool is
  a paid add-on, so the default input failed validation before the run could start:
  *"You currently do not have access to proxy groups: DATACENTER"*. That broke the actor for
  every buyer without the add-on, and for the daily auto-tester, which runs the prefilled
  default. `{"useApifyProxy": true}` lets each run use whatever groups its own account has.

#### Added

- `announcementBarText` — the storefront's announcement bar message, verbatim and in the
  store's own language, or `null`. No extra request: the bar is in the homepage already
  fetched for the theme and apps. Measured non-null on 18 of 29 live storefronts.
  Deliberately not parsed into a free-shipping threshold, a discount percentage or a promo
  code — the copy is in the merchant's own language, and a percent-off pattern run over a
  storefront matches product prices rather than promotions.

#### Fixed

- `activity.productsPerMonth` and `policies` appear in the `overview` dataset view. Both
  shipped on the record on 2026-09-15 with display labels already written, but neither was
  added to the view's field list, so neither has ever rendered in the Store's default view.
- `policies` declared in `dataset_schema.json`. It has shipped on the record since 0.1 but
  was missing from the field list, so the Apify Store's field table omitted it.

### 0.1 — 2026-09-15

First version. Finds and qualifies Shopify stores, one billable row per store.

#### Added

- Enrichment path: `storeUrls` in, full `Store` record out.
- Discovery path: a bundled seed of 6,542 verified stores, then the Common Crawl URL
  index (`ccIndex`, newest crawl resolved at run time when unset). Candidates are drawn
  lazily — index pages are fetched only once the cheaper sources run out — and a run that
  reaches its scan budget before filling `maxItems` says so rather than returning quietly
  short.
- Filters — `countries`, `query`, `excludeKeywords`, `minProducts`, `maxProducts`,
  `category`, `activeWithinDays`, `shipsToCountries`, `minAvgPrice`, `maxAvgPrice`,
  `inStockOnly`, `hasApps`, `appMatchMode`, `excludeApps`, `hasSocials`, `requireEmail`,
  `requirePhone` — each evaluated
  at the earliest funnel stage whose data can decide it, so a rejected store costs as
  little as one request, and none of them adds a request to a run.
- `Store` record: identity, catalog (product and collection counts, price band, product
  types, vendors, size band), activity (newest and oldest product, days since last
  product, `isActive`, new products per month), published policy pages, tech stack (theme,
  installed apps, card brands) and business contacts.
- Rate-limit handling: `Retry-After` is honoured, a 429 slows every request rather than
  being retried straight into the same limit, and a run refused ten times in a row stops
  instead of spending the rest of its budget being blocked. The proxy defaults to Apify
  Proxy's datacenter group, because the limit is per client IP and rotation is the answer
  to it.
- Contact extraction limited to business role addresses, with Cloudflare email-protection
  decoding and same-domain script-blob extraction. The store's contact page is fetched
  only when the homepage published no address, and only by following the store's own
  link.
- `previousDatasetId` accepted and ignored, reserving the name so change tracking can be
  added later without a breaking input change.
- `dataset_schema.json` with `views` — `overview` and `catalog` — so the Apify Store
  renders a field table.
- 62 tests: `test/funnel.test.mjs` pins the exact request count for every rejection path,
  `test/enrich.test.mjs` covers the extraction functions, and `test/candidates.test.mjs`
  pins when a Common Crawl index page is fetched.
