# Shopify Store Products Scraper - Any Store, Full Catalog, Cheap (`fabrikit/shopify-store-products-scraper`) Actor

Export the full product catalog of any Shopify store from its public JSON: titles, vendors, tags, images, every variant with SKU, price, compare-at price, stock and options, plus currency. Optional price and stock change tracking between runs. From US$0.50 per 1,000 products.

- **URL**: https://apify.com/fabrikit/shopify-store-products-scraper.md
- **Developed by:** [Fabrikit](https://apify.com/fabrikit) (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

from $0.50 / 1,000 product scrapeds

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 Products Scraper - Any Store, Full Catalog, Cheap

Export the **complete product catalog of any Shopify store** in minutes: titles, vendor, product type, tags, dates, images and **every variant** (SKU, price, compare-at price, stock status, options, weight), plus the store currency. Paste a domain, a collection URL or a product URL. No login, no API key.

It reads the public JSON endpoints that every Shopify storefront exposes (`/products.json`, `/collections/<handle>/products.json`, `/meta.json`), so it is fast and cheap: a 700-product store takes about 6 seconds.

**Price: US$0.50 per 1,000 records.** You can also run it as a **price and stock monitor**: in *Changes* mode it remembers the previous run and outputs only new, removed and changed products.

### What you get

- Whole catalog of one or many stores in a single run (250 products per request, automatic pagination).
- Scope by store, by collection URL or by single product URL (locale paths such as `/en-ca/` are supported).
- One record per product (with a `variants` array) or **one row per variant** for spreadsheets.
- `minPrice`, `maxPrice`, `currency` (from `meta.json`, falling back to `cart.js`).
- Optional plain-text or HTML description, optional list of each store's collections.
- **Non-Shopify sites are detected** and reported clearly per store (`not_shopify`, `shopify_restricted`, `empty`, `blocked`) instead of returning silently empty results.
- Works with Apify datacenter proxy and switches to residential automatically if a store rate-limits or blocks.

### Input

| Field | Description |
|---|---|
| `stores` | Domains or URLs, one per line. Store URL = whole catalog, `/collections/<handle>` = one collection, `/products/<handle>` = one product. |
| `mode` | `catalog` (default) or `changes`. |
| `flattenVariants` | One row per variant instead of one per product. |
| `maxItems` / `maxItemsPerStore` | Result caps (default 50). |
| `onlyAvailable` | Skip products where every variant is sold out. |
| `includeDescription`, `includeDescriptionHtml` | Add the description as text / HTML. |
| `listCollections` | Save each store's collections to the key-value store (`COLLECTIONS_<domain>`), free. |
| `changeTrackingStoreName`, `emitBaseline` | Changes mode settings (see below). |
| `proxyConfiguration` | Apify proxy (default datacenter, residential fallback is automatic). |

Example:

```json
{
  "stores": ["allbirds.com", "https://kith.com/collections/mens-footwear"],
  "maxItems": 500,
  "flattenVariants": false
}
```

### Output example

```json
{
  "store": "allbirds.com",
  "storeName": "Allbirds",
  "productId": 7340901859408,
  "handle": "womens-allbirds-flip-flop-dusty-pink",
  "title": "Women's Allbirds Flip Flop - Dusty Pink",
  "vendor": "Allbirds",
  "productType": "Shoes",
  "tags": ["allbirds::gender => womens", "shoprunner"],
  "createdAt": "2026-03-27T14:03:50-07:00",
  "updatedAt": "2026-10-13T10:05:56-07:00",
  "publishedAt": "2026-09-25T16:58:13-07:00",
  "url": "https://www.allbirds.com/products/womens-allbirds-flip-flop-dusty-pink",
  "images": ["https://cdn.shopify.com/s/files/1/1104/4168/files/A12513_PDP_LEFT.png"],
  "options": [{"name": "Size", "values": ["5", "6", "7"]}],
  "available": true,
  "minPrice": 25,
  "maxPrice": 25,
  "currency": "USD",
  "variantCount": 7,
  "variants": [
    {"id": 42146889039952, "sku": "A12513W050", "title": "5", "price": 25, "compareAtPrice": 50, "available": false, "options": {"Size": "5"}, "grams": 455, "image": null}
  ],
  "scrapedAt": "2026-10-13T17:05:58.347Z"
}
```

With `flattenVariants` each variant becomes its own record with `variantId`, `variantTitle`, `sku`, `price`, `compareAtPrice`, `variantAvailable`, `variantOptions`, `grams`, `variantImage` next to the product fields.

### Price and stock change tracking

Set `mode` to `changes` and schedule the actor (daily, hourly...). The first run stores a compact snapshot of each store in a **named key-value store** (`changeTrackingStoreName`, default `shopify-store-products-snapshots`) and outputs nothing (turn on `emitBaseline` to also get the catalog). Every later run outputs only:

- `changeType: "new"` - products that were not there before,
- `changeType: "changed"` - with `changes`: `[{variantId, sku, field, from, to}]` for `price`, `compareAtPrice`, `available`, added/removed variants and `title`,
- `changeType: "removed"` - products that disappeared (only when the whole store was scanned).

You pay only for the records that are output. Use a different snapshot store name for a separate watch-list.

### Pricing

| Event | Price |
|---|---|
| Record saved (`product-result`) | US$0.50 per 1,000 |

Plus the small standard Apify actor-start event. In Changes mode a run that finds no changes costs only the start event.

### FAQ

**Does it work on every Shopify store?** On every store that keeps the public `/products.json` feed on (the default). Password-protected stores and stores that disabled the feed are reported as `shopify_restricted`.

**Are prices in my currency?** Prices are in the store's default currency, returned in `currency`. Stores with multi-currency markets may show another currency on regional sub-paths; pass the regional URL (for example `https://store.com/en-ca/collections/all`).

**Is inventory quantity included?** The public feed only exposes in-stock / sold-out per variant (`available`), not quantities.

**How big can a run be?** Hundreds of thousands of records; the actor pages 250 products at a time.

### Use cases

Competitor price monitoring, catalog migration and backups, dropshipping research, MAP compliance checks, marketplace feeds, product data for AI and analytics.

***

### En espanol

Exporta el **catalogo completo de cualquier tienda Shopify**: titulo, marca, tipo, etiquetas, imagenes y todas las variantes (SKU, precio, precio anterior, disponibilidad, opciones, peso) mas la moneda. Pega un dominio, una URL de coleccion o de producto. Sin login ni API key. Modo **cambios**: compara con la corrida anterior (guardada en un almacen clave-valor con nombre) y entrega solo productos nuevos, eliminados o con cambios de precio/stock. Detecta sitios que no son Shopify. Precio: US$0,50 por 1.000 registros.

# Actor input Schema

## `stores` (type: `array`):

Shopify store domains or URLs, one per line. A store URL scrapes the whole catalog; a collection URL (https://store.com/collections/mens) scrapes only that collection; a product URL (https://store.com/products/handle) scrapes one product.

## `mode` (type: `string`):

Catalog: export every product. Changes: compare with the previous run of the same store (kept in a named key-value store) and output only new, removed and price/stock-changed products.

## `flattenVariants` (type: `boolean`):

Output one record per variant (product fields repeated on each row) instead of one record per product with a variants array. Counts every variant as one result. Ignored in Changes mode.

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

Total records to save across all stores (products, or variants when 'One row per variant' is on).

## `maxItemsPerStore` (type: `integer`):

Optional cap for each store, useful when you list many stores.

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

Skip products where every variant is sold out. Ignored in Changes mode.

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

Add the product description as plain text.

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

Add the original description HTML.

## `listCollections` (type: `boolean`):

Save each store's collections (handle, title, product count, URL) to a key-value store record named COLLECTIONS\_<domain>. Free, not counted as results.

## `changeTrackingStoreName` (type: `string`):

Name of the key-value store that remembers the previous run. Use the same name on every scheduled run; use a different name to track a separate watch-list.

## `emitBaseline` (type: `boolean`):

By default the first Changes run only stores the baseline and outputs nothing. Turn this on to also get every product (marked changeType 'baseline').

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

Most stores answer datacenter IPs. If a store rate-limits or blocks, the actor automatically switches to a residential proxy for the rest of the run.

## Actor input object example

```json
{
  "stores": [
    "allbirds.com"
  ],
  "mode": "catalog",
  "flattenVariants": false,
  "maxItems": 50,
  "onlyAvailable": false,
  "includeDescription": false,
  "includeDescriptionHtml": false,
  "listCollections": false,
  "changeTrackingStoreName": "shopify-store-products-snapshots",
  "emitBaseline": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

All records collected during the run

# 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 = {
    "stores": [
        "allbirds.com"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("fabrikit/shopify-store-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 = {
    "stores": ["allbirds.com"],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("fabrikit/shopify-store-products-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 '{
  "stores": [
    "allbirds.com"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call fabrikit/shopify-store-products-scraper --silent --output-dataset

```

## MCP server setup

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