# Shopify Scraper – Products, Prices, Variants & Stock Monitor (`scrapeworkshq/shopify-scraper`) Actor

Scrape any Shopify store: all products, variants, SKUs, prices, compare-at (sale) prices, discount %, stock availability, vendor, type, tags, images and description. Store, collection or product URLs. Price & stock change monitor. No login, no proxy.

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

## Pricing

Pay per event

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 Scraper – Products, Prices, Variants & Stock Monitor for Any Shopify Store

Scrape **any Shopify store** (custom domains included): every product, variant, SKU, price, compare-at (sale) price, discount %, stock availability, vendor, product type, tags, images and description. Point it at a store, a collection or a single product URL. Built-in **price & stock change monitor** – schedule it and get only what changed. No login, no proxy, no browser.

### 🚀 Quick start (60 seconds)

1. Paste one or more store URLs into **Shopify stores, collections or product URLs**, e.g. `allbirds.com`, `https://www.tentree.com/collections/sale` or `https://shop.com/products/some-handle`.
2. Optional: set filters (keywords, vendor, in-stock only, on-sale only, price range) and **Max products per store**.
3. Click **Start** and export to JSON, CSV, Excel or Google Sheets.

```json
{
  "startUrls": ["https://www.allbirds.com/collections/mens", "tentree.com"],
  "onlyOnSale": true,
  "maxProductsPerStore": 100
}
```

#### Sample output

```json
{
  "storeDomain": "www.allbirds.com",
  "productId": 4870548979792,
  "handle": "mens-tree-runners",
  "title": "Men's Tree Runner - Jet Black (White Sole)",
  "vendor": "Allbirds",
  "productType": "Shoes",
  "currency": "USD",
  "minPrice": 100,
  "maxPrice": 100,
  "compareAtPrice": null,
  "onSale": false,
  "discountPct": null,
  "available": true,
  "variantsCount": 7,
  "availableVariantsCount": 5,
  "options": ["Size"],
  "imageUrl": "https://cdn.shopify.com/...png",
  "url": "https://www.allbirds.com/products/mens-tree-runners",
  "variants": [{ "variantId": 42146889039952, "title": "8", "sku": "TR3MJBW080", "price": 100, "compareAtPrice": null, "available": true }],
  "publishedAt": "2025-03-01T10:00:00.000Z",
  "updatedAt": "2026-10-21T22:37:55.000Z"
}
```

### Why this Shopify product scraper?

- **Works on any Shopify store** – reads the store's public product feed (`/products.json`), so custom domains just work, 250 products per request.
- **Store, collection or product URLs** – scrape a whole catalog, just the `/collections/sale` page, or a watchlist of products.
- **Product or variant rows** – one row per product (with min/max price and nested variants) or one row per size/color/SKU with its own price and stock.
- **Sale & discount detection** – `compareAtPrice`, `onSale`, `discountPct` computed for you.
- **Price & stock monitor** – Monitor mode returns only new/changed products with `previousPrice` and `priceChange`; unchanged items are free.
- **Cheapest in class** – $0.75 / 1,000 products, 25% below the leading Shopify Actors.
- **Fast & polite** – plain HTTP, no browser; a 1,000-product store takes a few seconds.

### Use cases

- **Competitor price monitoring** for DTC brands and Shopify merchants – daily price/discount/stock changes per SKU.
- **Product research & dropshipping** – full catalogs with vendors, types, tags and images.
- **Market & assortment analysis** – catalog size, price ranges and discount depth across many stores.
- **Restock alerts** – variant mode + Monitor mode tells you when a size comes back in stock.
- **AI / RAG datasets** – clean product titles, descriptions and attributes.

### Input

| Field | Description |
|---|---|
| `startUrls` | Store domains, collection URLs or product URLs (required). |
| `keywords` / `excludeKeywords` | Include/exclude by words in title, tags, type, vendor. |
| `vendors`, `productTypes` | Exact vendor / product type filters. |
| `onlyAvailable`, `onlyOnSale` | In-stock only; discounted only. |
| `minPrice`, `maxPrice` | Price range in the store's currency (0 = off). |
| `outputLevel` | `product` (default) or `variant`. |
| `includeDescription` | Add description text/HTML (default on). |
| `maxProductsPerStore`, `maxResultsTotal` | Limits (0 = all). |
| `monitorMode`, `monitorKey`, `outputFormat`, `summaryWebhookUrl` | Monitoring & output options (see below). |

### Output

Product rows: `storeDomain`, `storeUrl`, `productId`, `handle`, `title`, `vendor`, `productType`, `tags`, `currency`, `minPrice`, `maxPrice`, `compareAtPrice`, `onSale`, `discountPct`, `available`, `variantsCount`, `availableVariantsCount`, `options`, `imageUrl`, `images`, `url`, `descriptionText`, `descriptionHtml`, `publishedAt`, `createdAt`, `updatedAt`, `variants[]` (`variantId`, `title`, `sku`, `price`, `compareAtPrice`, `available`, `option1-3`, `grams`, `requiresShipping`, `imageUrl`), `scrapedAt`.

Variant rows carry the same product fields plus the variant's own `variantId`, `sku`, `price`, `compareAtPrice`, `available` and a variant `url`.

Dataset views: **Overview**, **Variants** and **Changes**. A `RUN_STATS` record lists stores scraped, blocked or not Shopify.

### ⭐ Unique: Monitor mode, CSV-ready output & webhook summary

**Shopify price tracker in one setting**: schedule daily with `monitorMode: "changes"` and you get only products whose price, compare-at price or stock changed (with `previousPrice`, `priceChange`, `priceChangePct`) plus brand-new products. Unchanged products are **free**. `SUMMARY` also counts price drops/rises and products that disappeared.

- **Monitor mode** (`monitorMode`: `all` | `changes` | `onlyNew`, plus `monitorKey`) – cross-run dedupe and field-level diffs (`changeType`, `changedFields`, `changes`, `firstSeenAt`). Skipped items cost **$0**.
- **Flat / CSV-ready output** (`outputFormat: "flat"`) – one column per field, perfect for Excel and Google Sheets.
- **Run summary** – a free `SUMMARY` record in the run's key-value store (linked in the Output tab) and optionally POSTed to `summaryWebhookUrl`.

### 💰 Pricing (pay per result)

| Event | Price |
|---|---|
| Run start | $0.00005 |
| Product (or variant row) | **$0.75 / 1,000** |

25% cheaper than the most-used Shopify product Actors ($1.00 / 1,000). Filtered-out, duplicate and monitor-skipped items are never charged; stores that block their feed cost nothing.

### Notes & limitations

- Works for stores that expose Shopify's standard public product feed (the vast majority). A few large brands block it (HTTP 403) or are password-protected – these are skipped and logged, not charged.
- Inventory **quantities** aren't public on Shopify; you get per-variant **in-stock true/false**.
- Currency is detected from the store; multi-currency stores return their default currency.

### Legal & privacy

This Actor reads only **public product catalog data** that stores publish to every visitor. It does not log in, does not touch checkout, customers or orders and collects **no personal data**. You are responsible for using the data in line with applicable laws and each store's terms.

### 🔌 Integrations & API

Works with Zapier, Make, n8n, Google Sheets, Slack, Airbyte, LangChain/LlamaIndex and the Apify MCP server (AI agents).

**JavaScript**

```js
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('scrapeworkshq/shopify-scraper').call({ startUrls: ['https://www.allbirds.com'], onlyOnSale: true });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

**Python**

```python
from apify_client import ApifyClient
client = ApifyClient('<APIFY_TOKEN>')
run = client.actor('scrapeworkshq/shopify-scraper').call(run_input={"startUrls": ["https://www.allbirds.com"], "onlyOnSale": True})
items = client.dataset(run['defaultDatasetId']).list_items().items
```

**cURL (sync, returns CSV)**

```bash
curl -X POST 'https://api.apify.com/v2/acts/scrapeworkshq~shopify-scraper/run-sync-get-dataset-items?token=<APIFY_TOKEN>&format=csv' \
  -H 'content-type: application/json' -d '{"startUrls": ["tentree.com"], "outputLevel": "variant", "outputFormat": "flat"}'
```

### ❓ More FAQ

**How do I know if a site is a Shopify store?**\
Just try it – non-Shopify sites are detected, logged and skipped for free.

**Can I scrape only one collection?**\
Yes, paste the collection URL (`https://store.com/collections/<handle>`).

**How does Monitor mode work?**\
The Actor remembers the IDs and key fields of items it returned before (in a named key-value store in *your* account, keyed by `monitorKey`). Next run, each item gets `changeType` = `new` / `changed` / `unchanged` plus a `changes` diff. In *Only new* / *Only new or changed* modes, the rest is skipped **and not charged**.

**How do I get a clean CSV / Excel / Google Sheet?**\
Set **Output format → Flat / CSV-ready**. Nested objects become `dot.columns` and lists are joined with `|`.

**How do I limit cost?**\
Use the max-results inputs and Apify's *Maximum cost per run*. The Actor stops cleanly when the limit is reached.

Found a bug or need a field? Open an issue on the **Issues** tab – we usually reply within 24 h.

### Changelog

- **2026-10-21** – First release: store/collection/product URLs, product & variant rows, sale detection, Monitor mode, flat output, SUMMARY + webhook.

# Actor input Schema

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

Any Shopify store domain (allbirds.com), a collection URL (https://store.com/collections/sale) or a product URL (https://store.com/products/handle). Custom domains work – no need to know the myshopify.com address.

## `keywords` (type: `array`):

Keep products whose title, tags, type or vendor contain any of these words (case-insensitive). Empty = all.

## `excludeKeywords` (type: `array`):

Drop products whose title contains any of these, e.g. "gift card".

## `vendors` (type: `array`):

Keep only these vendors (case-insensitive exact match). Useful for multi-brand retailers.

## `productTypes` (type: `array`):

Keep only these Shopify product types, e.g. "Shoes".

## `onlyAvailable` (type: `boolean`):

Keep only products (or variants) that are available for purchase.

## `onlyOnSale` (type: `boolean`):

Keep only products with a compare-at price above the price (discounted).

## `minPrice` (type: `integer`):

0 = no minimum. In the store's currency.

## `maxPrice` (type: `integer`):

0 = no maximum. In the store's currency.

## `outputLevel` (type: `string`):

Variant rows are ideal for price/stock monitoring per SKU. Each output row is one billed result.

## `includeDescription` (type: `boolean`):

Adds descriptionText and descriptionHtml. Turn off for smaller output.

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

0 = all products.

## `maxResultsTotal` (type: `integer`):

0 = no overall limit.

## `maxConcurrency` (type: `integer`):

How many stores to fetch at once (pages within one store are fetched politely one by one).

## `monitorMode` (type: `string`):

Remembers items from previous runs (per Monitor key) and adds changeType = new/changed/unchanged plus a field-level diff. In the two 'only' modes, unchanged/old items are not output and not charged.

## `monitorKey` (type: `string`):

Name of the memory used by Monitor mode. Use a different key per schedule/task (e.g. 'competitor-a-daily'), so separate watchlists don't mix.

## `outputFormat` (type: `string`):

Flat makes CSV/Excel/Google Sheets exports one clean column per field.

## `summaryWebhookUrl` (type: `string`):

At the end of the run, POST a compact JSON summary (counts, new/changed, price drops, dataset ID) to this URL – e.g. Zapier/Make/n8n catch hook or Slack workflow. Free.

## Actor input object example

```json
{
  "startUrls": [
    "https://www.allbirds.com/collections/mens",
    "https://www.tentree.com"
  ],
  "onlyAvailable": false,
  "onlyOnSale": false,
  "minPrice": 0,
  "maxPrice": 0,
  "outputLevel": "product",
  "includeDescription": true,
  "maxProductsPerStore": 50,
  "maxResultsTotal": 0,
  "maxConcurrency": 4,
  "monitorMode": "off",
  "monitorKey": "default",
  "outputFormat": "full"
}
```

# Actor output Schema

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

All scraped items in the default dataset

## `overview` (type: `string`):

No description

## `summary` (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 = {
    "startUrls": [
        "https://www.allbirds.com/collections/mens",
        "https://www.tentree.com"
    ],
    "maxProductsPerStore": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapeworkshq/shopify-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": [
        "https://www.allbirds.com/collections/mens",
        "https://www.tentree.com",
    ],
    "maxProductsPerStore": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("scrapeworkshq/shopify-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 '{
  "startUrls": [
    "https://www.allbirds.com/collections/mens",
    "https://www.tentree.com"
  ],
  "maxProductsPerStore": 50
}' |
apify call scrapeworkshq/shopify-scraper --silent --output-dataset

```

## MCP server setup

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