Shopify Store Leads - Emails, Size & Activity avatar

Shopify Store Leads - Emails, Size & Activity

Pricing

from $5.00 / 1,000 qualified leads

Go to Apify Store
Shopify Store Leads - Emails, Size & Activity

Shopify Store Leads - Emails, Size & Activity

Turn a domain list into qualified Shopify leads. Returns contact email, phone and socials, plus what decides whether a store is worth approaching: catalogue size, price band, discount depth, stock health, and how recently it launched anything. One row per store; filtered stores are free.

Pricing

from $5.00 / 1,000 qualified leads

Rating

0.0

(0)

Developer

DONGMIN KIM

DONGMIN KIM

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

4 hours ago

Last modified

Share

Shopify Store Leads — Contact Details Plus the Numbers That Qualify a Store

Shopify Store Leads — Contact Details Plus the Numbers That Qualify a Store

Paste a list of domains. Get back one row per store: who to email, and whether the store is worth emailing.

Most contact scrapers stop at the address. The problem with a list of ten thousand addresses is that most of them belong to shops with eleven products, no sale in two years, and nothing to spend. This one reads the storefront the same way a buyer would — how big is the catalogue, what does it charge, how hard is it discounting, how much is out of stock, and when did it last launch anything — and ships those as fields you can filter on before you write a single email.

Works on any Shopify store. No API key, no login, no store password.

What it does well

  • Contact details — email, phone and Instagram/Facebook/TikTok/X/YouTube/Pinterest/LinkedIn accounts, from the home, contact and about pages.
  • Qualification, not just contacts — catalogue size, median price, discount depth, out-of-stock rate, and new products in the last 30 and 90 days.
  • Two plain-language bandssizeBand and activityBand, so a ten-thousand-row list sorts itself without you writing rules.
  • Filters run before billing — require an email, a minimum catalogue, or a launch in the last N days, and the stores you drop cost nothing.
  • Confirms Shopify first — a mixed prospect list does not waste requests on WordPress sites.

Input

{
"storeUrls": ["allbirds.com", "gymshark.com", "prospect-three.com"],
"requireEmail": true,
"minProducts": 25,
"activeWithinDays": 90
}

Every option

The same wording you see in the Apify console, with the JSON key for API and MCP callers.

OptionWhat it doesDefault
Store domainsstoreUrls (required)Bare domains or any URL on the store. Paste a whole prospect list; each domain becomes one row.
Find contact detailsincludeContactsRead the store's home, contact and about pages for emails, phone numbers and social accounts. Stops as soon as it has both an email and a social account.true
Max products scanned per storemaxProductsScannedHow much of the catalogue to read before judging size and activity. None of it is written as rows — this is a request budget, not a row budget.500
Only keep stores with an emailrequireEmailDrop stores where no contact email could be found. Dropped stores are not billed.false
Minimum productsminProductsDrop stores smaller than this. 0 disables.0
Launched something within (days)activeWithinDaysDrop stores whose newest product is older than this. 0 disables.0
Skip non-Shopify domainsskipNonShopifyCheck each domain is Shopify before qualifying it.true
ConcurrencyconcurrencyStores processed in parallel.3
ProxyproxyConfigurationLeave the default. The actor starts on cheap datacenter proxies and escalates only for stores that throttle.{"useApifyProxy":true}

Output

One row per qualified store.

{
"storeDomain": "allbirds.com",
"url": "https://allbirds.com",
"isShopify": true,
"themeName": "DNAM Theme",
"currency": "USD",
"productCount": 291,
"sizeBand": "medium",
"activityBand": "active",
"vendorCount": 1,
"productTypeCount": 3,
"minPrice": 3,
"maxPrice": 160,
"medianPrice": 68,
"onSaleCount": 155,
"onSalePercent": 53,
"averageDiscountPercent": 50,
"outOfStockCount": 43,
"outOfStockPercent": 15,
"newProductsLast30Days": 12,
"newProductsLast90Days": 34,
"daysSinceNewestProduct": 3.2,
"catalogueAgeDays": 1840,
"email": "help@allbirds.com",
"emails": ["help@allbirds.com", "press@allbirds.com"],
"phone": "+18885634387",
"instagram": "allbirds",
"facebook": "weareallbirds",
"tiktok": "allbirds",
"contactPageUrl": "https://allbirds.com/pages/contact",
"scrapedAt": "2026-08-20T09:14:02.113Z"
}

Every field

You are billed per lead delivered, so here is everything a row can contain. A field is absent when the store does not publish it.

Who to contact

FieldWhat it is
emailThe best single address found. Addresses on the store's own domain rank above a free-mail one, and role addresses (info@, hello@, sales@) above personal ones. no-reply@ is ranked last, never first.
emailsEvery address found, in that same order.
emailSourcemailto when the first address came from a link somebody published to be written to, text when it only matched in the page. A text address is worth a second look.
phone / phonesNumbers from tel: links, digits only. Anything under seven digits is treated as an extension, not a number.
instagram, facebook, tiktok, twitter, youtube, pinterest, linkedinThe store's handle on each network, when it links one. Share buttons and network furniture are excluded.
contactPageUrlWhich page the details came from, when it was not the homepage.

Whether to contact them

FieldWhat it is
sizeBandempty · micro (<25) · small (<150) · medium (<750) · large. The one field to sort a big list by. On a capped scan this is the floor — a scan that stopped at 250 proves at least medium and cannot rule out large.
activityBandactive (launched within 30 days) · slowing (90) · dormant (a year) · stale · unknown. The strongest single signal that a shop is still trading.
productCountProducts seen.
productCountCappedtrue when the catalogue was larger than Max products scanned — so productCount is a floor, not a count.
launchCountsAreFloorstrue in the same case. /products.json returns newest first, so a capped scan sees a recent slice: the launch counts below are minimums. catalogueAgeDays is then withheld entirely rather than guessed, because the oldest product in a recent slice is not the oldest product in the shop.
daysSinceNewestProductDays since the newest product record was created.
daysSinceLastPublishedDays since anything was last made visible on the storefront. A bulk republish moves this and not the one above.
daysSinceAnyActivityThe more recent of those two — the last sign somebody was at the wheel. This is what activityBand reads. All three are safe on a capped scan: the true values can only be smaller.
newProductsLast30Days / newProductsLast90DaysLaunch cadence, which is what separates a growing shop from a parked one. Counted from when a product was created, not when it was published — see the note below.
catalogueAgeDaysAge of the oldest product — roughly how long the shop has been trading. Absent when the scan was capped.
vendorCount / productTypeCountWhether it is a single brand or a marketplace-style reseller.
minPrice / maxPrice / medianPricePrice band in the store's own currency. Median, not average, because one $4,000 outlier should not move it.
currencyThe store's active currency.
onSaleCount / onSalePercentHow much of the catalogue is marked down.
averageDiscountPercentHow deep those markdowns go. Heavy, permanent discounting is a margin signal.
outOfStockCount / outOfStockPercentStock health. A high rate can mean demand, or it can mean a supply problem — read it with activityBand.

Housekeeping

FieldWhat it is
storeDomain / urlThe store, for joining back to your list.
isShopifyAlways true unless you turned off Skip non-Shopify domains.
themeNameThe Shopify theme, when the storefront exposes it.
scrapedAtWhen this run read the store.

Why there are three date fields instead of one

Shopify puts two timestamps on a product, and neither one means "launched". They fail in opposite directions, so this Actor reports both and derives the band from the pair.

Measured on real stores while building this:

  • published_at is a visibility stamp a bulk republish resets. gymshark.com: 500 products created between 2021 and 2026, every one published inside the same three weeks. allbirds.com: the gap between creation and publication has a median of 617 days, with the whole catalogue published within days of each other. Read alone, one admin action looks like explosive growth.
  • created_at misses shops that refresh instead of adding. allbirds.com has not created a new product record in 196 days and is obviously still trading — it updates existing records with new colourways. Read alone, a healthy shop looks abandoned.

So newProductsLast30Days counts creations, because a republish is not a launch. And activityBand reads daysSinceAnyActivity — the more recent of the two — because either one means somebody is still administering the store. A shop where both are stale is the one that is actually gone.

The catalogue Actor exposes both dates raw, and the new-arrivals monitor keys on published_at, because "newly visible to customers" is genuinely the right question there.

Who this is for

  • Agencies and freelancers selling design, CRO, email or ads to Shopify merchants — sizeBand and activityBand tell you who can afford you and who is still trading.
  • Suppliers and 3PLs looking for shops of the right size in the right price band.
  • App and SaaS founders on Shopify, building a launch list that is not mostly dead stores.
  • Anyone who bought a list and needs to know how much of it is real before working it.

What it does not do

It finds addresses a store publishes. It does not guess firstname.lastname@ patterns, buy from a contact database, or verify that an address accepts mail. Everything in a row came off the storefront on the day of the run.

Common uses

  • Qualify a bought list — run 10,000 domains, keep the ones with an email and a launch in the last 90 days, discard the rest for free.
  • Territory sizing — median price and catalogue size across a segment, before pitching it.
  • Trigger-based outreach — schedule it and approach stores whose activityBand just moved to active.
  • Competitive mapping — the same numbers for every shop in your category, in one table.

Pricing

Pay per lead delivered — $5.00 per 1,000. Stores removed by your filters, stores that are not Shopify, and stores that fail cost nothing, which is what makes it safe to point at a list you have not cleaned.

Starting a run costs $0.00001 — the platform's $0.00001 minimum, charged once per GB of memory, and these Actors run on 512 MB.

Scanning is the expensive part and you are not charged for it: a store read across nine requests that then fails your email filter costs you nothing at all.

Other Actors in this family

Same engines, same billing, no account or API key on any of them.

YouTube & video

Search demand

E-commerce

Hiring

Run it from code

Nothing here needs a login to the source, only your Apify token.

HTTP — start a run and wait for the rows:

curl -X POST "https://api.apify.com/v2/acts/gganbukim~shopify-store-leads/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
-H "content-type: application/json" \
-d @input.json

JavaScript

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('gganbukim/shopify-store-leads').call(input);
const { items } = await client.dataset(run.defaultDatasetId).listItems();

Python

from apify_client import ApifyClient
client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("gganbukim/shopify-store-leads").call(run_input=input)
items = client.dataset(run["defaultDatasetId"]).list_items().items

Scheduled or event-driven — attach a schedule to run it on a cron, or a webhook to push each finished run into your own endpoint. It also connects through Apify's Zapier, Make, n8n and LangChain integrations, and is reachable from an MCP server if you are driving it from an agent.

Standby / API mode — the run above is synchronous: one call in, rows out, no polling. That is the shape to use if you are calling this per request rather than in a batch.

Errors, limits and what you are charged for

  • You pay for delivered rows only. A row your filters removed, a page that failed, a retry — none of it is billed. Starting a run costs $0.00001: the platform minimum, charged once per gigabyte, and this Actor runs on 512 MB.
  • A run that delivers nothing still costs the start fee and nothing else. If the input resolved to zero items, the run fails loudly with the reason rather than finishing green on an empty dataset.
  • Blocking is handled by changing address, not by waiting. The Actor starts on cheap datacenter proxies and moves up only after a tier has actually been refused several times in a row, then drops back down once the cheap tier answers cleanly again. You are not paying for residential bandwidth that was never needed.
  • Rate limits belong to the source, not to this Actor. Very large inputs are worked through in batches; the run reports how many items succeeded, were filtered, and failed, so a partial result is never presented as a complete one.
  • Dataset retention follows your Apify plan. Export what you need, or push it out with a webhook, if you want it past that window.

This Actor reads pages and public endpoints that anyone can open in a browser without an account. It does not log in, does not defeat a paywall, and does not touch anything behind authentication.

Scraping public data is broadly lawful in the US and the EU, and courts have repeatedly said so — but "public" is not the same as "unrestricted", and what you may then do with the data is a separate question from whether you may collect it. Personal data pulls in the GDPR and similar regimes whatever the source, so if your rows contain people, you need a lawful basis for keeping them.

Apify publishes a fuller treatment in Is web scraping legal? and an ethical scraping guide. None of this is legal advice; if the use is commercial and the data is personal, ask someone qualified.

Something wrong, or missing?

Open an issue on the Actor's Issues tab — it goes straight to the developer and is the fastest route. Include the run ID; it carries the input and the log, which is usually enough to reproduce the problem without another round trip.

Sources change without warning, and a field that quietly goes null is worth reporting even if the run succeeded. A broken parser looks exactly like a quiet day in the data until someone says so.

FAQ

Is it legal to collect business contact details this way? This reads pages the store publishes publicly, with no login and nothing bypassed — a shop that puts hello@ in its footer is asking to be contacted there. Business contact details are treated differently from personal data in most regimes, but how you use them is regulated: GDPR, CAN-SPAM, PECR and their equivalents govern marketing email regardless of how the address was obtained. Each store's own terms are a separate contract question. Not legal advice.

Will I get blocked? Rarely. This reads public storefront pages and the same /products.json endpoint Shopify serves on every shop, on cheap datacenter proxies, escalating only for the minority of stores behind a WAF.

How much does 1,000 leads cost? $5.00, plus $0.00002 for the run. Filtered and failed stores are free, so a 10,000-domain list where 2,000 pass your filters costs $10.

Why one row per store instead of per contact? Because the decision you are making is per store. A shop with four published addresses is not four leads.

Can I export the results to Excel or Google Sheets? Yes. Every run's dataset downloads as CSV, Excel, JSON, XML or RSS from the Storage tab, or straight from the API if you want a live link a spreadsheet can pull.

Can I connect it to Zapier, Make or n8n? Yes — Apify publishes integrations for all three, plus webhooks that fire when a run finishes. A common setup is a schedule here and a webhook into your CRM.

Do I need to write code? No. Fill the form in the console and press Start. If you do want code, the Apify client libraries for Python and JavaScript call this the same way, and it is available over MCP so an AI agent can call it directly.

Can I run it on a schedule? Yes, via Apify Schedules, webhooks, or the API.