How to Export Shopify Store Product Data to CSV or JSON

Uses Shopify Store Scraper — Products, Prices, Store Info

Short answer: Give the ActorForge Shopify Store Scraper a store URL, let it page through the public products.json feed, then download the dataset as CSV or JSON or load it into pandas.

If you own a Shopify store, the admin has a product CSV export. If you want the catalog of a competitor, a supplier or a store you are about to migrate, you do not have that option. What most Shopify stores do have is a public products.json feed: the same JSON catalog Shopify serves to every visitor, 250 products per page.

Paging through it yourself is doable for one store, but handling robots.txt, pacing, collections, headless storefronts that do not serve the feed, and turning nested JSON into a clean table quickly adds up. The Shopify Store Scraper does that and returns one row per product with prices, compare-at prices, discount, stock, variants and images.

This guide walks through checking a store, choosing what to export, running the export and getting the result into CSV, JSON or pandas.

Step by step

  1. Check that the store serves products.json

    Open https://the-store-domain/products.json in a browser. If you see JSON with a products array, the store works. Headless storefronts built on Hydrogen/Oxygen or a custom frontend do not serve it, and a few large brands block automated requests; the actor returns a free row explaining why in those cases, so you can also just try it.

  2. Add the store and choose the mode

    Put the store domain or any URL from the store in storeUrls; the actor takes the host, so a collection or product URL works too, as do myshopify.com domains. Keep mode as products for one row per product. Use both if you also want a single store summary row with product counts, price stats, theme and detected apps.

  3. Decide on scope and row size

    maxProductsPerStore defaults to 1,000 and goes up to 100,000; raise it for large catalogs. Use collections with handles such as mens-shoes to export only certain collections. includeVariants is on by default and adds every variant with SKU, price, compare-at price, availability, options and weight. Turn on includeBodyHtml only if you need product descriptions, because it makes rows much bigger (the price stays the same).

    {
      "storeUrls": ["allbirds.com"],
      "mode": "products",
      "collections": ["mens"],
      "maxProductsPerStore": 5000,
      "includeVariants": true,
      "includeBodyHtml": true
    }
  4. Download the dataset as CSV or JSON

    When the run finishes, its dataset can be downloaded from Apify Console or the API in CSV, JSON and other formats. Use the fields parameter to choose a tidy set of columns for a spreadsheet, and request JSON when you want the nested variants and images kept intact.

    # Export the dataset of a finished run as CSV or JSON
    DATASET_ID="your-dataset-id"
    
    curl -s -H "Authorization: Bearer $APIFY_TOKEN" \
      "https://api.apify.com/v2/datasets/$DATASET_ID/items?format=csv&clean=true&fields=store,title,vendor,productType,price,priceMax,compareAtPriceMin,discountPct,currency,available,variantCount,updatedAt,url" \
      -o products.csv
    
    curl -s -H "Authorization: Bearer $APIFY_TOKEN" \
      "https://api.apify.com/v2/datasets/$DATASET_ID/items?format=json&clean=true" \
      -o products.json
    
  5. Load it into pandas for a quick summary

    For analysis, read the product rows into a DataFrame, write the columns you need to CSV and summarize per store. The dataset can mix product and store rows in both mode, so filter on type.

    import os
    import pandas as pd
    from apify_client import ApifyClient
    
    client = ApifyClient(os.environ["APIFY_TOKEN"])
    run = client.actor("actor-forge/shopify-store-scraper").call(run_input={
        "storeUrls": ["allbirds.com", "tentree.com"],
        "mode": "products",
        "maxProductsPerStore": 1000,
    })
    rows = list(client.dataset(run["defaultDatasetId"]).iterate_items())
    products = pd.DataFrame([r for r in rows if r.get("type") == "product"])
    
    cols = ["store", "title", "vendor", "productType", "price", "priceMax",
            "compareAtPriceMin", "discountPct", "currency", "available",
            "variantCount", "updatedAt", "url"]
    products[cols].to_csv("shopify_products.csv", index=False)
    
    # Quick per-store summary
    summary = products.groupby("store").agg(
        products=("title", "count"),
        medianPrice=("price", "median"),
        onSaleShare=("onSale", "mean"),
        inStockShare=("available", "mean"),
    )
    print(summary)
    
  6. Keep the export fresh with updatedSince

    For an ongoing price and stock feed, schedule the same input daily and set updatedSince to the previous day's date (YYYY-MM-DD) or an ISO timestamp. Only products whose updated_at is on or after that moment become rows, and skipped products are never charged, so you can merge each day's changes into your master export.

Sample input

{
  "storeUrls": [
    "allbirds.com"
  ],
  "mode": "products",
  "maxProductsPerStore": 1000,
  "includeVariants": true,
  "includeBodyHtml": false
}

Sample output

Product rows come out like this, with price, compare-at price, discount and stock in the first columns:

storetitlevendorproductTypepricecompareAtPriceMindiscountPctavailablevariantCount
www.allbirds.comWomen's Allbirds Flip Flop - Dusty PinkAllbirdsShoes255050true7
www.allbirds.comMen's Canvas Runner NZ - Deep Navy Stripes (Blizzard Sole)AllbirdsShoes100true13
www.tentree.comSasquatch Peak HattentreeAccessories31.54530true1
www.deathwishcoffee.comWhite Chocolate Pistachio Ground BundleDeath Wish CoffeeBundle48.9953.999.3true1

Run it from code

cURL

curl -X POST "https://api.apify.com/v2/acts/actor-forge~shopify-store-scraper/run-sync-get-dataset-items" \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"storeUrls": ["allbirds.com"], "mode": "products", "maxProductsPerStore": 1000, "includeVariants": true, "includeBodyHtml": false}'

JavaScript (Node.js)

// Node.js 18+ (built-in fetch). Set APIFY_TOKEN in your environment.
const input = {
  "storeUrls": [
    "allbirds.com"
  ],
  "mode": "products",
  "maxProductsPerStore": 1000,
  "includeVariants": true,
  "includeBodyHtml": false
};

const res = await fetch(
  "https://api.apify.com/v2/acts/actor-forge~shopify-store-scraper/run-sync-get-dataset-items",
  {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.APIFY_TOKEN}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify(input),
  }
);
const items = await res.json();
console.log(items.length, items[0]);

Python

# pip install apify-client   (set APIFY_TOKEN in your environment)
import os
from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])
run_input = {
    "storeUrls": [
        "allbirds.com"
    ],
    "mode": "products",
    "maxProductsPerStore": 1000,
    "includeVariants": True,
    "includeBodyHtml": False
}

run = client.actor("actor-forge/shopify-store-scraper").call(run_input=run_input)
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

The run-sync-get-dataset-items endpoint waits for the run (up to 5 minutes) and returns the dataset rows directly. For longer runs, start the run asynchronously and read the dataset when it finishes, as the Python example does. Replace APIFY_TOKEN with your own token from the Apify console.

What it costs

Product rows cost $0.0003 each, so exporting a 1,000-product store is $0.30 and three such stores are $0.90. In both mode, each store row adds $0.005, so the same three stores with summaries cost $0.915.

Charged forWhenPricePer 1,000Worked example
Product resultCharged per product row delivered (with variants and images)$0.0003$0.30A 1,000-product store = $0.30; with updatedSince, unchanged products are skipped and cost nothing
Store analyzedCharged per store row in store/both mode (meta, price stats, top vendors/types, collections, theme, apps)$0.005$5100 competitor stores = $0.50; blocked, headless, robots-disallowed and non-Shopify sites are free rows
Actor Start (platform start fee)Charged when the Actor starts running. Number of events charged depends on Actor memory (one event per GB, minimum one event).$0.00005—One run at 256 MB = $0.0000125

Ready to try it? Shopify Store Scraper — Products, Prices, Store Info runs on Apify, pay per result.

Run on Apify → Read the full actor page

Tips and pitfalls

FAQ

Can I export products from a Shopify store I don't own?

Yes, as long as the store serves Shopify's public products.json feed and its robots.txt allows it. The actor reads only that public catalog data and no customer or contact data.

Does the export include product images?

Yes. Each product row includes every image URL plus a featured image. The files themselves are not downloaded; you get the URLs.

Why does my CSV have fewer products than the store's website shows?

Common reasons are the maxProductsPerStore cap (default 1,000), a collections filter, an updatedSince filter, or products that are not published. Raise the cap and clear the filters for a full export.

Can I export several stores in one run?

Yes. Add them all to storeUrls; duplicates are removed and each row carries a store column, so one CSV can hold several catalogs.

Actors used in this guide

Shopify Store Scraper — Products, Prices, Store Info

Scrape any Shopify store's products from its public products.json: title, vendor, type, tags, price range, compare-at price, stock, variants and…

$0.30 per 1,000 · product result

Related guides

How to Monitor Competitor Ads in Google Ads Transparency Center

List your competitors' domains or advertiser names, run the ActorForge Ads Transparency actor daily with only-new mode, and route the new ads into…

How to Audit Every URL in Your Sitemap and Find Orphan Pages

Run the SEO Audit Crawler with useSitemap on and maxPages above your site's size; it audits every page it can reach plus every sitemap URL, and…

All guides