# Shopify Products Scraper | Prices & Variants (`datascraperes/shopify-products-scraper`) Actor

Extract complete public Shopify product catalogs through products.json. Get titles, prices, compare-at prices, variants, SKUs, availability, images, tags, and vendors. Process one or many stores with no Shopify API key. Pay only $1 per 1,000 saved products—no start or per-store fees.

- **URL**: https://apify.com/datascraperes/shopify-products-scraper.md
- **Developed by:** [DataScraperES](https://apify.com/datascraperes) (community)
- **Categories:** E-commerce, Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$1.00 / 1,000 product extracteds

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/platform/actors/running/actors-in-store#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

Extract complete public product catalogs from one or more Shopify stores. Add the storefront
URLs and receive one clean Dataset row per product, including titles, vendors, product types,
descriptions, prices, compare-at prices, availability, variants, SKUs, barcodes, options, images,
tags, and timestamps.

The Actor automatically paginates through each store, removes duplicate products, handles
temporary rate limits, and preserves results from successful stores when another store fails. No
Shopify account, Shopify API key, collection URL, product URL list, or technical performance
configuration is required.

This is an independent, unofficial tool and is not affiliated with or endorsed by Shopify.

### What can you use this Shopify Products Scraper for?

- **Competitor price monitoring:** compare current and compare-at prices, find price ranges, and
  detect products or variants that are no longer available.
- **Catalog and assortment analysis:** compare vendors, product types, tags, variants, images, and
  product counts across stores.
- **Product research:** create structured product datasets for analysis, sourcing, merchandising,
  or catalog planning.
- **Multi-store comparison:** process several Shopify storefronts in one run and identify the
  source store on every product row.
- **New and updated product tracking:** use product publication and update timestamps to compare
  scheduled runs.
- **Data export:** download the Dataset as JSON, CSV, Excel, XML, or RSS for spreadsheets,
  databases, dashboards, and other workflows.

### What product data can be extracted?

Each Dataset item represents one Shopify product. Depending on the selected output options, it can
contain:

- Product ID, title, handle, product URL, store URL, and store domain
- Vendor, product type, tags, plain-text description, and original HTML description
- Minimum and maximum variant prices
- Minimum and maximum compare-at prices
- Storefront currency when it can be confirmed
- Overall product availability
- Variant IDs, titles, SKUs, barcodes, prices, compare-at prices, and availability
- Variant option values such as size, color, or material
- Weight, shipping, tax, and inventory-management signals exposed by the storefront
- Product options and their possible values
- Image URLs, dimensions, positions, alternative text, and associated variant IDs
- Creation, publication, update, and extraction timestamps
- Warnings when the public Shopify data indicates possible incomplete variant coverage

Product and variant IDs are returned as strings so their exact values are preserved. Prices are
also returned as decimal strings to avoid floating-point rounding.

### Pricing

The Actor costs **$1 per 1,000 successfully extracted products**, equivalent to **$0.001 per
product**.

You are charged only when a unique product is successfully saved to the Dataset:

| Products saved | Product charge |
| ---: | ---: |
| 100 | $0.10 |
| 500 | $0.50 |
| 1,000 | $1.00 |
| 10,000 | $10.00 |

There is no Actor start fee, no per-store fee, and no charge for failed, blocked, invalid, or
duplicate products. Platform usage is included. If the run reaches your maximum spending limit,
the Actor stops adding chargeable products.

### How to scrape Shopify products

1. Open the **Input** tab.
2. Add one or more public Shopify storefront URLs.
3. Choose the maximum number of products per store and for the whole run.
4. Select which optional product data you want included.
5. Click **Start**.
6. Open the **Dataset** to view or export the products.

The default settings include variants, images, description HTML, and storefront currency
detection. They are suitable for most runs.

### Input example

```json
{
  "startUrls": [
    {
      "url": "https://partakefoods.com"
    },
    {
      "url": "https://www.greats.com"
    }
  ],
  "maxProductsPerStore": 500,
  "maxTotalProducts": 800,
  "respectRobotsTxt": true,
  "fetchStorefrontContext": true,
  "includeVariants": true,
  "includeImages": true,
  "includeDescriptionHtml": true,
  "outputMode": "normalized"
}
```

### Input fields

| Field | Required | Default | Description |
| --- | --- | --- | --- |
| `startUrls` | Yes | — | Between 1 and 100 public Shopify storefront homepages or localized storefront URLs. Duplicate URLs are processed once. |
| `maxProductsPerStore` | No | `1000` | Maximum unique products saved from each store. Accepted range: 1–100,000. |
| `maxTotalProducts` | No | `10000` | Maximum unique products saved across the entire run. Accepted range: 1–100,000. |
| `respectRobotsTxt` | No | `true` | When enabled, skips a store if its crawler rules disallow access to the public product feed. |
| `fetchStorefrontContext` | No | `true` | Attempts to identify the currency presented by each storefront. Product extraction continues if currency cannot be confirmed. |
| `includeVariants` | No | `true` | Adds nested variant and product-option data. |
| `includeImages` | No | `true` | Adds nested product image metadata and URLs. |
| `includeDescriptionHtml` | No | `true` | Keeps the original HTML description in addition to the cleaned plain-text description. |
| `outputMode` | No | `normalized` | Selects the stable normalized output or normalized output plus the original public Shopify product object. |

#### Shopify store URLs

Use public storefront URLs, for example:

- `https://example-store.com`
- `https://www.example-store.com`
- `https://example-store.myshopify.com`
- `https://example-store.com/en-us`

The Actor accepts normal custom domains and public `myshopify.com` domains. It does not accept
store administration pages, URLs containing credentials, localhost addresses, or private network
addresses.

#### Product limits

`maxProductsPerStore` limits each individual store. `maxTotalProducts` limits the complete run.
The run stops at whichever limit is reached first.

For example, with three stores, `maxProductsPerStore: 100` and `maxTotalProducts: 200`, the Actor
can save at most 100 products from any one store and at most 200 products in total.

The final count can be lower when:

- A store contains fewer public products than the selected limit
- Products are duplicated or missing required identifiers
- A storefront is password-protected, blocked, unavailable, or not compatible
- `robots.txt` disallows access and `respectRobotsTxt` is enabled
- The run reaches its maximum spending limit

#### Output mode

- **`normalized`** is recommended for most users. It returns a consistent field structure that is
  easier to export, compare, and import elsewhere.
- **`normalizedAndRaw`** returns the normalized fields and adds `rawProduct`, containing the
  original public Shopify product object. This produces larger Dataset items and is useful only
  when you need source fields that are not part of the normalized output.

#### Optional data and Dataset size

Disabling `includeVariants`, `includeImages`, or `includeDescriptionHtml` reduces Dataset size.
The product row and its core pricing, availability, identity, source, and timestamp fields remain
available.

### Output example

Each Dataset item represents one product:

```json
{
  "productKey": "www.greats.com:1234567890",
  "shopifyProductId": "1234567890",
  "storeUrl": "https://www.greats.com",
  "storeDomain": "www.greats.com",
  "productUrl": "https://www.greats.com/products/classic-leather-sneaker",
  "title": "Classic Leather Sneaker",
  "handle": "classic-leather-sneaker",
  "vendor": "Example Brand",
  "productType": "Sneakers",
  "descriptionText": "A versatile leather sneaker designed for everyday wear.",
  "descriptionHtml": "<p>A versatile leather sneaker designed for everyday wear.</p>",
  "tags": [
    "Leather",
    "Sneakers",
    "Men"
  ],
  "available": true,
  "currency": "USD",
  "priceMin": "119.00",
  "priceMax": "129.00",
  "compareAtPriceMin": "149.00",
  "compareAtPriceMax": "149.00",
  "variantCountObserved": 2,
  "variantsMayBeTruncated": false,
  "variants": [
    {
      "id": "9876543210",
      "title": "US 9 / White",
      "sku": "CLS-09-WHT",
      "barcode": "0123456789012",
      "available": true,
      "price": "119.00",
      "compareAtPrice": "149.00",
      "option1": "US 9",
      "option2": "White",
      "option3": null,
      "grams": 850,
      "weight": 0.85,
      "weightUnit": "kg",
      "requiresShipping": true,
      "taxable": true,
      "inventoryManagement": "shopify",
      "featuredImage": null
    },
    {
      "id": "9876543211",
      "title": "US 10 / White",
      "sku": "CLS-10-WHT",
      "barcode": "0123456789013",
      "available": false,
      "price": "129.00",
      "compareAtPrice": "149.00",
      "option1": "US 10",
      "option2": "White",
      "option3": null,
      "grams": 880,
      "weight": 0.88,
      "weightUnit": "kg",
      "requiresShipping": true,
      "taxable": true,
      "inventoryManagement": "shopify",
      "featuredImage": null
    }
  ],
  "options": [
    {
      "name": "Size",
      "position": 1,
      "values": [
        "US 9",
        "US 10"
      ]
    },
    {
      "name": "Color",
      "position": 2,
      "values": [
        "White"
      ]
    }
  ],
  "images": [
    {
      "id": "1111111111",
      "src": "https://cdn.shopify.com/s/files/example/classic-sneaker.jpg",
      "altText": "White classic leather sneaker",
      "width": 1600,
      "height": 1600,
      "position": 1,
      "variantIds": [
        "9876543210",
        "9876543211"
      ]
    }
  ],
  "createdAt": "2025-03-10T09:00:00Z",
  "publishedAt": "2025-03-12T09:00:00Z",
  "updatedAt": "2026-07-29T14:30:00Z",
  "scrapedAt": "2026-07-30T10:00:00+00:00",
  "source": {
    "endpoint": "https://www.greats.com/products.json?limit=100&page=1",
    "page": 1,
    "contract": "public_products_json_observed"
  },
  "warnings": []
}
```

Fields that the storefront does not provide are returned as `null`, empty arrays, or omitted when
their corresponding output option is disabled.

### Output field reference

#### Identity and source

| Field | Description |
| --- | --- |
| `productKey` | Stable combination of store domain and Shopify product ID, used to prevent duplicates. |
| `shopifyProductId` | Shopify product identifier returned as a string. |
| `storeUrl` | Normalized storefront URL used for the product. |
| `storeDomain` | Store hostname, useful for grouping multi-store results. |
| `productUrl` | Direct public product-page URL. |
| `handle` | Storefront product handle used in the product URL. |
| `source` | Public source URL, catalog page number, and observed source contract. |

#### Product content

| Field | Description |
| --- | --- |
| `title` | Product title. |
| `vendor` | Product vendor or brand when supplied by the merchant. |
| `productType` | Merchant-defined Shopify product type. |
| `descriptionText` | Product description converted to plain text. |
| `descriptionHtml` | Original HTML description when `includeDescriptionHtml` is enabled. |
| `tags` | Merchant-defined product tags. |

#### Pricing and availability

| Field | Description |
| --- | --- |
| `available` | `true` when at least one observed variant is reported as available. |
| `currency` | Currency presented by the storefront, or `null` when it cannot be confirmed. |
| `priceMin`, `priceMax` | Lowest and highest observed variant prices. |
| `compareAtPriceMin`, `compareAtPriceMax` | Lowest and highest observed compare-at prices, or `null` when none are supplied. |
| `variantCountObserved` | Number of variants present in the public product record. |
| `variantsMayBeTruncated` | `true` when 250 variants are observed and additional variants may exist. |

Availability is a public purchase-availability signal, not an exact inventory quantity.

#### Variants, options, and images

| Field | Description |
| --- | --- |
| `variants` | Nested variant records with IDs, SKUs, barcodes, prices, availability, option values, weight, shipping, tax, inventory-management, and image information. |
| `options` | Product option names and possible values, such as Size and Color. |
| `images` | Product image URLs and available metadata, including dimensions, positions, alt text, and related variant IDs. |

`variants` and `options` are omitted when `includeVariants` is disabled. `images` is omitted when
`includeImages` is disabled.

#### Timestamps and diagnostics

| Field | Description |
| --- | --- |
| `createdAt` | Product creation timestamp supplied by the storefront. |
| `publishedAt` | Product publication timestamp supplied by the storefront. |
| `updatedAt` | Product update timestamp supplied by the storefront. |
| `scrapedAt` | Timestamp when the product record was extracted. |
| `warnings` | Machine-readable warnings about the product record. |
| `rawProduct` | Original public Shopify object, included only in `normalizedAndRaw` mode. |

### Run summary and storefront errors

In addition to the product Dataset, the **Output** tab provides:

- **Run summary:** overall status, duration, requested and successful stores, stores with results,
  products saved, whether the product limit was reached, error count, and per-store statistics.
- **Storefront errors:** a compact list containing the store URL, error category, and HTTP status
  when available.

Per-store statistics can show products received and saved, duplicates, invalid products, pages,
retries, rate limits, currency, warnings, and the final store status.

A run can be marked **partial** when it preserves valid products but cannot complete every store or
reaches a run-wide limit. One failed store does not remove products already saved from another
store.

### Automatic reliability and performance

No performance or networking knowledge is required. The Actor automatically manages:

- Catalog pagination
- Safe multi-store concurrency
- Request pacing and timeouts
- Retries with increasing delays
- Rate-limit handling
- Duplicate detection
- Repeated-page detection
- Response-size protection
- Independent store failures and partial results

These settings are intentionally automatic so users only need to choose stores, limits, compliance,
and output data.

### Data availability and limitations

The Actor can return only data exposed by a store through its public Shopify catalog.

- Draft, private, password-protected, unlisted, wholesale-only, or market-restricted products may
  not appear.
- Exact inventory quantities, product costs, private metafields, customers, and orders are not
  public catalog data and are not returned.
- Reviews, ratings, sales estimates, collections, and analytics are not extracted.
- Availability indicates whether an observed variant can be purchased; it is not an inventory
  count.
- Public Shopify product data can expose at most 250 variants for a product. Records at that
  boundary are marked with `variantsMayBeTruncated: true`.
- Headless or heavily customized Shopify storefronts may not expose a compatible public catalog.
- Non-Shopify platforms such as WooCommerce, Magento, BigCommerce, Amazon, or custom stores are not
  supported.
- Currency may depend on the storefront market and visitor context. It remains `null` when it
  cannot be confirmed.
- Very active catalogs can change during a run, so results are not a transactional snapshot.
- A store may temporarily block, throttle, redirect, or restrict catalog access.

### Frequently asked questions

#### Does the Actor return every product from a Shopify store?

It returns the products made available through the store's public Shopify catalog, up to the limits
you select. Private, hidden, draft, password-protected, or market-restricted products are not
available.

#### Does one Dataset row represent a product or a variant?

One row represents one product. When variants are enabled, all observed variants are nested in the
product's `variants` array.

#### Can I scrape multiple Shopify stores in one run?

Yes. Add up to 100 URLs to `startUrls`. Each output item includes `storeUrl` and `storeDomain`, so
results can be grouped by store.

#### Why did the run return fewer products than my limit?

The selected value is a maximum, not a guaranteed count. The public catalog may be smaller, contain
duplicates or invalid records, be restricted, or become unavailable. The run can also stop at the
run-wide product limit or your maximum spending limit. Check the run summary for the final status
of each store.

#### Why is currency `null`?

Currency detection is separate from product extraction. If the storefront does not expose a
verifiable currency context, the Actor keeps the products and returns `null` instead of guessing.

#### Does `available: true` show how many units are in stock?

No. It means at least one observed variant is publicly reported as available for purchase. Shopify
does not expose exact inventory quantities in the public product catalog.

#### What happens if one store fails?

Other stores continue independently. Products already saved remain in the Dataset, and the summary
identifies the failed or partial store.

#### Am I charged when a store fails or returns no products?

No. Charges are created only for unique products successfully saved to the Dataset.

#### Which output mode should I choose?

Use `normalized` unless you specifically need the original Shopify object. It is smaller and easier
to compare between stores and runs. Choose `normalizedAndRaw` when retaining all available source
fields is more important than Dataset size.

#### Can I export the results?

Yes. Use the Dataset export controls to download JSON, CSV, Excel, XML, or RSS.

### Responsible use

Use the data only for purposes you are authorized to perform. Comply with storefront terms,
`robots.txt` preferences, intellectual-property rights, privacy requirements, and applicable laws.

# Actor input Schema

## `startUrls` (type: `array`):

Public Shopify storefront homepages or localized storefront URLs.

## `maxProductsPerStore` (type: `integer`):

Stop each storefront after this many unique products.

## `maxTotalProducts` (type: `integer`):

Hard run-wide limit across every storefront.

## `respectRobotsTxt` (type: `boolean`):

Skip storefronts whose robots.txt rules disallow the product feed.

## `fetchStorefrontContext` (type: `boolean`):

Read the public cart context once per store to attach its presented currency.

## `includeVariants` (type: `boolean`):

Include variant prices, SKUs, availability, and options.

## `includeImages` (type: `boolean`):

Include product image metadata and URLs.

## `includeDescriptionHtml` (type: `boolean`):

Keep the original HTML description in addition to plain text.

## `outputMode` (type: `string`):

Normalized provides a stable schema. Normalized + raw also preserves the source product object.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://partakefoods.com"
    }
  ],
  "maxProductsPerStore": 5,
  "maxTotalProducts": 10,
  "respectRobotsTxt": true,
  "fetchStorefrontContext": true,
  "includeVariants": true,
  "includeImages": true,
  "includeDescriptionHtml": true,
  "outputMode": "normalized"
}
```

# Actor output Schema

## `products` (type: `string`):

Normalized product records saved by this run.

## `summary` (type: `string`):

Store-level progress, coverage, limits, and failure categories.

## `errors` (type: `string`):

Bounded machine-readable errors for storefronts that could not be processed.

# 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 = {
    "startUrls": [
        {
            "url": "https://partakefoods.com"
        }
    ],
    "maxProductsPerStore": 5,
    "maxTotalProducts": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("datascraperes/shopify-products-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 = {
    "startUrls": [{ "url": "https://partakefoods.com" }],
    "maxProductsPerStore": 5,
    "maxTotalProducts": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("datascraperes/shopify-products-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "startUrls": [
    {
      "url": "https://partakefoods.com"
    }
  ],
  "maxProductsPerStore": 5,
  "maxTotalProducts": 10
}' |
apify call datascraperes/shopify-products-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=datascraperes/shopify-products-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/acts/SQTCvsleNUWaaev7C/builds/5nmwELHKnmKPLff6E/openapi.json
