# Shopify Store Scraper - Catalog, Variants, SKUs & Prices (`flash_scraper/shopify-store-scraper`) Actor

Shopify product catalog scraper for any store: the full product catalog with every variant, SKU, price and compare-at price, collections, and a store-intelligence record - location, currency, catalogue size, countries shipped to, theme, socials. Prices pinned.

- **URL**: https://apify.com/flash\_scraper/shopify-store-scraper.md
- **Developed by:** [Flash Scrape](https://apify.com/flash_scraper) (community)
- **Categories:** E-commerce, Business
- **Stats:** 4 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $3.00 / 1,000 product results

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/actors/running/actors-in-store.md#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 Scraper — products, variants, collections **and store intelligence**

**A Shopify scraper that pulls any store's entire public catalog AND a profile of the store itself in one run** — titles, vendors, prices, variants, SKUs, availability, tags and images, plus the store's theme, currency, location and total catalogue size. No login, no API key, no browser automation, no code. Paste one or more Shopify website URLs and export the products to CSV, Excel or JSON minutes later. Built for competitor price tracking, dropshipping product research and store qualification.

Copy-paste input (these are the defaults — only `storeUrls` is required):

```json
{
  "storeUrls": ["https://www.kith.com/"],
  "maxProducts": 50,
  "oneRowPerVariant": false,
  "includeStoreProfile": true,
  "includeCollections": false,
  "market": "US",
  "proxyConfiguration": { "useApifyProxy": true }
}
```

### What it does

- **Reads each store's public `/products.json` feed** — the same product data Shopify serves to any visitor, fetched over plain HTTP.
- **Paginates the full catalog automatically** — 250 products per request, page after page, until the store runs out (with a hard safety cap of 1,000 pages ≈ 250k products per store).
- **Two output shapes** — one row per product with a summarized price range (`price_min` / `price_max`, `in_stock`, `variants_count`), or one row per variant with per-variant `sku`, `price`, `compare_at_price`, `available` and `grams` — ideal for price and stock monitoring.
- **Store intelligence** — one extra `record_type: "store"` row per store with theme, currency, country, catalogue size, shipping reach and social handles (details below).
- **Handles many stores in one run** — pass a list of competitor URLs; a store that fails (password-protected, feed disabled, not actually Shopify) is logged and skipped, and the run continues.
- **Normalizes messy input** — `kith.com`, `www.kith.com` or a full product URL all resolve to the correct store base.
- **Rides out throttling** — a store that answers HTTP 429 is retried on up to 5 fresh proxy IPs before it is skipped.

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `storeUrls` | array | — (required) | One or more Shopify store URLs. Bare domains work too. |
| `maxProducts` | integer | `50` | Products per store. `0` = the entire catalog — note that on large stores hundreds of requests can trigger HTTP 429 throttling; start with a limit, raise it once the store tolerates it. |
| `oneRowPerVariant` | boolean | `false` | ON: one row per variant (best for price/SKU monitoring). OFF: one row per product with a price range. |
| `includeStoreProfile` | boolean | `true` | Adds the `record_type: "store"` intelligence row per store. |
| `includeCollections` | boolean | `false` | Adds the store's published collections (title, handle, product count, URL — up to 250) to the store row. |
| `market` | string | `"US"` | Two-letter market to price in (`US`, `GB`, `DE`, …). See "Prices are pinned" below. |
| `proxyConfiguration` | object | Apify Proxy (datacenter) | Keep this on: Shopify blocks the platform's bare run IP. The standard datacenter group is verified working — residential is not needed. |

### Output columns

Every product/variant row carries: `store`, `product_id`, `title`, `handle`, `url`, `vendor`, `product_type`, `tags`, first `image` URL, `images_count`, and Shopify's `created_at` / `published_at` / `updated_at` timestamps.

| Column | Mode | Availability |
|---|---|---|
| `store`, `product_id`, `title`, `handle`, `url` | both | every row |
| `vendor`, `product_type`, `tags` | both | straight from the store's feed; empty when the merchant left them blank |
| `image`, `images_count` | both | `image` is `null` for products with no images |
| `variants_count`, `price_min`, `price_max`, `in_stock` | one row per product | `price_min`/`price_max` are `null` if no variant has a parseable price |
| `variant_id`, `variant_title`, `price`, `available`, `grams` | one row per variant | every variant row |
| `sku` | one row per variant | empty when the merchant never assigned one |
| `compare_at_price` | one row per variant | `null` unless the item is marked down |

Both shapes are flat, so CSV export needs no post-processing.

### Pricing — what does it cost per 1,000 products?

**$0.003 per dataset row — $3.00 per 1,000 products (or variants)** — plus a fraction-of-a-cent Actor start fee (about $0.0001 at the default 2 GB). You pay only for rows actually delivered: a store that fails or a run that returns nothing costs nothing beyond that start fee. A **free Apify plan is enough to try it** on a full mid-size store.

Tip: variant mode produces more rows than product mode for the same store (one per SKU), so pick the shape that matches what you actually need.

### Store intelligence — what other Shopify scrapers don't give you

Most Shopify scrapers return product rows and stop. With **Include store profile** on (the default), every store also yields one `record_type: "store"` row describing the merchant itself, read from the store's public `/meta.json` and storefront:

| Field | Example |
|---|---|
| `store_name`, `store_city`, `store_province`, `store_country` | `Misen`, `Brooklyn`, `New York`, `US` |
| `store_currency`, `money_format` | `USD`, `${{amount}}` |
| `myshopify_domain` | `misenco.myshopify.com` |
| **`published_products_count`** | `342` — the store's TOTAL catalogue size, without scraping it |
| **`published_collections_count`** | `218` |
| **`ships_to_country_count`**, `ships_worldwide` | `134`, `true` |
| `accepted_card_brands`, `offers_shop_pay_installments` | `["visa","master",...]`, `true` |
| `theme_name`, `theme_id` | `1.3.0 [New Relic/Speedsize]`, `198967558225` |
| `instagram`, `facebook`, `tiktok`, `twitter`, `youtube` | `misenkitchen` |

The `/meta.json` fields are returned whenever the store serves that endpoint. Theme and social handles are read from the storefront HTML and are best-effort: if Shopify throttles the storefront page after all IP rotations, they come back empty and the run logs a warning — the `/meta.json` fields are still present.

#### How do I check what theme a Shopify store is using?

Shopify injects `Shopify.theme = {...}` into every storefront page, so `theme_name` and `theme_id` on the store row tell you the theme without any extension or manual source-viewing — for every store in your list at once.

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

`published_products_count` answers it from a single request: **you learn whether a store has 40 products or 25,000 before paying to scrape the catalogue**. Run with a tiny `maxProducts` cap to qualify a long list of stores cheaply, then scrape only the ones worth it.

### Prices are pinned to one currency — read this if you compare stores

**Shopify localises prices to whichever IP requests the page.** Measured: the same variant returned `1827.00` from a Moroccan exit node and `190.00` (USD) with the market pinned. Because cloud scrapers rotate proxy IPs between countries, an unpinned scrape can return **a different currency on different rows of the same run** — which quietly ruins any price comparison built on it.

This Actor pins the market (default `US`) and returns `store_currency` on the store row, so a price is always interpretable. Set **Market** to `GB`, `DE`, `CA` etc. to price in that market instead.

### How does it handle the products.json limit and pagination?

Shopify caps `/products.json` at 250 products per request, and the storefront endpoint ignores `since_id` — so this Actor paginates with 1-indexed `page` numbers, the only reliable cursor, until the store runs out. A hard cap of 1,000 pages (≈ 250,000 products) per store guards against runaway loops; `maxProducts` sets your own tighter cap.

### How do I export a Shopify store's products to CSV or Excel?

Run the Actor, open the run's **Dataset** tab and pick **CSV**, **Excel**, **JSON** or HTML — or pull the dataset programmatically via the Apify API. No Shopify admin access or export app needed: this works on any store, not just your own.

### Use cases

- **Competitor price tracking** — schedule a daily run over competitors' stores and diff prices, new products, and stock status over time.
- **Dropshipping product research** — pull a winning store's full catalog with vendors, tags and product types to spot trending products and suppliers.
- **Catalog migration and backup** — export a store's product list without private API keys or admin access.
- **Market analysis** — scrape every store in a niche and aggregate by `product_type`, `vendor`, or price band.
- **Store qualification at scale** — run with the store profile on and a tiny product cap to get each store's country, currency, catalogue size and shipping reach cheaply.
- **Competitor benchmarking** — compare catalogue size, collection count, shipping reach and tech (theme) across several merchants side by side.

#### Example output

A real sample from a live run (default product mode):

| title | vendor | product\_type | price\_min | price\_max | in\_stock |
|---|---|---|---|---|---|
| Men's Cruiser Terralux - Anthracite (Da… | Allbirds | Shoes | 135 | 135 | false |
| Women's Wool Runner NZ Mid Waterproof -… | Allbirds | Shoes | 160 | 160 | true |
| Free Returns Coverage | re:do | return,package\_protection | 0.8 | 2.98 | true |
| Women's Wool Runner-up Mizzle - Hazy In… | Allbirds | Shoes | 60 | 60 | true |

Example row (default product mode):

```json
{
  "store": "https://www.allbirds.com/",
  "product_id": 4547069443119,
  "title": "Men's Wool Runners",
  "handle": "mens-wool-runners",
  "url": "https://www.allbirds.com/products/mens-wool-runners",
  "vendor": "Allbirds",
  "product_type": "Shoes",
  "tags": "mens, wool, runners",
  "image": "https://cdn.shopify.com/s/files/1/1104/4168/products/wool-runner.jpg",
  "images_count": 6,
  "created_at": "2020-03-18T09:12:44-07:00",
  "published_at": "2020-03-20T08:00:01-07:00",
  "updated_at": "2026-06-28T02:14:09-07:00",
  "variants_count": 14,
  "price_min": 110.0,
  "price_max": 125.0,
  "in_stock": true
}
```

### What this Actor does NOT do

- **No inventory quantities** — Shopify's public feed carries no stock counts; `available`/`in_stock` are true/false only.
- **No product descriptions** — the HTML description body is not in the rows (see the FAQ for the workaround).
- **No sales, revenue or best-seller rankings** — Shopify does not publish sales numbers. Many stores expose a "best sellers" **collection** you can spot via `includeCollections`, but this Actor does not order products by sales.
- **No reviews, and no Shopify App Store data** — it scrapes merchant storefronts, not apps.apple/shopify listings.
- **No password-protected storefronts** — those are skipped with a warning, never logged into.

### Tips / FAQ

**Is this legal?**
It reads each store's own **public** `/products.json` feed — data Shopify serves to any anonymous visitor. No login, no private data, no paywall bypass. As always, use scraped data responsibly and within applicable laws and the store's terms.

**Do I need a proxy or an API key?**
No API key, no login, no headless browser. A proxy, yes — **Shopify blocks Apify's bare run IP**, so the Actor routes through Apify Proxy by default. The standard **datacenter** group is verified working and costs far less than residential; just leave the default on.

**Does it work on every Shopify store?**
Most standard stores, yes. Password-protected storefronts and stores that have disabled the public product feed return an error for `/products.json`; those are logged with a warning and **skipped**, and the rest of the run continues. Non-Shopify URLs are skipped the same way. A few large stores (allbirds.com is a common example) throttle shared cloud IPs aggressively — the Actor retries each 429 on up to 5 fresh proxy IPs before giving up on that store.

**Does it include product descriptions?**
No — rows include title, vendor, type, tags, timestamps, image URL and price/variant data, but not the HTML description body. If you need descriptions, pair it with an extraction actor on the product URLs (see Related actors below).

**How do I track prices over time?**
Turn `oneRowPerVariant` ON so every SKU gets its own row with `price` and `compare_at_price`, then schedule the actor (Apify Schedules) and compare datasets between runs — a competitor price tracker in two clicks.

**How fast is it, and are there limits?**
It fetches 250 products per request, so even multi-thousand-product catalogs finish in minutes. A built-in cap of 1,000 pages per store (≈ 250,000 products) guards against runaway loops; `maxProducts` lets you set your own tighter cap.

### Related actors

- [Shopify Leads Scraper](https://apify.com/flash_scraper/shopify-leads-scraper) — find Shopify **stores** (with contact data) instead of products; the natural front end to this actor.
- [Smart Scraper by Example](https://apify.com/flash_scraper/smart-scraper-by-example) — extract extra fields (like descriptions) from the product URLs this actor gives you.
- [Company & Domain Enricher](https://apify.com/flash_scraper/company-domain-enricher) — turn store domains into full company records with tech stack and socials.

***

Found a bug or need a feature? Open an Issue on this actor's **Issues** tab — typical response within 1 business day.

***

### More from Flash Scrape

Same publisher, same rules: no API keys, pay per row, filters run before billing.

- [Multi Job Board Scraper](https://apify.com/flash_scraper/multi-jobboard-scraper) - LinkedIn, Indeed, Glassdoor and 9 more boards in one deduplicated table
- [Local Business Leads Scraper](https://apify.com/flash_scraper/local-business-leads) - local businesses in any category and city with MX-verified emails, phones and socials
- [Remote Jobs Aggregator](https://apify.com/flash_scraper/remote-job-aggregator) - 10 remote job boards in one feed, only-new-jobs monitoring
- [Creator Leads Scraper](https://apify.com/flash_scraper/creator-leads-scraper) - YouTube, TikTok and Instagram creator emails in one run

# Actor input Schema

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

One or more Shopify store URLs (e.g. https://www.allbirds.com). The scraper reads each store's public /products.json feed. Note: some large stores (allbirds.com is a common example) throttle shared cloud IPs and will return HTTP 429 no matter which proxy IP is used - if that happens, try a different store or your own proxy.

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

Maximum products per store. **0 = the store's entire catalog**, which for a large shop means hundreds of requests and often triggers HTTP 429 throttling - set a limit for a fast sample, raise it or use 0 once you know the store tolerates it.

## `oneRowPerVariant` (type: `boolean`):

ON: emit a row for every variant (best for price/SKU monitoring). OFF: one row per product with a price range.

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

Shopify blocks Apify's bare run IP, so this Actor routes requests through Apify Proxy by default. The standard datacenter group is enough - verified working - and costs far less than RESIDENTIAL. Leave this on unless you have your own proxy.

## `includeStoreProfile` (type: `boolean`):

Adds one extra row per store (marked record\_type: store) with the store's name, city, province, country, base currency, myshopify domain, TOTAL catalogue size, collection count, how many countries it ships to, accepted card brands, Shop Pay availability, theme name and social handles. This is store intelligence for qualifying a merchant - product scrapers do not return it.

## `includeCollections` (type: `boolean`):

Adds the store's published collections (title, handle, product count, URL) to the store profile row - the merchant's own merchandising structure.

## `market` (type: `string`):

Two-letter market code, e.g. US, GB, DE. Shopify localises prices to the requesting IP: the same variant read 1827.00 from a Moroccan exit node and 190.00 (USD) with this pinned. Since proxy exit nodes move between countries, leaving this unset can return a different currency per row.

## Actor input object example

```json
{
  "storeUrls": [
    "https://store.example.com/"
  ],
  "maxProducts": 50,
  "oneRowPerVariant": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "includeStoreProfile": true,
  "includeCollections": false,
  "market": "US"
}
```

# Actor output Schema

## `results` (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.kith.com/"
    ],
    "maxProducts": 50,
    "proxyConfiguration": {
        "useApifyProxy": true
    },
    "includeStoreProfile": true,
    "includeCollections": false,
    "market": "US"
};

// Run the Actor and wait for it to finish
const run = await client.actor("flash_scraper/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 = {
    "storeUrls": ["https://www.kith.com/"],
    "maxProducts": 50,
    "proxyConfiguration": { "useApifyProxy": True },
    "includeStoreProfile": True,
    "includeCollections": False,
    "market": "US",
}

# Run the Actor and wait for it to finish
run = client.actor("flash_scraper/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 '{
  "storeUrls": [
    "https://www.kith.com/"
  ],
  "maxProducts": 50,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "includeStoreProfile": true,
  "includeCollections": false,
  "market": "US"
}' |
apify call flash_scraper/shopify-store-scraper --silent --output-dataset

```

## MCP server setup

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