Skip to content

Shopify Products API & Scraper

Rocketyard’s Shopify Products API & Scraper exports the whole public catalog of a Shopify store and returns one clean record per product: store, title, handle, product id, description, vendor and more. It runs on the Apify platform, so you can start it from the Apify Console, the REST API, an AI agent or a no-code tool.

  • E-commerce brands and agencies watching competitor catalogs and prices
  • Developers of price trackers, product feeds and comparison sites
  • Analysts building e-commerce market datasets
  • AI developers who need product data as a tool or in a pipeline
  • Export a Shopify store’s whole catalog to a spreadsheet or database
  • Track prices, discounts and sold-out variants on many stores every day
  • Pull only the products that changed since the last run

Stores on Shopify’s standard storefront publish their catalog as JSON at /products.json and /collections/<handle>/products.json, with no login or API key. The Actor reads those pages over plain HTTP, 250 products per request and one request per second, until the catalog ends or your limit is reached. It honours each store’s robots.txt and crawl-delay, waits and retries a bounded number of times when a store asks it to slow down, and turns the JSON into typed records: prices as numbers, dates in ISO 8601, descriptions as clean text. The currency comes from the store’s own answer for those prices. A store that refuses, or that isn’t on Shopify, gets an error item that says why, and isn’t charged.

A real record from a test run on tentree.com on 2026-11-05. Fields the page didn’t publish are left out here; the Actor returns them as null.

{
"url": "https://tentree.com/products/slogan-peak-hat-deep-olive-i-like-your-dog",
"type": "Product",
"store": "tentree.com",
"productId": 8143518761146,
"title": "Slogan Peak Hat",
"handle": "slogan-peak-hat-deep-olive-i-like-your-dog",
"description": "Show them what's on your mind with these fun, embroidered caps — each of which is made using organic cotton that's super soft and good for our planet.",
"vendor": "tentree",
"productType": "Accessories",
"tags": [
"accessories",
"baseball-cap",
"best-sellers",
"dirty-deals",
"final-sale",
"gh-unisex-slogan-peak-hat-meteorite-black-overthinking",
"gift-guide-25-all",
"gifts-best-sellers-26"
],
"options": [
{
"name": "Color",
"values": [
"DEEP OLIVE I LIKE YOUR DOG"
]
},
{
"name": "Size",
"values": [
"ONE"
]
}
],
"priceMin": 36,
"priceMax": 36,
"compareAtPrice": 45,
"onSale": true,
"discountPercent": 20,
"currency": "USD",
"available": true,
"variantCount": 1,
"availableVariantCount": 1,
"variants": [
{
"id": 44904928411834,
"sku": "TAU5925-5831-ONE",
"title": "DEEP OLIVE I LIKE YOUR DOG / ONE",
"options": {
"Color": "DEEP OLIVE I LIKE YOUR DOG",
"Size": "ONE"
},
"price": 36,
"compareAtPrice": 45,
"available": true,
"weightGrams": 300,
"requiresShipping": true,
"taxable": true,
"image": "https://cdn.shopify.com/s/files/1/2341/3995/files/Green-Organic-Cotton-Low-Profile-Embroidered-Baseball-Cap-TAU5925-5831_1.jpg?v=1749230208",
"updatedAt": "2026-11-05T18:07:35.000Z"
}
],
"image": "https://cdn.shopify.com/s/files/1/2341/3995/files/Green-Organic-Cotton-Low-Profile-Embroidered-Baseball-Cap-TAU5925-5831_1.jpg?v=1749230208",
"images": [
{
"url": "https://cdn.shopify.com/s/files/1/2341/3995/files/Green-Organic-Cotton-Low-Profile-Embroidered-Baseball-Cap-TAU5925-5831_1.jpg?v=1749230208",
"width": 960,
"height": 1200,
"variantIds": [
44904928411834
]
},
{
"url": "https://cdn.shopify.com/s/files/1/2341/3995/files/Green-Organic-Cotton-Low-Profile-Embroidered-Baseball-Cap-TAU5925-5831_3.jpg?v=1749230208",
"width": 960,
"height": 1200,
"variantIds": []
}
],
"publishedAt": "2026-10-28T02:51:19.000Z",
"createdAt": "2025-06-06T17:16:44.000Z",
"updatedAt": "2026-11-05T18:07:35.000Z",
"sourceUrl": "https://tentree.com",
"scrapedAt": "2026-11-05T18:07:35.372Z"
}
Field Description Filled in latest test
store Store domain, without www. 100%
title Product title 100%
handle The product’s URL name in the store 100%
productId Shopify product ID 100%
description Description as clean text, with HTML removed 100%
vendor Vendor or brand, as the store enters it 100%
productType Product type, as the store enters it 100%
tags Tags, as a list 100%
options Option names and their values, such as Size: S, M, L. Empty for products with no options 73%
priceMin Lowest variant price, as a number 100%
priceMax Highest variant price, as a number 100%
compareAtPrice Earlier price of the most discounted variant, only when it is above that variant’s price. Variant records: this variant’s 9%
onSale True when a variant has an earlier price above its price. Variant records: this variant 100%
discountPercent Discount of the most discounted variant, in percent, worked out from the two prices. Variant records: this variant 9%
currency ISO 4217 code of these prices, as the store named it in the same response. Empty when the store did not name one 100%
available True when a variant can be bought in the market read, false when none can. A flag, not a stock count. Variant records: this variant 100%
variantCount Number of variants 100%
availableVariantCount Number of variants that can be bought 100%
variants Every variant: id, sku, title, options (name to value), price, compareAtPrice, available, weightGrams, requiresShipping, taxable, image, updatedAt. Left out of variant records 100%
image Main image URL. Variant records: the image of this variant when it has one 100%
images Every image: url, width, height, variantIds 100%
publishedAt Published (ISO 8601, UTC) 100%
createdAt Created (ISO 8601, UTC) 100%
updatedAt Last updated (ISO 8601, UTC). Variant records: this variant’s 100%
sourceUrl The store or collection the record was read from 100%
variantId Variant records only: Shopify variant ID 0%
variantTitle Variant records only: variant title, such as “Black / M”. Empty for products with no options 0%
sku Variant records only: SKU, when the store sets one 0%
selectedOptions Variant records only: this variant’s options, such as {“Size”: “M”} 0%
price Variant records only: this variant’s price, as a number 0%
weightGrams Variant records only: weight in grams, when the store sets one 0%
requiresShipping Variant records only: whether it ships 0%
taxable Variant records only: whether it is taxed 0%
url the page the record came from 100%
scrapedAt ISO timestamp of the extraction 100%

A field is filled when the source publishes it. The Actor never guesses a value: a missing field comes back as null. “Filled in latest test” is the share of records from the latest test run (tentree.com) that had the field; other sources publish more or less.

You pay $0.50 per 1,000 products, plus a start fee of $0.00005 per run. Products removed by your filters are not charged, and neither are inputs that fail. Each stored record is one result, so with Variant records each variant counts.

Products Cost
1,000 $0.50
10,000 $5.00
100,000 $50.00

Set maxItems or the run’s Maximum cost per run in Apify to cap spending.

Every Apify Actor is also a REST endpoint, so Shopify Products API & Scraper works as a Shopify products API, a Shopify product data API, a Shopify catalog API or a Shopify scraper API. Call it from any language with the Apify REST API, or with Apify’s JavaScript and Python clients. You need an Apify API token.

Terminal window
curl -s -X POST "https://api.apify.com/v2/acts/rocketyard~shopify-products-scraper/run-sync-get-dataset-items" \
-H "Authorization: Bearer $APIFY_TOKEN" \
-H 'content-type: application/json' \
-d '{"startUrls":[{"url":"https://muji.us"}],"stores":["deathwishcoffee.com"],"country":"US","inStockOnly":true,"maxItems":1000}'
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('rocketyard/shopify-products-scraper').call({"startUrls":[{"url":"https://muji.us"}],"stores":["deathwishcoffee.com"],"country":"US","inStockOnly":true,"maxItems":1000});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
from apify_client import ApifyClient
client = ApifyClient("<APIFY_TOKEN>")
run = client.actor("rocketyard/shopify-products-scraper").call(run_input={"startUrls":[{"url":"https://muji.us"}],"stores":["deathwishcoffee.com"],"country":"US","inStockOnly":true,"maxItems":1000})
items = client.dataset(run["defaultDatasetId"]).list_items().items

The synchronous endpoint returns the records directly. For long crawls, start the run asynchronously and read the dataset when it finishes.

Apify’s MCP server exposes this Actor as a tool to Claude, Cursor and other MCP clients. Open this Actor’s MCP setup page for setup steps per client, or connect it with this URL:

https://mcp.apify.com?tools=rocketyard/shopify-products-scraper

Claude Code:

Terminal window
claude mcp add --transport http apify "https://mcp.apify.com?tools=rocketyard/shopify-products-scraper"

Cursor (.cursor/mcp.json) and other clients that read an mcpServers config:

{
"mcpServers": {
"apify": { "url": "https://mcp.apify.com?tools=rocketyard/shopify-products-scraper" }
}
}

On first use your browser opens so you can sign in to Apify. Then ask the agent, for example: “Export a Shopify store’s whole catalog to a spreadsheet or database”. The tool returns JSON records, and the runs are billed to that Apify account at $0.50 per 1,000 products.

n8n, Make and Zapier all connect to Apify. Each example below uses Actor rocketyard/shopify-products-scraper with this input:

{
"startUrls": [
{
"url": "https://muji.us"
}
],
"stores": [
"deathwishcoffee.com"
],
"country": "US",
"inStockOnly": true,
"maxItems": 1000
}

Copy this workflow and paste it onto an n8n canvas. It runs the Actor every morning through an HTTP Request node and returns the records as n8n items.

{
"name": "Shopify Products API & Scraper daily",
"nodes": [
{
"parameters": {
"rule": {
"interval": [
{
"field": "days",
"daysInterval": 1,
"triggerAtHour": 7
}
]
}
},
"id": "c1f6a2d0-4a57-4b5e-9d7a-0a1b2c3d4e01",
"name": "Every day at 07:00",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.2,
"position": [
0,
0
]
},
{
"parameters": {
"method": "POST",
"url": "https://api.apify.com/v2/acts/rocketyard~shopify-products-scraper/run-sync-get-dataset-items",
"authentication": "genericCredentialType",
"genericAuthType": "httpBearerAuth",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "{\n \"startUrls\": [\n {\n \"url\": \"https://muji.us\"\n }\n ],\n \"stores\": [\n \"deathwishcoffee.com\"\n ],\n \"country\": \"US\",\n \"inStockOnly\": true,\n \"maxItems\": 1000\n}",
"options": {
"timeout": 300000
}
},
"id": "c1f6a2d0-4a57-4b5e-9d7a-0a1b2c3d4e02",
"name": "Run Shopify Products API & Scraper",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
260,
0
],
"credentials": {
"httpBearerAuth": {
"name": "Apify API token"
}
}
}
],
"connections": {
"Every day at 07:00": {
"main": [
[
{
"node": "Run Shopify Products API & Scraper",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {}
}

Then open the HTTP Request node and create an HTTP Bearer Auth credential named Apify API token, with your token from Apify Console as the bearer token. The token stays in n8n’s credential store; the workflow only names the credential. Change the input in the node’s JSON body to your own.

The node waits up to 300 seconds, which is also the limit of Apify’s synchronous endpoint. An input that yields nothing comes back as an item with an error field, which you can drop with a Filter node. Apify also publishes a community node, @apify/n8n-nodes-apify, with a Run an Actor and get dataset operation.

  1. Add Apify’s Run an Actor module and connect your Apify account.
  2. Choose rocketyard/shopify-products-scraper, paste the input above and set Run synchronously to Yes. Make limits how long a synchronous run can take, depending on your plan. For longer runs, start the scenario from Watch Actor Runs instead.
  3. Add Get Dataset Items with the run’s default dataset ID.
  4. Send the items wherever you need them, for example with Google Sheets Add a Row, mapping store, title, handle and productId.
  1. Add Apify’s Run Actor action, connect your Apify account, choose rocketyard/shopify-products-scraper and paste the input above.
  2. Zapier stops synchronous runs after 30 seconds. For anything larger than a small run, let the run continue in the background and start a second Zap from the Finished Actor run trigger.
  3. Add Fetch dataset items with the run’s dataset ID, then map store, title, handle and productId into your app.
  4. Test with a small input and check that the Zap handles every record before you turn it on.
  • On 2026-11-05, 11 of 14 well-known stores served their catalog. Two run Shopify behind their own front end or don’t publish the feed, and one refused with a bot check.
  • No inventory quantities and no barcodes: /products.json doesn’t publish them. available is the store’s in-stock flag, not a count.
  • Shopify serves one listing up to an offset of 25,000 products (HTTP 400 after page 100). A larger store returns those 25,000 and a free HTTP_ERROR item that says so. Add its collection URLs to reach more; a product in several inputs is charged once.
  • Sites that run Shopify behind their own front end usually don’t serve the feed, and return a free NO_STRUCTURED_DATA item, like any site not on Shopify.
  • Some stores refuse automated reads or rate-limit them. Those return a free BLOCKED item. The Actor never logs in, solves a check or disguises itself, and a proxy will not change a refusal aimed at the feed itself.
  • A store selling in several markets answers for one of them, and prices, currency and available all follow it. Without Market country that is the market of where the run comes from, so set it to compare runs. On 2026-11-05, one store showed 0 of 26 variants available to a visitor in Kazakhstan, and 37 of 51 with US.
  • A product URL is found by reading its store or collection until the product turns up. The reading is free, but on a large catalog it takes a minute or two.

$0.50 per 1,000 products plus $0.00005 per run, billed by Apify. 10,000 products cost $5.00. There is no subscription, and you can cap any run with Maximum cost per run.

Your Apify plan sets your discount tier (Platinum and Diamond are enterprise tiers):

Tier Per 1,000 products
Free $0.50
Bronze $0.50
Silver $0.40
Gold $0.33
Platinum $0.33
Diamond $0.33

One request starts a run and returns its results, using your Apify API token:

Terminal window
curl -X POST "https://api.apify.com/v2/acts/rocketyard~shopify-products-scraper/run-sync-get-dataset-items" -H "Authorization: Bearer $APIFY_TOKEN" -H "Content-Type: application/json" -d '{"startUrls":[{"url":"https://muji.us"}],"stores":["deathwishcoffee.com"],"country":"US","inStockOnly":true,"maxItems":1000}'

The dataset gets one free error item for it, with a code and the reason, and the other inputs in the run carry on. Error codes explains each code, whether a retry helps and what to do.

Stores on Shopify’s standard storefront, which is most Shopify stores. You can check any store by opening https://<store>/products.json in a browser: if you see JSON with a products list, the Actor can read it.

The field comes back as null. The Actor reports what the source publishes and never fills in guesses.

The Actor reads only the catalog a store publishes for anyone at /products.json, honours robots.txt, sends no cookies, and doesn’t work around any refusal. The feed holds product data and no customer data. Some stores’ terms of service restrict automated access, so check the terms of the stores you read. You are responsible for how you use the data, including copyright in descriptions and photos.

Yes. Connect Apify’s MCP server as shown in MCP, and the agent can call the Actor as a tool.

See how it compares with other shopify product scrapers on price, users and run success, from Apify Store data. Need a field or site behaviour that isn’t covered? Open an issue on the Actor page.