# Shopify Products Scraper (`bluefin/shopify-products-scraper`) Actor

Extract products, prices, variants and stock from any public Shopify store via its /products.json endpoint. Flat output, no browser. Unofficial, not affiliated with Shopify.

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

## Pricing

from $0.50 / 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.

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

### Shopify Products Scraper

*This is an unofficial tool. It is not affiliated with, endorsed by, or sponsored by Shopify Inc. "Shopify" is a trademark of Shopify Inc.*

Extract the full product catalog from any **public Shopify store** — every product and, if you want, every variant — as clean, flat rows you can download as JSON, CSV, or Excel. It reads the store's public `/products.json` endpoint, so it is fast and does not run a browser.

Point it at one or more store URLs (for example `https://www.allbirds.com`) and it walks the whole catalog page by page, then hands you titles, handles, vendors, product types, tags, prices, compare-at prices, SKUs, availability, images, and canonical product URLs. Running it on the [Apify platform](https://apify.com) adds scheduled runs, API access, proxy rotation, and integrations with Make, Zapier, Google Sheets, and more.

#### Why use this scraper?

- **Competitor and price monitoring** — track a competitor's catalog, price points, and what they mark as on sale (via `compare_at_price`).
- **Catalog research** — pull an entire store's assortment for analysis, dropshipping research, or building a product feed.
- **Availability tracking** — see which variants are in stock at scrape time.
- **Lightweight and cheap** — HTTP-only (no headless browser), so runs are quick and use little compute.

#### How to use it

1. Add one or more Shopify store URLs to **Start URLs**. You can paste the store home page (`https://store.com`) or the full `https://store.com/products.json` path — both work.
2. Optionally set **Max products per store**, toggle **Expand variants into separate rows**, or enable **Also fetch collections**.
3. Leave **Proxy configuration** on Apify Proxy (recommended — see the note under Limitations).
4. Click **Start**. When the run finishes, open the **Output** tab or export the dataset as JSON, CSV, or Excel.

#### Input

| Field                 | Type    | Default     | Description                                                                                         |
| --------------------- | ------- | ----------- | --------------------------------------------------------------------------------------------------- |
| `startUrls`           | array   | (required)  | Shopify store URLs. Home page or `/products.json` path both accepted.                               |
| `maxProductsPerStore` | integer | `0`         | Cap on products per store. `0` means no limit. Counted per product, not per variant.                |
| `includeVariants`     | boolean | `true`      | Expand every variant into its own row. When `false`, one row per product using the default variant. |
| `includeCollections`  | boolean | `false`     | Also fetch the store's public collections list into a separate `collections` dataset.               |
| `proxyConfiguration`  | object  | Apify Proxy | Proxy settings.                                                                                     |

Example input:

```json
{
    "startUrls": [{ "url": "https://www.allbirds.com" }],
    "maxProductsPerStore": 0,
    "includeVariants": true,
    "includeCollections": false,
    "proxyConfiguration": { "useApifyProxy": true }
}
```

#### Output

Each row is one product variant (or one product when `includeVariants` is `false`). Real example row:

```json
{
    "store_url": "https://greatjonesgoods.com",
    "product_id": 8410904559695,
    "handle": "big-deal-saucy",
    "title": "Big Deal & Saucy",
    "vendor": "Great Jones",
    "product_type": "Bundle",
    "tags": ["badge_Save $60!"],
    "created_at": "2026-03-16T16:05:51-04:00",
    "updated_at": "2026-08-01T19:16:49-04:00",
    "published_at": "2026-03-30T14:47:52-04:00",
    "product_url": "https://greatjonesgoods.com/products/big-deal-saucy",
    "price": "190.00",
    "compare_at_price": "250.00",
    "variant_id": 44838392430671,
    "variant_title": "Default Title",
    "sku": null,
    "available": true,
    "image_url": "https://cdn.shopify.com/s/files/1/0066/9312/6202/files/BigDeal_Saucy.png"
}
```

You can download the dataset in JSON, HTML, CSV, or Excel from the Output tab or the API.

##### Data fields

| Field                                        | Description                                                  |
| -------------------------------------------- | ------------------------------------------------------------ |
| `store_url`                                  | Origin of the store the row came from.                       |
| `product_id`                                 | Shopify product ID.                                          |
| `handle`                                     | URL slug of the product.                                     |
| `title`                                      | Product title.                                               |
| `vendor`                                     | Vendor / brand as set in Shopify.                            |
| `product_type`                               | Shopify product type.                                        |
| `tags`                                       | Array of product tags.                                       |
| `created_at` / `updated_at` / `published_at` | Product timestamps.                                          |
| `price`                                      | Variant price (string, as Shopify returns it).               |
| `compare_at_price`                           | Compare-at (list) price, or `null`.                          |
| `variant_id`                                 | Shopify variant ID (`null` if the product has no variants).  |
| `variant_title`                              | Variant name, e.g. `"Small"` or `"Default Title"`.           |
| `sku`                                        | Variant SKU, or `null` if the store did not set one.         |
| `available`                                  | Whether the variant was in stock at scrape time.             |
| `image_url`                                  | Variant image if present, otherwise the first product image. |
| `product_url`                                | Canonical product page URL.                                  |

With `includeCollections` enabled, the store's collections are written to a **named dataset called `collections`**, which you can retrieve from the API or the run's Storage tab. It holds `collection_id`, `handle`, `title`, `description`, `products_count`, `published_at`, `updated_at`, `image_url`, and `collection_url`. Note that named datasets may not appear in the Console **Output** tab (which shows the default dataset) — open the run's **Storage** tab, or the `collections` dataset via the API, to see them.

#### Pricing

This Actor uses **pay-per-event** pricing with two charges:

- **Run start** — charged once each time a run starts (Apify's built-in start event).
- **Product scraped** — charged once per product stored. Variants of the same product do **not** add extra charges, so turning variant expansion on does not increase cost.

A store with 500 products is billed as one run start plus 500 product charges, no matter how many variants those products have. So a product with 20 size/color variants is billed once, not 20 times. Check the Actor's Store page for the current per-charge amounts.

The run-start charge is counted per gigabyte of memory: it is charged once for runs up to and including 1 GB of RAM, then once more for each additional GB (for example, 4 GB of RAM is billed as 4 start charges). This Actor runs HTTP-only and sets a 1 GB default, so under the default it is a single start charge.

> **For developers / publishers — important, read before publishing.** Billing is **per product**, achieved with a single custom event named `product-result` (defined in `src/main.ts` and `.actor/pay_per_event.json`). Apify's pay-per-event model also offers two automatic "synthetic" events, and one of them is a trap for this Actor:
>
> - Use the synthetic **`apify-actor-start`** for run startup (Apify charges it automatically — do not charge it from code, that fails).
> - You **must remove** the synthetic **`apify-default-dataset-item`** event in the Monetization wizard. If left enabled, it auto-charges once per row in the default dataset — and this Actor writes one row per variant, which would bill a 20-variant product 20 times and make the "no extra charge for variants" promise above false.
>
> Pay-per-event pricing is configured in the Apify Console Monetization wizard, not in `actor.json`. Follow `PUBLISH_CHECKLIST.md` exactly, and after publishing run the billing verification step there to confirm no per-row charges appear.

#### Limitations (please read)

- **Public data only.** It reads the store's public `/products.json`, the same JSON any browser can request. It cannot access password-protected stores, unpublished products, admin-only fields, or exact inventory counts.
- **Password-protected, closed, or non-Shopify sites are skipped**, not scraped. Each skip is logged with a reason and recorded in the `SUMMARY` record in the run's key-value store. If every store is unreachable, the run fails rather than reporting a false success.
- **Some stores block datacenter IPs.** Many Shopify stores sit behind Cloudflare and rate-limit or block datacenter traffic (HTTP 429/403). Keeping **Apify Proxy** enabled (the default) rotates IPs and materially improves success rates. If a store still returns 429, try residential proxy groups.
- **No currency field.** The `/products.json` endpoint does not include a currency code, so prices are returned as-is without one.
- **No product-to-collection mapping.** With `includeCollections`, you get collection metadata (title, handle, product count), not which products belong to which collection.

#### FAQ

**Is scraping Shopify stores legal?**
This Actor only requests the store's own public `/products.json` endpoint — publicly available data. You are responsible for using the output in line with the target store's terms and applicable laws. It targets product catalog data only and does not collect customer or account data. Note that catalog fields such as `vendor` can occasionally contain a person's name (for example, a sole trader's brand), so treat the output accordingly.

**Why did a store return 0 products or get skipped?**
The store may be password-protected, closed, not a Shopify store, or blocking your IP. Check the `SUMMARY` record in the run's key-value store for the exact per-store reason, and try enabling Apify Proxy.

**Does it get every product?**
It paginates until the store returns an empty page, so it retrieves the full public catalog unless you set `maxProductsPerStore`.

**How do I report a problem?**
Use the **Issues** tab on the Actor page.

# Actor input Schema

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

One or more Shopify store URLs. You can pass the store home page (e.g. https://www.allbirds.com) or the full /products.json path; both work. Each store is scraped independently.

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

Upper limit of products fetched per store. Set to 0 for no limit (fetch the whole catalog). Counting is per product, not per variant.

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

When enabled, each variant of a product becomes its own dataset row (with its own SKU, price and availability). When disabled, only the first (default) variant is used for the price/SKU fields.

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

When enabled, the public /collections.json list for each store is also fetched and stored in a separate 'collections' dataset. This is metadata only (collection title, handle, product count), not the product-to-collection mapping.

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

Proxy settings. Apify Proxy (automatic) is recommended: many Shopify stores sit behind Cloudflare and rate-limit or block datacenter IPs, so rotating proxies materially improve success rates.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://greatjonesgoods.com"
    }
  ],
  "maxProductsPerStore": 0,
  "includeVariants": true,
  "includeCollections": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# 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://greatjonesgoods.com"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("bluefin/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://greatjonesgoods.com" }] }

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

```

## MCP server setup

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

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/acts/grLB4ENG9opSAKTpO/builds/zIhN7QUlsyTHXCDZj/openapi.json
