Shopify Store Scraper: Emails, Phones & Social Links avatar

Shopify Store Scraper: Emails, Phones & Social Links

Pricing

from $3.99 / 1,000 results

Go to Apify Store
Shopify Store Scraper: Emails, Phones & Social Links

Shopify Store Scraper: Emails, Phones & Social Links

Shopify Store Scraper extracts store contact and social data, including emails, phone numbers, social media links, store details, product catalogs, and website URLs. Ideal for e-commerce lead generation, competitor research, market analysis, and prospecting.

Pricing

from $3.99 / 1,000 results

Rating

0.0

(0)

Developer

Scrapio

Scrapio

Maintained by Community

Actor stats

0

Bookmarked

10

Total users

0

Monthly active users

3 days ago

Last modified

Share

Shopify Store Scraper: Emails, Phones & Social Links turns a list of storefront URLs into typed JSON merchant profiles — the published email address, phone number, social profile links, and policy inventory for each store, plus the full store identity and catalogue block. Unlike scraping frameworks that return raw HTML, it returns structured JSON ready for a CRM, a database, or an LLM context window without any parsing. Nothing is guessed: when a store doesn't publish a value, the field comes back empty rather than filled with a pattern. This guide covers every input and output field and how outbound, monitoring, and research teams actually run it.


It reads the public pages of a Shopify storefront — the homepage, up to a configurable number of contact pages, and the store's policy pages — and assembles one merchant profile row per store. No Shopify account, Partner API key, or storefront login is required; every page it reads is one a normal visitor can load. The row carries three things at once: the contact points the merchant actually publishes (email, phone, social links), a policy inventory (which of Shopify's six policy pages exist and what they say), and the full store identity and catalogue metadata (name, location, currency, theme, product and collection counts, price range).

  • ✉️ Email discovery — reads the homepage, contact pages, policy pages, and any Organization JSON-LD, and picks the customer-facing address over a legal or press mailbox
  • ☎️ Phone discovery — prioritizes a published tel: link over a number found only in prose
  • 🔗 Social profile resolution — matches links against the store's own domain token so an app's or agency's profile doesn't get picked over the merchant's own
  • 📜 Policy inventory — URL, body presence, and body length for each of the six Shopify policy types, plus a refund-window day count when the policy states one
  • 🏬 Merchant identitymyshopifyDomain, shop id, HQ city/region, money format, ships-to countries, accepted card brands, Shop Pay Installments flag
  • 📦 Catalogue snapshot — product/collection counts, price range, vendors, categories, and a top-10 recent-products sample
  • 🚦 Honest status reporting — an open, password_protected, or no_public_catalogue store are three distinct states, not one blended "success"

⚡ Features & Capabilities

The Actor's job splits into three layers: contact discovery, policy inventory, and store identity/catalogue — all three arrive in the same row.

Core features

  • Evidence-ranked contact picking. Every candidate email and phone is collected first, then ranked — an address on the store's own domain outranks one on a third-party domain, and a customer-facing local part (hello@, support@, info@) outranks a legal or press mailbox. The winning value lands in primaryEmail / primaryPhone, with emailSource / phoneSource telling you where it came from (homepage, contact_page, policy_page, structured_data, tel_link, contact_page_text, policy_text).
  • Placeholder and noise filtering. Theme-default addresses (your@email.com, info@example.com), tracking-vendor domains, and file extensions masquerading as TLDs are filtered out of emails before ranking — a naive regex extractor emits these, this one doesn't.
  • Social profile scoring, not first-match. extract_socials gathers every candidate URL per platform across a page, then scores each by whether the handle matches the store's own brand token and how many times the store links it, so a one-off mention doesn't outrank the merchant's real footer link.
  • Configurable contact-page ladder. contactPagePaths and maxContactPages control exactly which paths are tried and how many; contactPagesTried and contactPagesRead report back what actually happened, so the count you set is verifiable in the output.
  • Policy body verification, not just existence. Each policy entry in policies carries hasBody and bodyChars — a policy page that resolves but ships no text is recorded as such, not silently skipped.
  • JSON-LD structured-data fallback. When a store publishes Organization/Store schema markup, its email, telephone, logo, and sameAs links feed into the same candidate pool as page-scraped values.
  • Backward-compatible catalogue block. total_products, total_variants, collections, price_min/price_avg/price_max, vendors, categories, and top_products ride along on every row, sourced from the store's public /products.json and /collections.json endpoints.

This Actor covers store-level contact, policy, and identity data — one row per storefront. For deeper per-product fields (descriptions, specs, tags), use Shopify Products Scraper — Descriptions, Specs & Tags. For market, currency, and shipping-focused merchant metadata, use Shopify Merchant Scraper: Market Currency & Shipping Data. For related-product and price-comparison data, use Shopify Scraper: Related Products & Price Comparison.


Why do developers and data teams scrape Shopify stores?

🏢 Outbound and lead generation teams

You already have a list of Shopify domains — from a lead database, a niche directory export, or a prospecting tool — and need the mailbox and social handles to actually reach the brand. Feed merchantUrls in, read back primaryEmail, emailSource, primaryPhone, socialProfiles, and merchantName, and the row lands directly in a CRM import or an outbound sequencing tool. emailSource matters here: a contact_page hit is a mailbox someone checks; a policy_page hit is often a legal address, worth routing differently.

📊 AI training data and RAG indexing

merchantDescription, refundPolicyExcerpt, and the individual policies[].bodyChars fields are the highest-information text on a store: they describe what the merchant sells and what it promises, in the merchant's own words. For RAG enrichment, index merchantDescription and refundPolicyExcerpt against the store domain so an agent answering "does this store accept returns" can cite the actual policy text and its window (returnWindowDays). For training data, merchantCity, merchantProvince, acceptedCardBrands, and shipsToCountries return as consistently typed primitives across every store in a run, with no HTML to strip first.

📱 Competitive and market intelligence

Track price_min, price_avg, price_max, total_products, products_on_sale, and discount_pct_avg across a watch-list of competitor stores on a schedule, and the delta on any of those fields is the signal — a widening discount_pct_avg says a competitor moved into a sale cycle before their own marketing announces it.

🔬 Research and academic use

policies, returnWindowDays, acceptedCardBrands, and shipsToCountryCount build a structured, publicly sourced dataset on DTC merchant practices — return-policy generosity, international shipping reach, payment-method adoption — without touching anything behind a login. Public storefront data only; no account credentials are collected or required.

🎥 Product and SaaS development

Build a merchant-enrichment API, a Shopify store directory, or a compliance-monitoring tool on top of storefrontStatus, hasReturnPolicy, policyCount, and the contact fields — a due-diligence tool, for instance, can flag any store where hasReturnPolicy is false or primaryEmail is empty before a partnership proceeds.


🍚 Input Parameters

All twelve parameters, copied from .actor/input_schema.json. None is required — running with merchantUrls empty pushes a single no_input status row rather than failing.

ParameterRequiredTypeConstraintsDescription
merchantUrlsNoarray (stringList)prefill: ["https://www.allbirds.com"]One entry per Shopify store, for example https://www.allbirds.com or rothys.com.
startUrlsNoarray (stringList)Kept so older input files keep working. Used only when merchantUrls is empty.
findEmailsNobooleandefault: trueRead merchant email addresses from the store's own pages. Measured on two live runs over the same 12-store list: 9–10 of 12 stores (75–83%). Turn this off and the email columns come back empty.
findPhonesNobooleandefault: trueRead merchant phone numbers from the store's own pages. Measured on two live runs over the same 12-store list: 8 of 12 stores (67%) in both. Stores with no published number return empty, never a guessed number.
findSocialProfilesNobooleandefault: trueInstagram, Facebook, TikTok, YouTube, Pinterest, X/Twitter and LinkedIn profile links. Measured on two live runs over the same 12-store list: 12 of 12 stores (100%) in both.
contactPagePathsNoarray (stringList)prefill: ["/pages/contact", "/contact", "/pages/contact-us", "/policies/contact-information"]Store paths checked for contact details, in order. The row reports back which of them answered, in contactPagesRead.
maxContactPagesNointegermin 0, max 10, prefill 4How many of the paths listed above to try per store, counted from the top of that list. Enter 2 and only the first two are tried; enter 0 and none are, so contact details come from the storefront's own front page and its policy pages only. The number tried is reported back in contactPagesTried, and the ones that answered in contactPagesRead. Never more than the number of paths you listed.
readPoliciesNobooleandefault: trueBuilds a policy inventory with the page URL, whether it has body text, and how long that text is.
policyTypesNoarray (select, multi)enum: refund-policy, privacy-policy, terms-of-service, shipping-policy, contact-information, subscription-policy; prefill: all sixLeave all selected to inventory everything the store publishes.
includePolicyExcerptNobooleandefault: trueAdds the opening text of the refund policy to the row. Empty when the store publishes the page without body text.
excerptCharsNointegermin 0, max 5000, prefill 600Hard limit on how many characters of the refund policy are kept in refundPolicyExcerpt. Enter 200 and the excerpt is never longer than 200 characters; enter 0 and no excerpt is returned. Shorter policies are returned in full.
proxyConfigurationNoobject (proxy)prefill: {"useApifyProxy": false}Leave as-is unless you need the run routed differently.

JSON input example

{
"merchantUrls": [
"https://www.allbirds.com",
"rothys.com",
"https://partakefoods.myshopify.com"
],
"findEmails": true,
"findPhones": true,
"findSocialProfiles": true,
"contactPagePaths": ["/pages/contact", "/contact", "/pages/contact-us", "/policies/contact-information"],
"maxContactPages": 4,
"readPolicies": true,
"policyTypes": ["refund-policy", "privacy-policy", "shipping-policy", "contact-information"],
"includePolicyExcerpt": true,
"excerptChars": 600,
"proxyConfiguration": { "useApifyProxy": true }
}

Supported URL types and input formats

merchantUrls accepts three input shapes, normalized internally before the run starts (https:// is prepended if missing, and any path/query is stripped down to scheme + host):

  • Full URLhttps://www.allbirds.com
  • Bare domainrothys.com
  • .myshopify.com handlepartakefoods.myshopify.com

startUrls accepts the same three shapes and is read only when merchantUrls is empty, purely for input files written against an older schema version. Duplicate URLs (after normalization) are de-duplicated before the run starts.


📦 Output Format

Every run produces typed, normalized JSON — one row per reachable store, pushed the moment that store finishes, plus an uncharged status row for any store that couldn't be read or for a run started with no input at all. Field names stay stable across runs; there is no CSV/XML transform inside the Actor, but the Apify platform's own dataset export (CSV, JSON, XML, Excel, RSS) works on the resulting dataset regardless.

Uncharged rows. Only successful store profiles are billed, via the row_result charged event. A store that returns no usable public data is pushed as {"type": "status", "storefrontStatus": "unreachable", ...}, and a run started with no store URLs at all pushes {"type": "status", "storefrontStatus": "no_input", ...} — neither is charged. Filter them out of an export with item.type !== "status" (or item.storefrontStatus !== "unreachable" && item.storefrontStatus !== "no_input").

Output for a store profile row

{
"type": "store",
"isChild": false,
"store_domain": "www.allbirds.com",
"store_url": "https://www.allbirds.com",
"shop_name": "allbirds.myshopify.com",
"country": "US",
"currency": "USD",
"locale": "en",
"theme_name": "Impulse",
"theme_id": 123456789,
"theme_version": "8.1.0",
"apps_installed": ["Elevar Conversion Tracking"],
"apps_by_category": { "Analytics": ["Elevar Conversion Tracking"], "Other": [] },
"apps_count": 1,
"total_products": 214,
"total_variants": 812,
"collections": ["Men", "Women", "Shoes", "Accessories"],
"collections_count": 4,
"categories": ["Apparel", "Footwear"],
"vendors": ["Allbirds"],
"price_min": 30.0,
"price_avg": 88.45,
"price_max": 150.0,
"products_on_sale": 12,
"discount_pct_avg": 22.5,
"total_variants_available": 690,
"inventory_availability_pct": 85.0,
"estimated_launch_date": "2016-02-03",
"oldest_product_date": "2016-02-03",
"newest_product_date": "2026-07-30",
"products_with_images": 214,
"avg_images_per_product": 4.3,
"products_with_description": 214,
"top_products": [
{ "title": "Men's Tree Runners", "url": "https://www.allbirds.com/products/mens-tree-runners", "price": 98.0 }
],
"api_blocked": false,
"myshopifyDomain": "allbirds.myshopify.com",
"shopId": 12345678,
"merchantName": "Allbirds",
"merchantDescription": "Sustainably made shoes and apparel.",
"merchantCity": "San Francisco",
"merchantProvince": "CA",
"moneyFormat": "${{amount}}",
"shipsToCountries": ["US", "CA"],
"shipsToCountryCount": 2,
"acceptedCardBrands": ["visa", "master", "american_express"],
"offersShopPayInstallments": true,
"organizationLogoUrl": "https://cdn.shopify.com/s/files/allbirds/logo.png",
"organizationSameAs": ["https://www.instagram.com/allbirds", "https://www.facebook.com/allbirds"],
"publishedProductsCount": 214,
"publishedCollectionsCount": 6,
"emails": ["hello@allbirds.com"],
"primaryEmail": "hello@allbirds.com",
"emailSource": "contact_page",
"emailCount": 1,
"phones": ["+1 415-489-1443"],
"primaryPhone": "+1 415-489-1443",
"phoneSource": "tel_link",
"phoneCount": 1,
"socialProfiles": {
"instagram": "https://instagram.com/allbirds",
"facebook": "https://facebook.com/allbirds",
"twitter": null,
"tiktok": "https://tiktok.com/@allbirds",
"youtube": null,
"pinterest": null,
"linkedin": "https://linkedin.com/company/allbirds"
},
"socialPlatformCount": 3,
"policies": [
{ "type": "refund-policy", "url": "https://www.allbirds.com/policies/refund-policy", "hasBody": true, "bodyChars": 1840 }
],
"policyCount": 6,
"hasReturnPolicy": true,
"returnWindowDays": 30,
"refundPolicyExcerpt": "We want you to love your Allbirds. If you're not satisfied, return them within 30 days...",
"contactPageUrl": "https://www.allbirds.com/pages/contact",
"contactPagesRead": ["https://www.allbirds.com/pages/contact"],
"contactPagesTried": 4,
"storefrontStatus": "open",
"storeProfileComplete": true,
"catalogueReadable": true,
"collectionPagesRead": 1,
"productPagesRead": 1,
"scraped_at": "2026-08-15T09:12:47.123456+00:00",
"scrape_duration_sec": 4.87
}

Output for a policy inventory entry

Each item inside policies[] — one per policy type the store actually publishes:

{
"type": "refund-policy",
"url": "https://www.allbirds.com/policies/refund-policy",
"hasBody": true,
"bodyChars": 1840
}

Output for a top-product entry

Each item inside top_products[] — up to the 10 most recently created products found while walking the store's public product feed:

{
"title": "Men's Tree Runners",
"url": "https://www.allbirds.com/products/mens-tree-runners",
"price": 98.0
}

Schema stability and export options

Field names are fixed by the row-building code and do not change when Shopify updates a store's front-end theme — the Actor reads the store's public meta.json, products.json, collections.json, JSON-LD, and page HTML directly, not a theme-specific DOM structure. A field that cannot be honestly read comes back null rather than a fabricated 0 or empty string, so a missing value and a genuinely-zero value never look the same. Results live in the run's default Apify dataset and export as JSON, CSV, XML, Excel, or RSS from the Apify Console or API — there is no separate delivery mechanism beyond the dataset itself.

⚠️ Catalogue read ceiling. The Actor walks the store's public product and collection feeds in pages of 250 items, up to 60 product pages (15,000 products) and 40 collection pages (10,000 collections) per store. For a store with a catalogue larger than that, total_products / total_variants / collections_count and the price/vendor/category aggregates reflect only what was read before the ceiling, and storeProfileComplete is false. This ceiling does not affect the contact, policy, or identity fields, which come from the homepage, contact pages, and policy pages, not the catalogue feed.


🎯 Strategy 1: Real-time lead enrichment pipeline

When a new Shopify domain lands in your pipeline — from a form fill, a directory export, or a prospecting tool — trigger a single-store run with merchantUrls: [domain]. Read primaryEmail, emailSource, primaryPhone, socialProfiles, and merchantName back and append them to the lead record in your CRM or outbound tool. emailSource tells you whether the mailbox came from a contact_page (likely monitored) or a policy_page (often a legal address), which is worth routing to different sequences.

🎯 Strategy 2: Scheduled monitoring and alerting

Build a watch-list of competitor or partner store URLs and run it on an Apify Schedule. Diff each run's dataset against the previous one on price_avg, discount_pct_avg, hasReturnPolicy, returnWindowDays, and storefrontStatus. A store flipping from open to password_protected, or a returnWindowDays value shrinking, is the kind of delta worth an alert — nothing in the Actor itself sends webhooks, so wire the alert logic into whatever consumes the dataset after the run completes.

🎯 Strategy 3: Bulk dataset build

For a large list, split merchantUrls across several runs rather than one giant list — scrape_single_store processes stores one at a time within a run (its own products.json/collections.json fetches run concurrently, but stores in the list do not), so wall-clock time scales with list length per run. Aggregate the resulting datasets with the Apify API or apify_client and export to CSV or a database once every run finishes.

Strategy comparison at a glance

StrategyBest forRun patternOutput format
Real-time enrichmentNew leads arriving one at a timeSingle-store run triggered per eventJSON row appended to CRM
Scheduled monitoringTracking known stores over timeRecurring run on an Apify ScheduleDataset diffed against the prior run's export
Bulk dataset buildLarge one-off research or lead listsList split across multiple runsCombined dataset export to CSV / database

ScraperWhat it extracts
Shopify Products Scraper — Descriptions, Specs & TagsPer-product descriptions, specs, and tags — deeper catalogue detail than this Actor's top_products sample
Shopify Merchant Scraper: Market Currency & Shipping DataMerchant-level market, currency, and shipping-zone metadata
Shopify Scraper: Related Products & Price ComparisonRelated-product and price-comparison data across a store's catalogue
Amazon DE Sellers Leads Scraper By ASIN & Seller IDSeller contact leads on Amazon.de — the cross-platform equivalent of a store-contact lookup
Instagram Post Details Scraper With Comments & RepliesOnce you have a store's Instagram handle from socialProfiles, pull its post-level content
Facebook Reviews Scraper With Date & Keyword FiltersComplementary brand-monitoring once you have a store's Facebook page from socialProfiles

Shopify Store Scraper: Emails, Phones & Social Links works with any language or tool that can make an HTTP request — it runs on the Apify platform and is called through the Apify API or the apify-client SDKs.

Python

from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_API_TOKEN>")
run_input = {
"merchantUrls": [
"https://www.allbirds.com",
"rothys.com",
],
"findEmails": True,
"findPhones": True,
"findSocialProfiles": True,
"readPolicies": True,
"excerptChars": 600,
}
run = client.actor("<YOUR_APIFY_USERNAME>/shopify-store-scraper-emails-phones-social-links").call(
run_input=run_input
)
rows = []
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
if item.get("type") == "status":
continue # skip uncharged no_input / unreachable rows
rows.append({
"store_domain": item.get("store_domain"),
"merchantName": item.get("merchantName"),
"primaryEmail": item.get("primaryEmail"),
"primaryPhone": item.get("primaryPhone"),
"socialPlatformCount": item.get("socialPlatformCount"),
})
import csv
with open("shopify_stores.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=rows[0].keys())
writer.writeheader()
writer.writerows(rows)

Node.js

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: '<YOUR_APIFY_API_TOKEN>' });
const runInput = {
merchantUrls: ['https://www.allbirds.com', 'rothys.com'],
findEmails: true,
findPhones: true,
findSocialProfiles: true,
readPolicies: true,
};
const run = await client.actor('<YOUR_APIFY_USERNAME>/shopify-store-scraper-emails-phones-social-links').call(runInput);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
const rows = items
.filter((item) => item.type !== 'status')
.map((item) => ({
store: item.store_domain,
email: item.primaryEmail,
phone: item.primaryPhone,
socials: item.socialPlatformCount,
}));
console.log(rows);

Async and scheduled pipelines

For large or recurring jobs, start the run without waiting on it (client.actor(...).start(run_input=run_input) in Python, or the Console's "Run" button) and poll the run status or the dataset item count until it finishes, rather than holding a synchronous connection open. For recurring jobs, an Apify Schedule triggers the same input on a cron-style interval without any external orchestrator.


🏢 Outbound and lead-gen teams

Turn a list of Shopify domains into a contactable list — primaryEmail, primaryPhone, and socialProfiles feed directly into a CRM import or a sequencing tool, with emailSource flagging whether the address is a monitored inbox or a policy-page mailbox.

📊 Data and analytics teams

Build a structured Shopify merchant dataset — merchantCity, merchantProvince, acceptedCardBrands, shipsToCountries, and the price/catalogue fields — for market-sizing or segmentation work, all typed and consistent across every store in the run.

📱 Ecommerce and DTC competitive intelligence

Watch price_avg, discount_pct_avg, hasReturnPolicy, and returnWindowDays on a competitor list over time; a policy or pricing change shows up in the next scheduled run's row.

🔬 Researchers

Build a public, structured dataset on DTC merchant practices — refund windows, shipping reach, payment-method adoption — using only publicly published storefront data, with no account or login involved.

🎥 Product and compliance tooling builders

Use storefrontStatus, hasReturnPolicy, and the contact-completeness fields as inputs to a due-diligence or partner-vetting tool, flagging stores that don't publish a return policy or a working contact channel before a deal proceeds.


Scraping publicly accessible Shopify storefront pages — the same pages any visitor's browser loads — is generally lawful in most jurisdictions; the well-known US precedent for scraping public web data is hiQ Labs v. LinkedIn (9th Cir. 2019, 2022), which held that scraping data with no access controls does not violate the Computer Fraud and Abuse Act. That case concerned public profile data specifically and its reasoning does not automatically extend to every jurisdiction or every dataset, so it is cited here as context, not a blanket guarantee.

This Actor returns store-level business records — merchant identity, published contact points, social links, policy text, and catalogue metadata — not individual consumer profiles, so general data-protection frameworks built around personal profiles (GDPR's Article 6 lawful-basis requirements, for instance) generally do not attach to the bulk of this output the way they would to a scraper of individual people. What can apply regardless of jurisdiction is the target site's own Terms of Service: scraping in violation of a site's ToS is a potential civil contract matter between the scraper and the site operator, separate from the public-data legality question above. A published business email or phone number that happens to identify a specific individual (a sole proprietor's own inbox, for example) can still carry data-protection obligations for how you store and use it — that determination and the lawful basis for it sit with you.

Shopify Store Scraper: Emails, Phones & Social Links returns only publicly accessible data. What you do with that data is your responsibility — consult legal counsel for commercial applications involving personal data.


❓ Frequently asked questions

Yes. It reads only public storefront pages — the homepage, contact pages, policy pages, and public meta.json/products.json/collections.json endpoints — so no Shopify account, Partner API key, or storefront login is needed or collected.

How does it handle Shopify's anti-scraping measures?

It runs an escalating connection ladder per request: on a blocking or error status (403, 406, 407, 408, 425, 429, 451, 500, 502, 503, 504) or a connection exception, it retries — up to 3 attempts per request — and, if Apify Proxy is enabled in proxyConfiguration, escalates from a direct connection to Apify's standard datacenter proxy and then to a residential proxy group. If Apify Proxy is left off (the schema's default, useApifyProxy: false), retries still happen but always over a direct connection, since there is no proxy tier to escalate into.

Can I run it at scale without getting blocked?

The Actor processes the store list one storefront at a time within a single run (each store's own product and collection pages fetch concurrently, but stores in the list do not run in parallel), so large lists are best split across multiple runs rather than one very large merchantUrls array. No uptime or throughput figure is published for this Actor.

How fresh is the data it returns?

Live per run. Every field is read from a fresh HTTP fetch of the target store at run time — there is no cached lookup table or stale contact database behind any field.

Which fields work best for AI training and RAG indexing?

For RAG, index merchantDescription and refundPolicyExcerpt against the store domain — they're the highest-information free text on a merchant page, in the merchant's own words. For training data, merchantCity, merchantProvince, acceptedCardBrands, shipsToCountries, and the price fields return as consistently typed primitives (strings, numbers, booleans, arrays) across every store, requiring no HTML stripping or normalization before use.

Does it work with Claude, ChatGPT, and other AI agent tools?

It is callable as an HTTP endpoint by any agent framework through the Apify API — every response is typed JSON, ready to drop into an LLM context window without a parsing step. No dedicated MCP server integration is documented for this Actor.

What happens to a password-protected storefront?

It's reported as storefrontStatus: "password_protected" with the catalogue and contact-discovery fields left empty — the Actor detects the store's own /password redirect and unlock form rather than guessing from body text, so an ordinary open store with a "password" string somewhere in its markup doesn't get misclassified.

Why might primaryEmail or primaryPhone come back empty for some stores?

Because that store genuinely doesn't publish the value anywhere the Actor checks — the homepage, the contact pages tried (per maxContactPages), the policy pages, and any JSON-LD structured data. Per the schema's own measured coverage over a 12-store sample across two runs: email resolved for 9–10 of 12 stores (75–83%), phone for 8 of 12 (67%). The Actor never fills a gap with a constructed value.

What is contactPagesTried versus contactPagesRead?

contactPagesTried is how many paths from contactPagePaths were attempted, bounded by maxContactPages. contactPagesRead is the subset of those that actually resolved to a real page (not a 404, not a password wall) and were read for contact data — so the two numbers diverge whenever a listed path doesn't exist on that particular store.


ℹ️ Disclaimer

Shopify Store Scraper: Emails, Phones & Social Links extracts only publicly available data from Shopify storefronts. This tool is intended for lawful use cases only. Users are responsible for complying with Shopify's terms of service and applicable data protection laws in their jurisdiction.