# Shopify & E-commerce Store Leads — Contacts, Apps & Products (`signalloom/ecommerce-store-leads`) Actor

Turn a list of store domains into e-commerce leads: detects Shopify, WooCommerce, BigCommerce, Magento and 8 more platforms, installed apps, exact product count, price range, country, and company contacts (role emails, phones, socials). $0.005 per store; non-stores and blocked sites free.

- **URL**: https://apify.com/signalloom/ecommerce-store-leads.md
- **Developed by:** [Signalloom](https://apify.com/signalloom) (community)
- **Features:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 store enricheds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

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

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Shopify & E-commerce Store Leads: Contacts, Apps & Products

Turn **any list of store websites into sales-ready leads**. For each site you get the e-commerce platform, an **exact product count**, price range, location and currency, the **apps and marketing tools** it runs (Klaviyo, Judge.me, Recharge, Attentive…), and **contact emails, phones and social profiles**.

- **12 platforms detected**: Shopify, WooCommerce, BigCommerce, Magento / Adobe Commerce, Salesforce Commerce Cloud, Shopware, PrestaShop, Squarespace, Wix, Ecwid, OpenCart, plus Webflow and WordPress sites
- **Live catalog stats, not estimates**: exact product count, price min / median / max, share of products on sale or sold out, products added in the last 30 days, top brands and categories (Shopify and WooCommerce)
- **Store location and setup** (Shopify): city, region, country, currency, countries it ships to, Shop Pay Installments, and the .myshopify.com address
- **40+ apps and tools**: email and SMS, reviews, subscriptions, loyalty, support chat, buy now pay later, ad pixels and analytics
- **Contacts**: company role emails (info@, support@…) ranked so the store's own customer-facing address comes first, business phone numbers, and Instagram, TikTok, Facebook, X, YouTube, Pinterest and LinkedIn profiles. For Shopify stores it also reads the standard contact and privacy pages, where support emails usually are.
- **Pay only for what you keep**: sites that fail, block automated visits, aren't stores, or don't match your filters are **free**. The same site entered twice is checked and charged once.
- **Respects store terms**: a store whose terms of service contain its own wording against scraping, crawling or automated access is skipped for free, with the clause quoted so you can see why. Store owners can ask to be excluded; we honour it immediately.

**Price: $5 per 1,000 stores** ($0.005 each). Failed, blocked and non-store sites are free. The prefilled example (3 stores) finishes in under a minute.

### Use this when…

- you have a list of **Shopify store** domains and need **store contacts** (company emails, phones, Instagram, TikTok) for outreach
- you want to know **which Shopify apps a store uses** (Klaviyo, Yotpo, Recharge, Attentive…), or find stores *without* a certain kind of app
- you need to tell **which e-commerce platform** a website runs on (Shopify, WooCommerce, BigCommerce, Magento…)
- you want to qualify **e-commerce store leads** by product count, price range, country or currency before reaching out
- an AI agent or automation needs structured store data from a domain list

It enriches domains you already have; it doesn't search the web for stores.

### For AI agents

- **Run** `signalloom/ecommerce-store-leads` (Apify MCP server `call-actor`, or the Apify API) with input like `{"websites": ["goodr.com", "ooni.com"]}`
- **Returns** one JSON row per site in the default dataset: `platform`, `storeName`, `country`, `currency`, `productCount`, `priceMin`/`priceMedian`/`priceMax`, `technologies` (apps by category), `emails` (company role addresses), `phones`, `socials`, `status`, `termsCheck`. Rows whose `status` starts with "skipped:" (store terms, or the store owner opted out) are free and carry only the domain and the terms check. `termsCheck: "default-template"` means the only such wording is Shopify's unmodified default template line; those stores are read normally.
- **Typical cost:** 2 stores ≈ **$0.01** ($0.005 per store). Failed, blocked and non-store sites are free.

### Who is it for?

- **Shopify and WooCommerce app makers and agencies**: find stores that *don't* use your category of app yet (e.g. no reviews app), or that use a competitor
- **Suppliers, wholesalers and 3PLs**: qualify stores by catalog size, price point and country before you reach out
- **Lead-gen and sales teams**: enrich a CSV of domains with platform, size and contacts
- **Investors and market researchers**: compare catalogs, pricing and tooling across a list of brands

### How to use it

1. Paste websites into **Websites**, one per line: `goodr.com`, `https://www.gymshark.com`, or any page URL.
2. Optional filters: **Only these platforms** (e.g. Shopify) and **Minimum products**. **Only online stores** is on by default; turn it off to also enrich non-store websites (charged like stores).
3. Click **Start**, then export to CSV, Excel or JSON, or send to Google Sheets, HubSpot or a CRM with Make, Zapier or n8n.

**Recipe: find Shopify stores without a reviews app.** Set Only these platforms to Shopify and Minimum products to 50. Then filter the results where `technologies` has no "Reviews" category. That's a ready list of prospects for a reviews app or agency.

**Recipe: qualify supplier leads.** Set Minimum products to 200, export, and sort by `priceMedian` and `country`.

### Output

One record per website (real example, shortened):

```json
{
  "domain": "ooni.com",
  "url": "https://ooni.com",
  "status": "OK",
  "platform": "shopify",
  "storeName": "Ooni USA",
  "termsCheck": "ok",
  "termsUrl": "https://ooni.com/pages/terms-conditions",
  "city": "Camden, Kent",
  "region": "Delaware",
  "country": "US",
  "currency": "USD",
  "shipsToCountries": ["US"],
  "myshopifyDomain": "uuni.myshopify.com",
  "shopPayInstallments": true,
  "productCount": 123,
  "productCountIsExact": true,
  "priceMin": 10,
  "priceMedian": 95,
  "priceMax": 1837.3,
  "onSaleShare": 0.37,
  "soldOutShare": 0.02,
  "productsAddedLast30Days": 2,
  "newestProductAt": "2026-11-05T17:03:13.000Z",
  "topCategories": [{ "name": "Accessory", "count": 66 }, { "name": "Bundle", "count": 25 }],
  "technologies": [
    { "name": "Yotpo", "category": "Reviews" },
    { "name": "Recharge", "category": "Subscriptions" },
    { "name": "Klarna", "category": "Buy now pay later" }
  ],
  "emails": ["support@ooni.com", "press@ooni.com", "marketing@ooni.com", "privacy@ooni.com"],
  "socials": {
    "instagram": "https://www.instagram.com/oonihq",
    "tiktok": "https://www.tiktok.com/@oonihq",
    "facebook": "https://www.facebook.com/OoniHQ",
    "youtube": "https://www.youtube.com/channel/UCYzfI-z1fyo2rVbpkDmu7Fg"
  }
}
```

Records also include `description`, `language`, `priceAvg`, `collectionsCount`, `topVendors`, `catalogSampleSize`, `contactPageUrl` and the store terms check (`termsCheck`, `termsUrl`, `termsNote`). Fields that don't apply to a platform are `null`.

A store whose terms forbid automated access gets one free row instead (real example):

```json
{
  "domain": "allbirds.com",
  "url": "https://www.allbirds.com",
  "status": "skipped: store terms forbid automated access",
  "platform": "shopify",
  "storeName": "Allbirds",
  "termsCheck": "forbids",
  "termsUrl": "https://www.allbirds.com/policies/terms-of-service",
  "termsClause": "systems; Access any of the features of the Platform through any automated means or with any automated features or devices (including use of scri"
}
```

A Shopify store whose terms only contain Shopify's unmodified default template line is read normally, and the line is shown (real example, shortened):

```json
{
  "domain": "knix.com",
  "url": "https://knix.com",
  "status": "OK",
  "platform": "shopify",
  "storeName": "Knix",
  "country": "US",
  "currency": "USD",
  "productCount": 708,
  "priceMin": 7.5,
  "priceMedian": 42,
  "priceMax": 193,
  "emails": ["info@knix.com", "press@knix.com", "support@knix.mail.kustomerapp.com"],
  "termsCheck": "default-template",
  "termsUrl": "https://knix.com/policies/terms-of-service",
  "termsClause": "(i) to spam, phish, pharm, pretext, spider, crawl, or scrape",
  "termsNote": "the only wording about crawling or scraping is Shopify's unmodified default terms-template line (Prohibited Uses section), treated as boilerplate; proceeding"
}
```

The run summary (key-value store → `OUTPUT`) lists every site that wasn't output and why (unreachable, blocked, not a store, filtered out, skipped because of its terms).

### Data handling and privacy

- **Company mailboxes only.** Emails are limited to role addresses such as info@, support@, sales@ or the brand's own name (colourpop@colourpop.com). Addresses that look like a person's (jane@, jane.doe@) are **not output**, because many small stores are run by one person and a named email is personal data. `emailsSuppressed` shows how many were held back.
- **Public sources only.** Data comes from each store's own public pages and public product data. Each site's robots.txt is respected.
- **Store terms respected.** Before any catalog or contact page is read, the store's terms page is checked. If the store's own terms forbid scraping, crawling or automated access, the store is skipped and not charged (see Limitations).
- **Facts only.** Store names, product counts, prices, availability, apps and public business contact details. No customer reviews, no product descriptions, no images (the site's own short meta description is capped at 200 characters).
- **Opt-out.** Store owners can ask to be excluded; we honour it immediately. Open an issue on this Actor's **Issues** tab with the domain. Listed sites (and their subdomains) are skipped before any request, with `status: "skipped: owner opt-out"`, and never charged.
- Use the results lawfully: follow the anti-spam and privacy laws that apply to you (for example CAN-SPAM, GDPR, CASL) when contacting businesses.

### Pricing

- **$5 per 1,000 stores** output ($0.005 each)
- Sites that fail, are blocked, are filtered out, are skipped because of their terms, or opted out: **free**

### Limitations

- This Actor **enriches websites you provide**; it doesn't search the web for stores. Start from your own list, a directory export, or another Actor's results.
- Price stats and "added in the last 30 days" come from a sample of up to 250 products (Shopify) or 100 (WooCommerce); `catalogSampleSize` shows how many. When every sampled product is new, `productsAddedLast30DaysIsMinimum` is `true`. The product count itself is exact where `productCountIsExact` is `true`.
- Contacts are only what the site publishes. Many stores use a contact form instead of an email address. Apps are detected from the homepage, so tools that only load on product or checkout pages may be missed.
- Some sites block automated visits. They're reported as `BLOCKED` in the run summary and not charged.
- The Actor respects each site's **robots.txt**. Sites that ask crawlers not to visit are skipped (`ROBOTS_DISALLOWED`, not charged), and pages or product endpoints a site disallows aren't fetched. That site's record then has no catalog stats.
- **Store terms.** For Shopify, WooCommerce and unrecognised sites, the Actor finds the store's terms page (the link on its homepage, or the standard `/policies/terms-of-service` and `/terms-and-conditions/` paths; at most 2 extra requests per site) and scans it for wording against scraping, crawling, spiders, bots or automated access. If the store has **its own** such wording, it is output as `status: "skipped: store terms forbid automated access"` with `termsCheck: "forbids"`, the quoted clause (up to 160 characters) and the terms URL, and is **not charged**. The scan is deliberately strict, so some stores with a narrow clause (for example about loyalty points) are skipped too. **Shopify's default template:** many Shopify stores use Shopify's standard terms template, whose "Prohibited Uses" section includes the line "(i) to spam, phish, pharm, pretext, spider, crawl, or scrape". When that section is unmodified and is the only such wording on the page, the store is read normally with `termsCheck: "default-template"`, and the line is quoted in `termsClause` so you can see it. The section is compared word by word with Shopify's standard text (case, spacing and punctuation ignored); if the store edited that section or added any other wording about bots, scripts, data mining or automated access, it counts as the store's own wording and is skipped. The scan reads both the visible page and the terms text that some stores load with JavaScript (inline page data). If a **Shopify** store's terms page exists but can't be read (`unreadable`: blocked, empty, or not in English), the store is skipped as well, with `status: "skipped: store terms could not be read"`, and is **not charged**. On other platforms an unreadable terms page means the Actor proceeds. If a store has no terms page at all (`not-found`), the Actor proceeds. `termsNote` always says which case applied. Sites on other platforms (BigCommerce, Magento…) aren't checked (`termsCheck: null`).

### FAQ

**How do I find out which apps a Shopify store uses?**

Add the store's domain to **Websites** and run. The `technologies` field lists the detected apps and tools with their category (email and SMS, reviews, subscriptions, loyalty, support chat, pixels…). It works for a whole list of domains in one run.

**How can I check whether a website is built on Shopify?**

Run the domain: `platform` returns `shopify`, `woocommerce`, `bigcommerce`, `magento` and 8 more. Sites that aren't stores are skipped for free (or turn off **Only online stores** to enrich them anyway).

**How do I get contact emails for a list of Shopify stores?**

Paste the domains; each row has `emails` (company role mailboxes such as support@ or hello@), `phones` and social profiles. Person-looking emails are never output.

**How many products does a Shopify store have?**

`productCount` is the catalog size (exact when `productCountIsExact` is true), with price range, products added in the last 30 days and top brands.

**Why was a store skipped with "store terms forbid automated access"?**

Its terms of service contain its own wording against scraping, crawling or automated access. The row shows the clause and the terms URL. It is free, and no catalog or contact data was read from that store.

**What does `termsCheck: "default-template"` mean?**

The store's terms are Shopify's standard template, and the only line about crawling is the template's "(i) to spam, phish, pharm, pretext, spider, crawl, or scrape" in its unmodified "Prohibited Uses" section. That line is boilerplate the store didn't write or change, so the store is read normally. The line is quoted in `termsClause`. If the store edited that section or added its own anti-scraping wording, it is skipped instead.

**Can a store owner have their store excluded?**

Yes. Store owners can ask to be excluded; we honour it immediately. Open an issue on this Actor's **Issues** tab with the domain. The store is then skipped before any request (`status: "skipped: owner opt-out"`), for every user.

**Why was a Shopify store skipped with "store terms could not be read"?**

Its terms page exists, but the Actor could not read any terms text on it (blocked, empty, or not in English). Because the terms might forbid automated access, the store is skipped the same way, for free. `termsNote` says what happened.

**Does it find new stores for me?**

No. It enriches domains you already have (from a CSV, a CRM or another Actor). Schedule it monthly on the same list to see catalog, app and price changes.

### Feedback

Want another platform, app or field detected? Open an issue on the **Issues** tab.

# Actor input Schema

## `websites` (type: `array`):

Store websites or domains, one per line: "goodr.com", "https://www.gymshark.com". Any page URL works; the site's homepage is used. Stores whose own terms forbid automated access, or whose owners opted out, are skipped for free. Example: \["goodr.com", "https://www.gymshark.com"].

## `onlyPlatforms` (type: `array`):

Output only stores on these platforms. Sites on other platforms are skipped and not charged. Leave empty for all. Example: \["shopify"].

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

Output only stores with at least this many products (Shopify and WooCommerce, where the count is known). 0 = no minimum. Example: 50.

## `onlyStores` (type: `boolean`):

Skip sites that aren't online stores (e.g. plain WordPress or company sites). Skipped sites aren't charged. Turn off to also get contacts and tech for non-store websites (charged like stores).

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

Emails, phone numbers and social profiles from the homepage and contact pages. Turn off for platform, apps and catalog data only.

## `maxContactPages` (type: `integer`):

How many contact, imprint or about pages to read per site, in addition to the homepage. Example: 2.

## `includeCatalogStats` (type: `boolean`):

Product count, price range, share on sale or sold out, products added in the last 30 days, top brands and categories (Shopify and WooCommerce).

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

How many sites to check at the same time.

## Actor input object example

```json
{
  "websites": [
    "goodr.com",
    "ooni.com",
    "www.nutribullet.com"
  ],
  "minProducts": 0,
  "onlyStores": true,
  "includeContacts": true,
  "maxContactPages": 2,
  "includeCatalogStats": true,
  "maxConcurrency": 5
}
```

# Actor output Schema

## `results` (type: `string`):

No description

## `runSummary` (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 = {
    "websites": [
        "goodr.com",
        "ooni.com",
        "www.nutribullet.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("signalloom/ecommerce-store-leads").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 = { "websites": [
        "goodr.com",
        "ooni.com",
        "www.nutribullet.com",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("signalloom/ecommerce-store-leads").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 '{
  "websites": [
    "goodr.com",
    "ooni.com",
    "www.nutribullet.com"
  ]
}' |
apify call signalloom/ecommerce-store-leads --silent --output-dataset

```

## MCP server setup

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

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/nOkbpNyftAqhAJIaH/builds/1pUabNW8k5hxU6EcT/openapi.json
