# Shopify Store Leads Scraper - Emails, Apps & Store Size (`vonsensey/shopify-store-leads-scraper`) Actor

Find and qualify Shopify stores. Each lead carries the contact email, socials, country, currency, product and collection counts, theme, installed apps, price band, and the date the store last published a product - so you can tell a live brand from a dead one. Pay per store.

- **URL**: https://apify.com/vonsensey/shopify-store-leads-scraper.md
- **Developed by:** [Blackcube Agency AB](https://apify.com/vonsensey) (community)
- **Categories:** Lead generation, E-commerce, Business
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.75 / 1,000 stores

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Shopify Store Leads Scraper

**More from this account:** [YouTube Transcript Suite](https://apify.com/vonsensey/youtube-transcript-scraper) · [Website Contact & Email Suite](https://apify.com/vonsensey/website-contact-email-extractor) · [Career Site & ATS Jobs Suite](https://apify.com/vonsensey/career-page-job-postings-scraper-api) · [Google News Suite](https://apify.com/vonsensey/google-news-scraper-api) · [Keyword Research Suite](https://apify.com/vonsensey/google-keyword-ideas-scraper) · [eBay Data Suite](https://apify.com/vonsensey/ebay-scraper-api) · [Amazon Reviews Suite](https://apify.com/vonsensey/amazon-reviews-scraper-api) · [Reddit](https://apify.com/vonsensey/reddit-scraper-posts-comments-api) · [Meta Ad Library](https://apify.com/vonsensey/facebook-ads-library-scraper-meta-ad-api) · [Vinted](https://apify.com/vonsensey/vinted-scraper-api)

**Find Shopify stores, and know which ones are worth contacting.** Every record tells you whether
the store is still trading, how big it is, where it is, who to email, and what it runs on.

**You pay once per store delivered.** Stores that fail your filters are free. Non-Shopify domains,
dead domains, frozen stores and bot-walled sites are free. There is no run-start fee, and a run
that finds nothing costs nothing.

***

### The problem with every other Shopify lead list

A list of Shopify domains is easy to buy and mostly worthless. Half the rows are stores that
stopped trading years ago, you cannot tell a one-product hobby shop from a 4,000-SKU retailer, and
the same store shows up twice under two different addresses — billed twice.

This returns a **qualified** store instead:

| You get | Why it matters |
|---|---|
| `isActive`, `daysSinceLastProduct`, `newestProductAt` | The store published a product 2 days ago, or 900 days ago. That is the difference between a lead and a ghost. |
| `productCount`, `collectionCount`, `sizeBand` | Sort a 10-product startup from a 4,000-product retailer before you write a single email. |
| `storeId` + `myshopifyDomain` | The store's own permanent ID. `brand.com` and `brand.myshopify.com` collapse into **one record and one charge**. |
| `email`, `emails[]`, `phone`, `socials` | The published contact route, plus Instagram, TikTok, Facebook, X, YouTube, Pinterest and LinkedIn. |
| `themeName`, `apps[]` | What the store actually runs — target the stores that already use a tool, or the ones that don't. |
| `country`, `province`, `city`, `currency`, `shipsToCountries` | The store's own declared market, not a guess from its language. |
| `priceMin`, `priceMax`, `priceAverage`, `vendors[]`, `productTypes[]` | Price band and catalogue mix, for positioning and niche sizing. |

***

### Measured field coverage

Real numbers from a 25-store discovery run, not estimates. Rerun it yourself and check.

| Field | Present on |
|---|---|
| Theme | **100%** |
| Contact email | **72%** |
| Active in the last year | **60%** |
| Social profiles | **56%** |
| Installed apps detected | **44%** |

Nothing is invented to fill a column. A store that publishes no email returns `null`, because a
guessed address is worse than no address — it quietly poisons your outreach list.

***

### Two ways to run it

#### 1. Qualify stores you already have

Paste domains into **Store URLs**. Bare domains, full URLs and `.myshopify.com` handles all work.

```json
{ "storeUrls": ["allbirds.com", "deathwishcoffee.com", "kith.com"] }
```

#### 2. Find stores you don't have

Clear **Store URLs** and turn on **Discover new stores**. Discovery is broad rather than
keyword-targeted, so you narrow it with the filters — and **anything filtered out is free**, so a
tight filter costs you nothing but time.

**Repeat runs bring back new stores, not the ones you already paid for.** Each run samples a
different slice by default; set `discoverySeed` if you deliberately want a run to be reproducible.

```json
{
  "discoverStores": true,
  "countries": ["US", "CA"],
  "minProducts": 20,
  "activeWithinDays": 90,
  "keywords": ["coffee", "roaster"],
  "maxStores": 500
}
```

***

### How to find new Shopify stores

Set `launchedWithinDays` to reach stores while they are still choosing their tools. It ranks by the
oldest product a store ever published, so a shop that started last quarter is separable from one
that started in 2014. Stores whose catalogue is too large to establish a launch date are **excluded
rather than guessed at**.

### How to find Shopify stores by country

Set `countries` to ISO codes — `["GB", "IE"]`, `["DE", "AT", "CH"]`. This filters on the market the
store itself declares, and each record also carries `currency` and `shipsToCountries`, so you can
separate "based in Germany" from "ships to Germany".

### How to find Shopify stores using a specific app

Turn on **Include theme and apps** and filter the output on `apps[]`. Every record lists the apps
detected on the storefront — email, reviews, subscriptions, support, loyalty, page builders and
analytics — so you can build an install-base list, or target the stores missing a category entirely.

### How to get Shopify store owner emails

Turn on **Include contact details**. Each store returns the published contact address in `email`,
every address found in `emails[]`, plus `phone` and seven social networks. Role addresses on the
store's own domain are ranked first, so you get `support@brand.com` rather than the owner's
personal inbox where both exist.

### How to check if a Shopify store is still active

`isActive` and `daysSinceLastProduct` answer this on every record. A store that has not published a
product in years is still online, still resolving, and still worthless as a lead — and no other
field in a lead list will tell you.

### How to export a Shopify store's product catalogue summary

Turn on **Include catalogue** for `priceMin`, `priceMax`, `priceAverage`, `vendors[]` and
`productTypes[]` — the catalogue's shape without the catalogue's bulk. Store size arrives on every
record regardless, even for stores with tens of thousands of products.

***

### Sample output

```json
{
  "type": "store",
  "storeUrl": "https://www.deathwishcoffee.com",
  "domain": "www.deathwishcoffee.com",
  "myshopifyDomain": "deathwishcoffee.myshopify.com",
  "storeId": 2717209,
  "name": "Death Wish Coffee",
  "country": "US", "province": "New York", "city": "Round Lake", "currency": "USD",
  "productCount": 148, "collectionCount": 124, "sizeBand": "mid",
  "isActive": true, "daysSinceLastProduct": 2,
  "newestProductAt": "2026-08-18T08:56:35-04:00",
  "oldestPublishedProductAt": "2014-03-12T13:35:00-04:00",
  "priceMin": 5, "priceMax": 119.99, "priceAverage": 38.58,
  "productTypes": ["Accessories", "Apparel", "Coffee", "Mugs", "Storage & Brewing"],
  "themeName": "Dawn",
  "apps": ["Gorgias", "Klaviyo", "Minmaxify", "OrderGroove", "Postscript", "Rebuy", "Smile.io"],
  "email": "hey@deathwishcoffee.com",
  "socials": { "instagram": "deathwishcoffee", "facebook": "deathwishcoffee", "x": "deathwishcoffee" },
  "offersShopPayInstallments": true,
  "discoveredVia": "input"
}
```

Alongside the store rows, every run also emits **free** `error` rows (one per unusable domain, with
a machine-readable `reason`) and one free `diagnostics` row summarising what was scanned, delivered,
filtered and skipped.

***

### What you are charged for

One charge per **store record delivered**. That is the whole billing model.

**Always free:**

- stores that fail any filter you set
- domains that are not Shopify stores
- dead, parked and unreachable domains
- frozen or deleted stores
- sites behind a bot wall that refuse to answer
- **any store whose data could not actually be read** — a rate-limited store is a free error row,
  never a full-price row full of nulls
- a domain that turns out to be served by a different shop than it claims
- every `error` and `diagnostics` row
- the second sighting of a store you have already been charged for in the run
- a run that returns nothing

**Cost controls built in:** `maxStores` caps what you pay for. `maxCostUsd` stops the run cleanly
before it exceeds a hard ceiling. `maxStoresScanned` bounds how far a narrow filter searches. The
three enrichment switches let you trade detail for speed on a big sweep.

***

### Setup

**Apify Proxy is required** — select it in the Proxy field (it is on by default). Datacenter proxy
is enough; you do not need residential. Runs without a proxy return almost nothing.

***

### FAQ

**Does this work on stores with a custom domain?**
Yes, and it recognises them as the same store as their `.myshopify.com` address, so you are never
billed twice for one shop.

**Can I target a specific niche?**
Use `keywords`, which matches the store name, description, domain, vendors and product types.
Discovery itself is broad, so a narrow keyword needs a higher `maxStoresScanned` — and everything
rejected along the way is free.

**Why is `oldestPublishedProductAt` sometimes null?**
Very large catalogues cannot be paged to the end, so the launch date genuinely cannot be
established. It returns `null` rather than a wrong date.

**Why is `email` null on some stores?**
Because that store publishes no contact address. Inventing `info@` would give you a bounce and a
damaged sending reputation. Addresses are also only accepted from the store's **own domain** —
a support address belonging to some third-party tool the store happens to use is not your lead.

**Will I get the same stores if I run it again?**
No. Each discovery run samples a different slice, so a weekly top-up brings new leads rather than
re-charging you for ones you already have. Pin `discoverySeed` if you want a run to repeat exactly.

**Is the data personal information?**
The contact details are business contact routes a merchant publishes on its own storefront and
policy pages for exactly this purpose. Handle them under your own lawful basis and honour any
opt-out you receive.

***

### Unofficial

This is an **unofficial** tool. It is **not affiliated with, endorsed by, or sponsored by Shopify
Inc.** "Shopify" is a trademark of Shopify Inc. and is used here only to describe what the tool
works with. It reads only what a store publishes publicly on its own storefront — nothing private,
nothing behind a login.

### Use cases

- **Build a qualified lead list.** Every store arrives with its contact email, country, catalogue size and price band, so the list is already filtered before you write a line.
- **Sell to a tech stack.** Find the stores running a given theme or app category — or the ones missing it entirely, which is the better pitch.
- **Size a niche before entering it.** Catalogue depth, price band, vendors and whether a store still ships new products tell you if a market is alive.
- **Catch new stores early.** Recently launched brands are still choosing the tools they will pay for.

### Run it on a schedule

A one-off pull answers a question; a schedule answers it every day without you. Open **Schedules** in the Apify Console, point a cron at this Actor, and the dataset keeps filling on its own — no server, no cron box, no babysitting. Everything here is built to be re-run: you are billed per store delivered, so a scheduled run that finds nothing new costs nothing.

### FAQ

#### Where do the stores come from?

Live storefronts, read at the moment you run it — either the URLs you supply or discovery across the public web. Nothing here is a resold static list.

#### Is the email the store owner's?

It is the contact address the store publishes. Where a store exposes several, the most owner-facing one is preferred and the rest are kept.

#### Do I pay for stores that fail my filters?

No. A store that does not match your country, size or keyword filter is a free row, so you can filter aggressively without paying for it.

#### Can I check which apps and theme a store runs?

Yes — theme family, Theme Store ID where it has one, and the installed app categories, on the same row as the contact details.

***

Something wrong, or a field you need that is missing? Open an issue on the **Issues** tab — it is read and it gets fixed. If this saved you time, a rating on the Store page helps the next person find it.

# Actor input Schema

## `storeUrls` (type: `array`):

Shopify stores to qualify, one per line. Accepts a bare domain (example.com), a full URL, or a myshopify handle (brand.myshopify.com). Leave empty to let discovery find stores instead.

## `discoverStores` (type: `boolean`):

Also find Shopify stores automatically, instead of only qualifying the ones you supply. Discovery is broad rather than keyword-targeted, so narrow it with the filters below. Stores that fail your filters are never charged.

## `maxStores` (type: `integer`):

How many qualified store records to return. This is what you pay for — one charge per delivered store.

## `maxStoresScanned` (type: `integer`):

Ceiling on how many candidates are checked while looking for matches. Raise it when a narrow filter (a small country, a rare keyword) rejects most candidates. Checking is free; only delivered stores are charged.

## `discoverySeed` (type: `integer`):

Pin this to get the same discovered stores every run — useful for reproducible tests. Leave empty and each run samples a different slice, so repeat runs bring back new stores instead of the ones you already paid for.

## `ccIndex` (type: `string`):

Pin discovery to one specific web-crawl index, e.g. CC-MAIN-2026-30. Leave empty to draw from the most recent indexes automatically, which is what you want unless you are reproducing an old run.

## `countries` (type: `array`):

Only deliver stores based in these countries. Two-letter ISO codes, e.g. US, GB, AU, CA, DE.

## `minProducts` (type: `integer`):

Only deliver stores with at least this many published products. Filters out empty and abandoned stores.

## `maxProducts` (type: `integer`):

Only deliver stores with at most this many published products. Useful for targeting small independent brands rather than large retailers.

## `activeWithinDays` (type: `integer`):

Only deliver stores that published a product in the last N days. The strongest signal that a store is still trading. Needs the catalogue, so it requires 'Include catalogue' to stay on.

## `launchedWithinDays` (type: `integer`):

Only deliver stores that look recently launched, judged by the oldest product they ever published. Turns on the launch-era lookup automatically (one extra request per store). Stores whose catalogue is too large to page to the end are excluded rather than guessed at.

## `keywords` (type: `array`):

Only deliver stores whose name, description, domain, vendors or product types contain at least one of these words.

## `excludeKeywords` (type: `array`):

Never deliver stores matching any of these words.

## `includeCatalog` (type: `boolean`):

Adds the last-product date (is this store still active?), price range, vendors and product types. Costs one extra request per store.

## `includeContacts` (type: `boolean`):

Adds the store's public contact email, phone and social profiles, taken from the storefront and its published policy pages.

## `includeTechStack` (type: `boolean`):

Adds the Shopify theme and the apps detected on the storefront — useful for targeting stores that already run (or lack) a given tool.

## `includeLaunchDate` (type: `boolean`):

Adds the oldest published product date as a rough store-age signal. Off by default because it costs one extra request per store.

## `maxCostUsd` (type: `number`):

Hard ceiling on what this run may cost, in USD. The run stops cleanly once delivering another store would exceed it. Accepts fractions, e.g. 0.25. Leave empty for no ceiling.

## `maxConcurrency` (type: `integer`):

Parallel stores in flight. Higher is faster; the default is tuned to stay well inside Shopify's rate limits.

## `proxyConfiguration` (type: `object`):

Required. Shopify rate-limits by IP, so a run without rotating proxies returns almost nothing. Datacenter proxies are enough — residential is not needed.

## Actor input object example

```json
{
  "storeUrls": [
    "https://www.allbirds.com",
    "https://www.deathwishcoffee.com",
    "https://kith.com"
  ],
  "discoverStores": false,
  "maxStores": 25,
  "countries": [],
  "keywords": [],
  "excludeKeywords": [],
  "includeCatalog": true,
  "includeContacts": true,
  "includeTechStack": true,
  "includeLaunchDate": false,
  "maxConcurrency": 10,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `stores` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "storeUrls": [
        "https://www.allbirds.com",
        "https://www.deathwishcoffee.com",
        "https://kith.com"
    ],
    "countries": [],
    "keywords": [],
    "excludeKeywords": [],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("vonsensey/shopify-store-leads-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "storeUrls": [
        "https://www.allbirds.com",
        "https://www.deathwishcoffee.com",
        "https://kith.com",
    ],
    "countries": [],
    "keywords": [],
    "excludeKeywords": [],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("vonsensey/shopify-store-leads-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "storeUrls": [
    "https://www.allbirds.com",
    "https://www.deathwishcoffee.com",
    "https://kith.com"
  ],
  "countries": [],
  "keywords": [],
  "excludeKeywords": [],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call vonsensey/shopify-store-leads-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,vonsensey/shopify-store-leads-scraper"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/MclKlYDTipUym9rpK/builds/YhgVgyaWYVuoCqCMe/openapi.json
