# Shopify Scraper: Products, Prices & Variants API (`mrslm_data/shopify-products`) Actor

Scrape every product from any Shopify store's products.json: prices, compare-at prices, variants, SKUs, stock, images. Bulk stores, CSV/Excel/JSON.

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

## Pricing

from $0.72 / 1,000 products

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?

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 API

**Get every public product, price, variant and stock flag from any Shopify store as JSON, CSV or Excel. $0.90 per 1,000 products, no login, no browser.**

Point it at one store or a thousand. It reads each store's own public product feed, adds the store's currency, and hands you one clean row per product (or per variant if you want size/colour rows for a spreadsheet).

### Quick start

1. Press **Start**. The form is already filled with this example input, so the first run returns real rows in under a minute:

```json
{
  "stores": [
    "allbirds.com",
    "kith.com"
  ],
  "maxProductsPerStore": 50
}
```

2. Swap in your own values, or call the Actor through the Apify API, Make, Zapier, n8n or the Python/JS clients with the same JSON.
3. Download the results as JSON, CSV or Excel from the **Output** tab, or schedule the run to repeat.

### Output fields

Every row has: store, productId, handle, title, url, vendor, productType, tags, currency, priceMin, priceMax, compareAtPriceMax, onSale, available, variantCount, options, imageUrl, imageCount, createdAt, updatedAt, publishedAt, scrapedAt, optional description, and a variants list (id, title, sku, price, compareAtPrice, available, option1-3, grams). In variant mode each variant becomes its own row with variantId, variantTitle, sku, price, compareAtPrice, variantAvailable, option1-3 and weightGrams as flat columns.

*The full guide below is also published as "How to Scrape Shopify Products, Prices and Stock (products.json to CSV)".*

### What the Shopify products.json feed gives you

Every Shopify storefront answers `GET /products.json`, unless the merchant has turned it off with an app or a custom proxy. The response is a list of products. Each product has:

- the product ID, handle (the URL slug), title, vendor, product type and tags
- created, updated and published timestamps
- a list of **variants**: each size or colour combination with its own ID, SKU, price, compare-at price (the "was" price), weight and an `available` flag
- option names and values (for example *Size: 8, 8.5, 9...*)
- image URLs
- the description as HTML (`body_html`)

What it does **not** give you:

- **Stock quantity.** Shopify only exposes a yes/no `available` flag publicly. Exact inventory counts are private to the merchant.
- **Currency.** Prices are plain numbers in the store's base currency. The currency code lives in a separate file, `/meta.json`.
- **Reviews, sales counts or traffic.** None of that is in the feed.

There are also two limits that trip up almost everyone who writes their own script:

1. **Page size.** The feed returns at most 250 products per request (`?limit=250`). You have to page with `&page=2`, `&page=3` and so on.
2. **The 25,000 cap.** Shopify refuses requests where page × limit goes past 25,000 (you get HTTP 400). For stores with more than 25,000 products you need to fetch collection by collection (`/collections/{handle}/products.json`) instead.

A third, less known issue: some stores return a page with fewer than 250 products *before* the real end of the catalog. A script that stops at the first short page silently loses products. The Actor keeps paging until it gets an empty page, and skips any product ID it has already seen, so a store that ignores the `page` parameter can never make you pay for duplicates.

### How to scrape Shopify products, step by step

#### Step 1: Open the Actor and list your stores

Open [Shopify Scraper: Products, Prices & Variants API](https://apify.com/mrslm_data/shopify-products) and press **Try for free**. You need a free Apify account; the free plan includes monthly platform credit, which covers thousands of products.

In **Stores**, paste one store per line. Any of these forms work:

- `allbirds.com`
- `https://kith.com`
- any page URL from the store, such as a product or collection link (only the domain is used; set **Collection** to filter)

You can add hundreds of stores in one run. Each store is paged on its own, politely: at most a few requests at a time and about one request every 2 seconds per store.

#### Step 2: Choose product rows or variant rows

**Output mode** decides the shape of your table:

- **product** (default): one row per product, with `priceMin`, `priceMax`, `onSale`, `available`, `variantCount` and the full variants list nested inside. Best for catalog overviews and price monitoring.
- **variant**: one row per size/colour, with `sku`, `price`, `compareAtPrice`, `variantAvailable`, `option1`–`option3` and `weightGrams` as flat columns. Best for spreadsheets, SKU matching and size-level stock checks.

Variant mode produces many more rows. In our test run below, 3 products became 42 rows, because each shoe came in 14 sizes. You pay per row, so pick the mode you actually need.

#### Step 3: Limit the run (optional)

- **Collection**: a collection handle such as `mens` or `sale` fetches only that collection. This is also how you get past the 25,000-product cap on huge stores: run once per collection.
- **Max products per store**: the default is 1,000. Set `0` to take the whole catalog.
- **Max results**: a hard cap across all stores. The run stops cleanly when it is reached and you are never charged past it.
- **Include description**: adds the product description as plain text (HTML stripped, cut at 2,000 characters). Leave it off if you only need prices; it keeps the dataset small.

#### Step 4: Run it and download the data

Press **Start**. A run over two stores takes a few seconds. When it finishes, open the **Output** tab and export as CSV, Excel, JSON, XML or HTML, or fetch the dataset through the Apify API.

Every run also writes a `RUN_SUMMARY` record to its key-value store: how many products came from each store, and a plain-English reason for any store that failed ("Not a Shopify store, or its public product feed is turned off"). Failed stores cost nothing.

#### Step 5: Automate it

To track prices over time, save your input as a **Task** and add a **Schedule** (daily, hourly, any cron). Each run gets a fresh dataset. To get rows into Google Sheets, Make, Zapier or n8n, use the Apify integration for that tool: "Run Actor", then "Get dataset items". From code, the Apify Python and JavaScript clients take the same JSON input:

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("mrslm_data/shopify-products").call(run_input={
    "stores": ["allbirds.com", "kith.com"],
    "maxProductsPerStore": 50,
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["store"], item["title"], item["priceMin"], item["currency"])
```

### Real example 1: two stores, product mode

This is the Actor's default input, the same thing you get when you press Start without changing anything:

```json
{
  "stores": ["allbirds.com", "kith.com"],
  "maxProductsPerStore": 50
}
```

Result from a real run on 28 October 2026: **100 products in 5.6 seconds** (50 per store), and 100 product charges, so the cost was $0.09 plus the $0.00005 start fee. A few rows, trimmed to the main columns:

| store | title | vendor | productType | priceMin | compareAtPriceMax | onSale | available | variantCount |
|---|---|---|---|---|---|---|---|---|
| allbirds.com | Women's Allbirds Flip Flop - Dusty Pink | Allbirds | Shoes | 50 | null | false | true | 7 |
| allbirds.com | Women's Tree Dasher Relay - Blizzard/Hazy Cobalt (Clarity Blue Sole) | Allbirds | Shoes | 67 | 135 | true | false | 13 |
| allbirds.com | Women's Wool Runner Mizzle - Natural Grey (Cream Sole) | Allbirds | Shoes | 62 | 125 | true | true | 7 |
| kith.com | Salomon XT-Whisper GORE-TEX - Iron / Shadow Gray / Black Olive | Salomon | Low Top Sneakers | 165 | null | false | true | 30 |
| kith.com | Rick Owens Geobaskets - Black / Milk | Rick Owens | Boots | 840 | null | false | true | 18 |

Already useful at a glance: 13 of the 50 Allbirds products were on sale (a compare-at price above the current price), 47 were in stock in at least one size, and those 100 products held 1,284 variants between them. Kith's prices in the sample ran from $65 to $1,650, with nothing marked down.

### Real example 2: one collection, variant mode

```json
{
  "stores": ["allbirds.com"],
  "collection": "mens",
  "outputMode": "variant",
  "maxProductsPerStore": 3
}
```

Result: **3 products became 42 variant rows** in 4.5 seconds, with 42 product charges. One row:

```json
{
  "store": "allbirds.com",
  "productId": 7199602016336,
  "handle": "mens-wool-cruiser",
  "title": "Men's Wool Cruiser - Natural Grey (Natural White Sole)",
  "url": "https://allbirds.com/products/mens-wool-cruiser",
  "vendor": "Allbirds",
  "productType": "Shoes",
  "currency": "USD",
  "priceMin": 105,
  "priceMax": 105,
  "onSale": false,
  "available": true,
  "variantCount": 14,
  "variantId": 41243415609424,
  "variantTitle": "8",
  "sku": "A11723M080",
  "price": 105,
  "compareAtPrice": null,
  "variantAvailable": true,
  "option1": "8",
  "weightGrams": 685,
  "updatedAt": "2026-10-28T09:08:31-07:00",
  "scrapedAt": "2026-10-28T16:08:32.273Z"
}
```

In that run, sizes 13.5, 14 and 15 of the Natural Grey Wool Cruiser showed `variantAvailable: false`. That is the size-level stock signal resellers and competitors watch, and it only shows up in variant mode.

### What people use Shopify product data for

- **Competitor price monitoring.** Schedule a daily run over your competitors' stores and compare `price` and `compareAtPrice` with yesterday's file. A new compare-at price means a markdown; `available` flipping to false means a sell-out.
- **Catalog and assortment research.** Count products by `productType` and `vendor`, see which brands a retailer carries, and spot new launches by `publishedAt`.
- **Dropshipping and wholesale buying.** Pull a supplier's catalog with SKUs, weights and images straight into a sheet instead of copying it by hand.
- **Brand protection (MAP checks).** Brands check what price their products are listed at across many Shopify retailers.
- **Market and AI datasets.** Clean, structured product data for analysis, product-matching models or a RAG index, without writing or maintaining a scraper.

### Shopify scraper vs. doing it by hand

**Opening products.json in a browser** works for a quick look at a small store. It stops being practical when you need more than 250 products (manual paging), more than one store, a CSV, or anything on a schedule. You also get prices with no currency.

**Writing your own Python script** is a reasonable weekend project. The loop is simple; the edge cases are what take time:

- stopping correctly (empty page, not short page) and handling the 25,000 cap
- stores on custom domains, `www` redirects and stores that turned the feed off
- adding currency from `/meta.json`
- flattening variants into rows and handling `compare_at_price` that is `null`, `"0.00"` or lower than the price
- polite rate limits so you are not blocked, retries on timeouts, robots.txt
- somewhere to run it every day and store the results

**Using the Actor** costs $0.90 per 1,000 products, and those cases are already handled. It runs in the cloud on a schedule and connects to Sheets, Make, Zapier and n8n. For a store with 2,000 products checked daily, that is about $1.80 a day in product mode. If you only need one export, once, from one small store, the free browser method is fine and you do not need us.

### Pricing

You pay per row saved: **$0.90 per 1,000 products** (Apify Silver plan $0.81, Gold $0.72), plus a $0.00005 start fee per run. In variant mode each variant row counts as one product. Stores that are not Shopify, or that have their feed turned off, cost nothing.

Worked examples: 3 stores × 2,000 products = 6,000 products = **$5.40**. 10,000 products = **$9.00**. The default example run above (100 products) costs **$0.09**.

### FAQ

**Is there an official Shopify API for scraping other stores?** No. Shopify's Admin and Storefront APIs need the store owner's access token. The public `products.json` feed is the only open source of a store's catalog, and it is what this Actor reads.

**Which stores work?** Any Shopify storefront that serves its public product feed, which is most of them. If a store turned it off, the run says so for that store and carries on with the others.

**Why is there no stock quantity?** Shopify only publishes an in-stock yes/no to the public. Exact counts are private.

**How do I get all products from a store with more than 25,000?** Shopify blocks paging past 25,000 products in one feed. Run the Actor once per collection (set **Collection** to `mens`, `womens`, `sale` and so on). Each collection has its own 25,000 limit.

**Are prices in USD?** Prices are in the store's base currency, and the `currency` column (read from the store's `/meta.json`) tells you which one: USD, EUR, GBP and so on. Localised prices a store shows to visitors from other countries can differ from the base price in the feed.

**How many products per store?** All of them if you set *Max products per store* to 0. Pages of 250 are fetched politely, about one every 2 seconds per store.

**Can I schedule it?** Yes. Create a Task with your input and add a Schedule (daily, weekly, any cron). Each run gets a fresh dataset you can download or fetch via API.

**Does it work with Make, Zapier, n8n or Google Sheets?** Yes. Use the Apify app in Make, Zapier or n8n ("Run Actor", then "Get dataset items"), or the Google Sheets integration on the run's Integrations tab to append rows to a sheet automatically.

**How do I keep costs predictable?** Set *Max results*, or *Maximum cost per run* in the run options. The Actor stops cleanly at the cap and you are never billed past it.

**Is scraping Shopify stores legal?** The Actor only reads the feed the store itself publishes to every visitor without a login, follows robots.txt, keeps a polite request rate and collects no personal data. You are responsible for how you use the data: follow the site's terms, copyright and the privacy laws that apply to you.

### Is it legal?

This Actor only reads pages and feeds that the website itself publishes to every visitor without a login. It obeys each site's robots.txt, keeps a polite request rate, and never tries to get past logins, paywalls, captchas or bot checks. It does not collect personal data such as emails, phone numbers or names of private individuals. You are responsible for how you use the data: follow the source site's terms, copyright and the privacy laws that apply to you (GDPR, CCPA, etc.). This tool is independent and not affiliated with or endorsed by the platforms it reads.

# Actor input Schema

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

Store domains or URLs, one per line (e.g. allbirds.com or https://kith.com). Any page URL of the store works - we use its domain.

## `collection` (type: `string`):

Collection handle, e.g. 'mens-shoes' from /collections/mens-shoes. Leave empty for the whole catalog.

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

Product: one row per product with its variants listed inside. Variant: one row per size/colour/variant (best for price and stock tracking in spreadsheets).

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

Stop after this many products per store. 0 = whole catalog.

## `maxResults` (type: `integer`):

Hard cap on rows saved (and paid for) across all stores. 0 = no cap.

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

Adds the product description as plain text (cut to 2,000 characters).

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

Not needed for most stores. Turn on Apify Proxy if a store rate-limits you.

## Actor input object example

```json
{
  "stores": [
    "allbirds.com",
    "kith.com"
  ],
  "outputMode": "product",
  "maxProductsPerStore": 50,
  "maxResults": 0,
  "includeDescription": false,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

All results as a dataset (JSON, CSV, Excel and more via the API or Console).

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

Counts of results, targets that succeeded or failed, and the stop reason.

# 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",
        "kith.com"
    ],
    "maxProductsPerStore": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("mrslm_data/shopify-products").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",
        "kith.com",
    ],
    "maxProductsPerStore": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("mrslm_data/shopify-products").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",
    "kith.com"
  ],
  "maxProductsPerStore": 50
}' |
apify call mrslm_data/shopify-products --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,mrslm_data/shopify-products"
        }
    }
}
```

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/NHoz8NchmiDlltU0Q/builds/pGw3UoNHzUy1IQ6s6/openapi.json
