# Shopify Store Scraper (`mlg14/shopify-store-scraper`) Actor

Discover Shopify stores by product niche or category and export public store details, contact channels, ratings, and sample products.

- **URL**: https://apify.com/mlg14/shopify-store-scraper.md
- **Developed by:** [MLG Data](https://apify.com/mlg14) (community)
- **Categories:** Lead generation, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$0.30 / 1,000 results

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 Store Scraper

Discover Shopify stores by the products they sell and collect public store contact information in one dataset. Search by keyword or product category, then export store names, websites, emails, phone numbers, social profiles, ratings, business addresses, and sample products as JSON, CSV, or a spreadsheet.

The scraper searches the public Shop marketplace, follows result pages, groups matching products by store, and opens each public store page for details. One dataset row represents one store, even if several of its products match your search. It is useful for niche research, supplier lists, market mapping, and store lead discovery.

### What data can you extract from Shopify stores?

| Field | Description | Example |
| --- | --- | --- |
| id | Marketplace store identifier. | "14671" |
| shopifyId | Platform store identifier when published. | "1538490427" |
| name | Public store display name. | "Example Store" |
| websiteUrl | Store website, or the domain of a matched product if the store page omits it. | "https://example.com" |
| myshopifyDomain | Published platform subdomain. | "example.myshopify.com" |
| shareUrl | Public marketplace store page. | "https://shop.app/m/example" |
| logoUrl | Public logo image URL, when provided. | "https://cdn.shopify.com/..." |
| address | Published address lines and a recognized two-letter country code. | {"formatted":\["Street 1","City","United States"],"country":"US"} |
| email | Public store email channel. | "hello@example.com" |
| phone | Public store phone channel. | "555-0100" |
| contacts | All public contact channels, including website and social links. | \[{"method":"web","target":"https://example.com"}] |
| rating | Average product rating for the store. | 4.8 |
| totalProductRatings | Number of product ratings. | 1200 |
| totalProductReviews | Number of product reviews. | 430 |
| sampleProducts | Up to three matching products, each with title, price, currency, URL, and image URL. | \[{"title":"Coffee Beans","price":18.0,"currency":"USD","url":"https://example.com/products/coffee","imageUrl":null}] |
| searchQuery | Keyword that led to the store. | "organic coffee" |
| scrapedAt | Time this dataset row was created, in UTC. | "2026-09-26T12:00:00+00:00" |

The contacts array retains channel types as published. Typical methods include web, email, phone, contact_url, and social networks. A store may publish several methods, only one method, or none. The separate email and phone fields make common lead-list exports easier. Their values come from the published contact list; the scraper does not guess addresses or generate contacts.

A sample product contains the price and currency shown in the marketplace for that result. Search results can show different currencies depending on the shopper region or product. Do not combine prices from different currencies without conversion. A store may appear with more matching products than the three included in the row; sampleProducts is a short illustration of why the store matched, not its full catalog.

### How to scrape Shopify stores

1. Enter a product keyword such as organic coffee, or choose a category. A keyword and category can be combined.
2. Set Maximum stores to the number of unique store rows you want. The default is 50.
3. Optionally choose Store country to keep only stores whose public business address can be mapped to that country.
4. Run the scraper. It follows search pages until it has enough candidate stores, reaches the page limit, or the search feed ends.
5. Download the dataset in your preferred format. Each row contains the store identity, available contact details, ratings, and product examples.

For a short first run, use a broad product term and request 30 stores. Narrow terms can return fewer unique stores because several results may belong to the same shop. A single product result is not the same as a single store result.

### Input

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| query | String | Empty | Product phrase used for store discovery. Required unless category is set. |
| category | String | Any category | Public product category identifier. It can be used alone or with a keyword. |
| storeLocation | String | Any location | Two-letter country code. Applied to the published business address after each store is opened. |
| maxItems | Integer | 50 | Maximum unique store rows, from 1 to 10,000. |
| maxPages | Integer | 100 | Maximum search pages, from 1 to 1,000. Each page usually contains about 30 product matches. |
| proxyConfiguration | Object | Enabled | Connection configuration. The marketplace requires residential routing in the current network environment. |

Example input:

```
{
  "query": "organic coffee",
  "maxItems": 30,
  "maxPages": 10
}
```

For a category search, select a category in the input form and leave query empty. Category searches can be broad, so set a manageable store limit first. When combining a category with a keyword, the marketplace decides which matching products to surface. The results are discovery matches, not a certification that a store sells only that category.

The country filter is intentionally strict. A store whose public page omits a business address will be excluded when storeLocation is set, even if its website appears to operate in the selected country. For wider lead discovery, leave the filter empty and inspect address.country in the exported dataset. This helps avoid treating a shipping destination or a domain suffix as proof of business location.

### Output example

This row comes from the 30-store verification run. Store identifying values and direct contact details are masked for display; numeric values and field availability are from the run.

```
{
  "id": "2080194",
  "shopifyId": "26358284334",
  "name": "[store name]",
  "websiteUrl": "https://[store website]",
  "myshopifyDomain": "[store subdomain].myshopify.com",
  "shareUrl": "https://shop.app/m/[store handle]",
  "logoUrl": "https://cdn.shopify.com/[image path]",
  "address": {
    "formatted": ["[street address]", "[city and postal code]", "United Kingdom"],
    "country": "GB"
  },
  "email": "[published email]",
  "phone": null,
  "contacts": [
    {"method": "web", "target": "https://[store website]"},
    {"method": "facebook", "target": "https://[profile URL]"},
    {"method": "instagram", "target": "https://[profile URL]"},
    {"method": "youtube", "target": "https://[profile URL]"},
    {"method": "email", "target": "[published email]"}
  ],
  "rating": 4.8327,
  "totalProductRatings": 7728,
  "totalProductReviews": 3532,
  "sampleProducts": [
    {
      "title": "[matching product]",
      "price": 23.73,
      "currency": "EUR",
      "url": "https://[product URL]",
      "imageUrl": "https://cdn.shopify.com/[image path]"
    }
  ],
  "searchQuery": "organic coffee",
  "scrapedAt": "2026-09-26T14:39:07.723557+00:00"
}
```

The displayed sampleProducts array is shortened to one product; the actual row contained three. In that verification run, all 30 rows had store IDs, names, websites, ratings, and product samples. Public address data appeared in 17 rows, email in 19, and phone in 13. Those proportions describe one keyword and one run, not guaranteed fill rates for other searches.

### Use cases

- Niche prospect lists: Find storefronts that surface for a product phrase and export their published business contact channels for review.
- Supplier discovery: Compare stores offering similar product types, then open their websites or marketplace pages for deeper qualification.
- Market sizing: Collect a directional set of stores for several keywords and compare store counts, ratings, and visible product ranges. Search ranking is not a census, so interpret totals accordingly.
- Competitor tracking: Repeat the same query on a schedule and compare the stores and example products visible over time.
- Regional analysis: Use the address country filter when a published business address is essential to your workflow. Stores without an address are excluded.
- Catalog exploration: Use sample product URLs to inspect a matched item and its price before deciding whether the store is relevant.

A product keyword is usually the most useful starting point because this marketplace ranks product results. Store names may repeat across searches, so deduplicate exports by id when combining multiple runs. The row identifier is a better join key than a store name, which can change or be shared by different shops.

### How much does it cost to scrape Shopify stores?

The configured result event price is $0.30 per 1,000 store rows. The event portion is proportional to the number of rows delivered: 30 stores are about $0.009, 1,000 stores are $0.30, and 5,000 stores are $1.50. A 10,000-store run would be $3.00 in result events if enough stores were available and the search page limit allowed them to be reached.

Connection and execution usage may be billed separately by the hosting plan. The 30-store verification run reported about $0.027 in platform usage. This scraper opens a public detail page for each candidate store, so a restrictive country filter or a broad page limit can increase request usage while producing fewer output rows. Start with a smaller maxItems and maxPages for a new query, then increase them after checking the result quality.

The maximum input value is a cap, not a promise of that many stores. Billing for result events follows rows actually pushed to the dataset. If the feed ends after 420 distinct stores, setting maxItems to 1,000 does not create another 580 results.

### Tips for best results

Use terms describing what stores sell, such as ceramic mugs or running shoes. Broad words can bring more stores but lower relevance; very narrow phrases can yield repeated products from a few stores. Try related terms in separate runs to expand coverage, and merge exports by id afterward.

Search result pages usually contain around 30 product cards. Several cards can belong to the same store. Expect to read multiple pages to reach 30 or 50 unique stores. The maxPages input prevents an open-ended crawl when a query is repetitive. If a run ends below maxItems while the feed still has results, raise maxPages.

When a country filter is set, the scraper first discovers candidate stores and then checks each public store address. This can produce fewer rows than the maximum even when the search feed is large. If coverage matters more than address certainty, leave storeLocation empty and filter the downloaded data later.

Some stores provide websites but no email or phone. Others publish a contact page or social profile instead. Inspect contacts for alternative public channels. The scraper records what the marketplace publishes; it does not infer private contact details from unrelated sources.

Prices in sampleProducts are displayed marketplace prices at collection time. They can change after the run, and variants, discounts, tax, shipping, or localization can alter the final price. Use the linked product page when making a purchase or pricing decision.

### Limits

The discovery source is a product search feed. It surfaces stores through matching products and ranking; it is not a complete directory of every Shopify store. A store with no indexed matching product may be absent. Result ordering can change, and repeated runs can have overlap. This actor deduplicates within each run but does not keep cross-run history.

The marketplace controls pagination and may end a feed or return fewer results for a narrow phrase. maxPages also limits coverage. The scraper uses the feed's continuation cursor rather than page numbers; a page=2 URL does not expose the second feed page.

Business addresses are published as formatted lines. The country code is populated only when the last line matches a recognized country name. Street, city, region, and postal code are kept together in formatted; they are not guessed into separate fields. If a store has no address, address is null.

Store contacts and ratings are optional. Product review totals describe the store's product ratings on the marketplace, not a rating of the store's shipping or customer service. The first three matching product samples are included; this actor does not crawl a full catalog.

A proxy tunnel used for discovery can refuse datacenter traffic. The current implementation uses residential routing for marketplace requests based on that observed behavior. Availability and request cost can vary by account and region. The scraper uses only public search and public store pages; it does not sign in, access checkout pages, or read customer records.

### Automated workflows

You can schedule the same keyword search to watch which stores appear over time. One useful workflow asks for 30 stores selling a niche product, downloads the rows, and compares id values with the prior export. Another collects several related phrases, merges the outputs by id, then sorts stores by rating or review count for manual research.

For a location-focused workflow, request a country code and check both address.formatted and address.country in the results. A missing address means the store cannot be verified by this filter. For a lead review workflow, inspect contacts alongside websiteUrl and sampleProducts before adding a store to an outreach list.

### FAQ

#### Do I need a store URL?

No. Enter a product keyword or choose a category. The scraper discovers stores from public product search results.

#### Can I get only stores in one country?

Yes, use storeLocation. This checks the public business address on each store page. Stores without a recognized published country are left out. It is a strict address filter, not a shipping or language filter.

#### Why did I receive fewer stores than maxItems?

The query may have limited matches, many products may belong to the same store, the feed may end, or maxPages may stop discovery. A country filter can also remove stores without a matching public address.

#### Why are email and phone sometimes empty?

The store did not publish those channels in its public marketplace profile. Check contacts for a website, contact page, or social link. Empty fields are normal and are not filled with guessed values.

#### Are the ratings store reviews?

rating, totalProductRatings, and totalProductReviews summarize product feedback associated with the store. They should not be interpreted as a separate fulfillment or service score.

#### Can I export to a spreadsheet?

Yes. The dataset can be downloaded in tabular formats. Nested contacts, address, and sampleProducts are easiest to inspect in JSON; spreadsheet exports may represent them as nested values or text.

#### Is this legal to use?

The actor reads public marketplace pages and public store profiles. Follow the source site's terms, applicable privacy rules, and your local requirements for using contact information. Do not use published contacts for abusive or unlawful outreach.

### Integrations

The resulting dataset can feed a scheduled report, a webhook, a spreadsheet import, or a programmatic data pipeline. Keep the id field when syncing records so repeated searches update the same store rather than creating duplicates. The scrapedAt timestamp lets downstream jobs distinguish a newly collected row from an older export.

### Support

Open an issue on the actor's Issues tab with the query, relevant input values, and an example of the unexpected row or missing field. Include the run identifier when available so the behavior can be reproduced.

# Actor input Schema

## `query` (type: `string`):

Find stores selling products matching this phrase. If both keyword and category are left empty, the default keyword is used.

## `category` (type: `string`):

Optionally narrow discovery to a product category, or select a category without a keyword.

## `storeLocation` (type: `string`):

Keep only stores with a public business address in this country. Stores without a listed country are excluded when this filter is set.

## `maxItems` (type: `integer`):

Maximum unique store records to return.

## `maxPages` (type: `integer`):

Stop discovery after this many search pages even when more results are available. Each page usually has 30 product matches.

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

Connection settings for reaching the public marketplace.

## `maxRuntimeSecs` (type: `integer`):

Stop collecting after this many seconds and return the stores found so far. 0 = automatic (150 seconds plus 2 seconds per requested store). Raise it for large runs combined with a store-country filter.

## Actor input object example

```json
{
  "query": "organic coffee",
  "category": "",
  "storeLocation": "",
  "maxItems": 50,
  "maxPages": 100,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "maxRuntimeSecs": 0
}
```

# Actor output Schema

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

Store records in the default dataset.

# 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 = {
    "query": "organic coffee"
};

// Run the Actor and wait for it to finish
const run = await client.actor("mlg14/shopify-store-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 = { "query": "organic coffee" }

# Run the Actor and wait for it to finish
run = client.actor("mlg14/shopify-store-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 '{
  "query": "organic coffee"
}' |
apify call mlg14/shopify-store-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,mlg14/shopify-store-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/RMw0uwKeOJyhtVjbv/builds/gIfWQ4ZUQDbwQJeCU/openapi.json
