Shopify Scraper
Pricing
$19.99/month + usage
Shopify Scraper
Shopify Scraper extracts product data from Shopify stores. It collects product names, prices, descriptions, images, variants, availability, and product URLs. Ideal for e-commerce research, competitor monitoring, price tracking, and building product datasets.
Pricing
$19.99/month + usage
Rating
0.0
(0)
Developer
Scraper Engine
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
13 days ago
Last modified
Categories
Share
Shopify Scraper — Products, Variants, Prices and Images
Shopify Scraper turns any Shopify storefront URL — a homepage, a collection, or a single product page — into structured JSON: product title, brand, every variant with SKU and price, stock status, and full image sets, with no Shopify Admin API key or Partner account required. Point it at one or more stores and results land in the dataset as each product is collected. Paste a store URL and run it to see the shape for yourself.
What is Shopify Scraper?
Shopify Scraper is an Apify Actor that reads a Shopify store's own public product JSON — the same data every visitor's browser loads when it renders a storefront, collection, or product page — and normalizes it into one JSON row per product. It needs no Shopify Admin API key, no Partner account, and no login: only the store's URL. It's built for competitor-price monitoring, catalogue migration, market research, and feeding Shopify product data into search indexes or LLM pipelines.
What Shopify product data is publicly available to scrape?
Everything a normal Shopify storefront shows an anonymous visitor — title, description, price, variants, images, and tags — is returned by Shopify Scraper; nothing that requires a login or a store-owner token is.
| Data Category | Publicly Available | Restricted |
|---|---|---|
| Title, description, vendor/brand | ✅ Yes | — |
| Price and compare-at price | ✅ Yes | — |
| Variant options, SKU | ✅ Yes | — |
| Stock status | ✅ Yes, as in-stock / out-of-stock | Exact stock quantity — Shopify's public product JSON exposes only an availability flag, never a count |
| Product images | ✅ Yes | — |
| Tags / categories | ✅ Yes | — |
| Full catalogue of a normal store | ✅ Yes | Password-protected (private or pre-launch) stores — blocked entirely until the store owner removes the password |
| Customer reviews | ❌ Not exposed | Reviews come from a separate review app (not Shopify's own product data) and aren't part of the product JSON this Actor reads |
Shopify Scraper only returns publicly visible data — what any visitor sees. Nothing behind a login wall.
What data can I extract with Shopify Scraper?
Shopify Scraper returns identity fields for each product, a full variant list with pricing and stock, and every image plus the product's option types.
Product identity and descriptive fields
| Field | Description |
|---|---|
source.id | Shopify's numeric product ID. |
source.canonicalUrl | The product page URL, built from the store domain and product handle. |
source.retailer | The store's domain. |
source.currency | Currency read from the product's own variant data; defaults to USD if not present. |
source.createdUTC | Product creation timestamp, as published by the store. |
source.updatedUTC | Product last-updated timestamp, as published by the store. |
source.publishedUTC | Product publish timestamp, as published by the store. |
title | Product title. |
description | Product description, as raw HTML. |
brand | The product's vendor field. |
categories | The product's Shopify tags, split into a list. |
tags | Same values as categories (Shopify has no separate tags/categories distinction in the public product JSON). |
Variant and price fields
| Field | Description |
|---|---|
variants[].id | Shopify's numeric variant ID. |
variants[].title | Variant title (e.g. US 9 / Blue). |
variants[].sku | Variant SKU. |
variants[].options | The variant's option values (e.g. ["US 9", "Blue"]), one per option axis the product defines. |
variants[].price.current | Current price, as an integer in the currency's minor unit (cents) — 8999 = 89.99. |
variants[].price.previous | Compare-at price, same minor-unit format. Returns 0 (not null) when the store has not set a compare-at price. |
variants[].price.stockStatus | InStock or OutOfStock. |
Media and option fields
| Field | Description |
|---|---|
medias[].id | Index of the image within the product's image list. |
medias[].type | Always Image. |
medias[].url.src | The image's CDN URL. |
medias[].url.width / medias[].url.height | Image dimensions. |
medias[].url.alt | The image's alt text, if the store set one. |
medias[].url.id / medias[].url.product_id / medias[].url.position | Shopify's own image ID, parent product ID, and display position. |
medias[].url.created_at / medias[].url.updated_at | Image timestamps, as published by the store. |
medias[].url.variant_ids | Variant IDs the image is tied to, if the store linked it to specific variants. |
medias[].variantIds / medias[].alt | ⚠️ Always [] and "" at this outer level — the actual variant links and alt text live one level down, at medias[].url.variant_ids and medias[].url.alt. |
options[].type | The option's name (e.g. Size, Color). |
options[].values[].id / options[].values[].name | Each possible value for that option. Both carry the same string — Shopify's public product JSON has no separate value ID. |
🤖 Add-on: Need additional Shopify data?
For full-catalogue exports at scale, pair this with Shopify Products Scraper, which is built specifically around bulk variant/SKU extraction. For the store's own profile — address, contact details, storefront metadata rather than products — use Shopify Store Scraper. Both live in the same account as this Actor.
How does Shopify Scraper differ from the official Shopify API?
Shopify does publish official APIs — the Admin API and the Storefront API — but both require an access token issued by the specific store you're calling, generated via a custom app or the Headless sales channel set up by that store's own admin (shopify.dev, checked 2026-08-15; shopify.dev, checked 2026-08-15). Neither API gives you a way to read a store you don't own or manage.
| Feature | Shopify Admin / Storefront API | Shopify Scraper |
|---|---|---|
| Access requires | A token issued by that specific store's own admin | Just the store's public URL |
| Works on stores you don't own or manage | No — the token is scoped to the one store that issued it | Yes — reads any Shopify store's own public product JSON |
| Setup before first request | Create/install a custom app or enable the Headless channel, then generate a token | Paste a URL into Start URLs |
| Multiple stores in one call | Not applicable — one token per store | One run, multiple startUrls, each store handled independently |
| Output shape across different stores | Depends on each store's own API version and schema | Same normalized JSON fields (variants, medias, price.current, …) for every store |
If you own or manage the store, the Admin/Storefront API is the right tool — it also gives you write access and data no public visitor can see. If you need product data from stores you don't control, that's what Shopify Scraper is for.
How to use Shopify Scraper
Shopify Scraper runs as an Apify Actor — no separate signup or API key beyond your Apify account.
- Open Shopify Scraper on the Apify Store and click Try for free (or run it directly from your own Apify Console).
- Paste one or more Shopify URLs into Start URLs — the only required input.
- Optionally set Max products per URL, Max total requests, and a Proxy configuration.
- Click Start.
- Open the run's Dataset (Output tab) to view, filter, and export results as JSON, CSV, Excel, or the other formats Apify's dataset export offers.
How to scale to bulk product extraction
startUrls accepts a list, not a single value — mix store homepages, collection URLs, and direct product URLs in one run, and each is auto-detected and routed to the right scrape mode. ⚠️ Note that Max total requests (maxRequestsPerCrawl) is applied per start URL, not summed across the run: with three start URLs and maxRequestsPerCrawl set to 50, each URL can independently return up to 50 products, not 50 combined. To bound the whole run's total output, use Max products per URL on each individual URL instead.
What can you do with Shopify product data?
- 🏢 Retail analysts monitor
variants[].price.currentandvariants[].price.stockStatusacross competitor stores to catch price changes and restocks before they show up anywhere else. - 📊 Category managers pull
categories,tags, andbrandacross a full collection to map assortment gaps against their own catalogue. - 🛍️ Migration teams export
title,description,variants, andmediasfrom an existing store to seed a new theme or a different platform. - 🔍 Price-comparison builders aggregate
source.retailer,variants[].sku, andvariants[].price.currentacross many stores into one comparison feed. - 🤖 AI engineers feed
title,description, andvariantsstraight into a RAG index or a shopping agent's tool call, without writing a Shopify JSON parser first.
How does Shopify Scraper handle rate limits and blocking?
Every request first goes out direct. On a blocking signal (HTTP 403 or 503, or a connection/timeout error), the Actor escalates to an Apify Proxy datacenter pool, and if that's also blocked, to a residential pool — retrying up to 3 times on residential with an increasing delay before giving up on that one request. Once a run escalates to residential proxy, it stays on residential for the rest of that run rather than dropping back to direct. A 429 response gets a short wait and a retry on the current proxy tier rather than an immediate escalation. Concurrency is capped at 10 simultaneous requests, with a connection pool limit of 100 total / 30 per host. Requests that exhaust retries are skipped — no row is pushed to the dataset and nothing is charged for them; the run log states which URL that happened on.
⬇️ Input
| Parameter | Required | Type | Description | Example Value |
|---|---|---|---|---|
startUrls | Yes | array (string list) | Shopify store homepages, collection URLs, or product URLs, one per line. | ["https://boat-lifestyle.com"] |
maxResults | No | integer | Maximum products to scrape per start URL. Default 100, minimum 0. Set to 0 to remove this per-URL cap — the run then falls through to maxRequestsPerCrawl below. | 100 |
maxRequestsPerCrawl | No | integer | Cap on requests, applied per start URL (see the bulk-extraction note above), used only when maxResults is 0. Default 0, minimum 0. 0 + 0 together means a genuinely unlimited run. | 0 |
proxyConfiguration | No | object | Optional Apify Proxy configuration. Leave the default unless you need the run to go out through a specific proxy selection. | { "useApifyProxy": true } |
Example input
{"startUrls": ["https://boat-lifestyle.com","https://store.com/collections/shoes","https://store.com/products/blue-sneakers"],"maxResults": 100,"maxRequestsPerCrawl": 0,"proxyConfiguration": { "useApifyProxy": true }}
⬆️ Output
Each dataset row is one product, in the same normalized JSON shape regardless of which store it came from — pushed as soon as it's scraped, not batched at the end of the run. Download it as JSON, CSV, Excel, or any other format Apify's dataset export supports.
Example output
{"source": {"id": "8123456789","canonicalUrl": "https://boat-lifestyle.com/products/blue-sneakers","retailer": "boat-lifestyle.com","currency": "USD","createdUTC": "2024-01-10T12:00:00-05:00","updatedUTC": "2026-07-15T09:15:00-04:00","publishedUTC": "2024-01-10T12:00:00-05:00"},"title": "Blue Sneakers","description": "<p>Lightweight everyday sneaker with breathable mesh upper.</p>","brand": "Boat Lifestyle","categories": ["Footwear", "Sneakers"],"tags": ["Footwear", "Sneakers"],"variants": [{"id": "44123456789","title": "US 9 / Blue","sku": "BL-SNK-9-BLU","options": ["US 9", "Blue"],"price": {"current": 8999,"previous": 11999,"stockStatus": "InStock"}}],"medias": [{"id": "0","type": "Image","url": {"id": 34567890123,"product_id": 8123456789,"position": 1,"created_at": "2024-01-10T12:00:05-05:00","updated_at": "2024-01-10T12:00:05-05:00","alt": "Blue Sneakers side view","width": 1200,"height": 1200,"src": "https://cdn.shopify.com/s/files/1/0000/0001/products/blue-sneakers.jpg","variant_ids": []},"variantIds": [],"alt": ""}],"options": [{ "type": "Size", "values": [{ "id": "US 9", "name": "US 9" }] },{ "type": "Color", "values": [{ "id": "Blue", "name": "Blue" }] }]}
How does it work?
Shopify Scraper requests each store's own public product JSON endpoints — the same /products/{handle}.json and /collections/{handle}/products.json files a browser loads when it renders a storefront — rather than parsing rendered HTML for product data. Requests go out direct first, escalating through Apify Proxy's datacenter and residential pools if a store starts blocking. Before scraping, it checks whether the storefront currently requires a password, so a private or pre-launch store is reported as such instead of returning an indistinguishable zero-product result. For a full-store run without a collection URL, it reads the store's own /collections/all listing first and, if that's unavailable, discovers other collections from links on the homepage. Because the output is built from Shopify's own product JSON keys rather than scraped HTML text, the schema stays the same across every store regardless of storefront theme.
Integrations
Shopify Scraper runs on the Apify platform, so it works with anything that can call the Apify API or trigger an Apify run.
Calling Shopify Scraper programmatically
from apify_client import ApifyClientclient = ApifyClient("<YOUR_APIFY_API_TOKEN>")run_input = {"startUrls": ["https://boat-lifestyle.com"], "maxResults": 100}run = client.actor("shopify-scraper").call(run_input=run_input)for item in client.dataset(run["defaultDatasetId"]).iterate_items():print(item["title"], item["brand"])
Works in Go, Ruby, Node.js, cURL — any language that can make an HTTP request to the Apify API.
No-code tools (n8n, Make, LangChain)
In n8n, use the HTTP Request node (or Apify's own community node) pointed at this Actor's run endpoint, authenticated with your Apify API token. In Make, the Apify app's "Run an Actor" module does the same. In LangChain or a comparable agent framework, wrap the Apify API call as a tool and let the agent pass startUrls at call time.
⚖️ Is it legal to scrape Shopify product data?
Scraping publicly available product data — the same information any store visitor can see in their browser — is generally permitted; the more relevant constraints are the target store's own terms of service and, in some jurisdictions, database-rights protections over a compiled catalogue, not personal-data law. Shopify Scraper returns only publicly available data: no login, no password-protected store content, no customer accounts. This is product/business data, not personal data, so GDPR and CCPA do not apply to the fields this Actor returns. Consult legal counsel if your use case involves bulk storage or redistribution of scraped catalogue data, or if you plan to scrape at a scale or frequency a store's terms of service restrict.
❓ Frequently asked questions
What Shopify product fields does Shopify Scraper return?
title, brand, variants[].price.current, variants[].price.stockStatus, and medias[].url.src are the fields most runs use first. See What data can I extract above for the full field list.
Does Shopify Scraper require a Shopify account or login?
No. It reads each store's own public product JSON with no Admin API key, Partner account, or login. The one exception is a storefront that itself requires a password (a private or pre-launch store) — that store is skipped and reported in the run log, since it has no publicly readable catalogue at all.
How many products can I extract in one run?
As many as maxResults (default 100 per start URL) allows, or unlimited if you set maxResults to 0 and leave maxRequestsPerCrawl at its default 0 too. There's no fixed maximum built into the Actor beyond your own input values.
What happens if a product is delisted or out of stock?
An out-of-stock variant still returns normally, with variants[].price.stockStatus set to OutOfStock. A product that's been fully delisted or whose URL no longer resolves to Shopify's product JSON is skipped — no row is pushed for it, and it isn't counted toward your result total.
Can I scrape multiple Shopify stores at once?
Yes. startUrls accepts an array — mix store homepages, collection URLs, and product URLs from different stores in a single run, and each is scraped independently.
Does Shopify Scraper work with Claude, ChatGPT, and other AI agent tools?
It's callable as an HTTP endpoint through the Apify API by any agent framework that can make a tool call — LangChain, a custom Claude or ChatGPT tool definition, or any other orchestrator that can issue an HTTP request.
How does Shopify Scraper compare to calling Shopify's product JSON endpoints yourself?
The endpoints this Actor reads (/products.json, /collections/{handle}/products.json, /products/{handle}.json) are publicly reachable on any standard Shopify storefront, so you can call them yourself with a plain HTTP client. What Shopify Scraper adds is proxy escalation and retry handling when a store blocks you, password-gate detection, multi-URL orchestration in one run, and one normalized JSON schema across every store instead of hand-parsing each store's raw response.
Does Shopify Scraper return data in a format LLMs can use directly?
Yes. Typed, normalized JSON with consistent field names across every store and run — no HTML parsing, no CSS selectors. Pass it directly to an LLM, index it into a vector store, or feed it to an agent tool.
What happens when Shopify changes its storefront layout or anti-bot system?
Because Shopify Scraper reads Shopify's own product JSON endpoints rather than scraping rendered HTML, a storefront theme redesign doesn't change the output schema. The Actor is maintained; no specific turnaround time is promised for adapting to platform-side changes.
Can I use Shopify Scraper without managing proxies or browser infrastructure?
Yes. Proxy selection and escalation (direct → Apify Proxy datacenter → Apify Proxy residential) happen automatically inside the Actor; you only need to set a proxy configuration if you want to override the default.
Which Shopify product fields work best for AI training data and RAG indexing?
For RAG, index title, description, and tags — the high-information text fields. For structured training or feature data, variants[].price.current, variants[].price.stockStatus, and brand are the most consistently populated across products, and every field returns as a typed primitive (string, integer, or boolean), not a string you need to re-parse.
🔗 Related scrapers
| Scraper Name | What it extracts |
|---|---|
| Shopify Products Scraper | A Shopify store's full product catalogue, built around bulk variant and SKU extraction. |
| Shopify Store Scraper | A Shopify store's own profile — address and storefront metadata, not products. |
| Amazon Search Scraper | Amazon search results as structured product rows — ASIN, title, brand, price, rating. |
| Amazon Product Details Scraper | Individual Amazon product detail pages. |
| Google Play Scraper | Google Play app listings and reviews. |
💬 Your feedback
Found a bug, or a field that doesn't match what your store actually returns? Open an issue from the Actor's Issues tab in Apify Console, or reach out via the Contact developer link on the store listing — reports like this keep the Actor's field mapping accurate as Shopify's own storefront JSON evolves.