Shopify Products Scraper
Pricing
$19.99/month + usage
Shopify Products Scraper
Shopify Products Scraper extracts publicly available product data from Shopify stores, including titles, prices, variants, images, descriptions, and availability. Use it for catalog analysis, research, or monitoring with fast runs and structured results.
Pricing
$19.99/month + usage
Rating
0.0
(0)
Developer
Scraper Engine
Maintained by CommunityActor stats
0
Bookmarked
4
Total users
1
Monthly active users
3 days ago
Last modified
Categories
Share
Shopify Product Scraper — Variants, Prices, SKUs and Stock
Shopify Products Scraper reads a Shopify store's public product catalogue and returns every product with its complete variant list — SKU, per-variant price, stock status and options — plus price range, currency, vendor, tags and image URLs, as structured JSON. No login or Shopify account is required: it reads the same public JSON feed the storefront itself publishes to browsers. Point it at one or more store URLs and start the run below.
What is Shopify Products Scraper?
Shopify Products Scraper is an Apify Actor that crawls a public Shopify storefront's product catalogue and returns one row per product — with the full variant array nested inside — plus one honest coverage-summary row per store explaining exactly what was and wasn't reachable. It reads the same public products.json and products/<handle>.json feeds a browser loads, so no Shopify account, API key or store login is required. It's built for e-commerce analysts, dropshippers, competitor-pricing teams and AI engineers who need a store's catalogue as clean, typed JSON rather than scraped HTML.
What Shopify product data is publicly available to scrape?
Any Shopify store that publishes its catalogue feed exposes full product and variant data — title, price, stock and options — to any visitor's browser without a login. Order data, customer data and checkout are never public; they sit behind Shopify's own admin authentication, which this Actor never touches.
| Data category | Publicly available | Restricted |
|---|---|---|
| Product title, price, images, URL | ✅ | — |
| Full variant list (SKU, per-variant price, options, stock) | ✅ | — |
| Vendor, product type, tags | ✅ | — |
| Inventory quantity per variant | ⚠️ Only when the store chooses to publish it | Left null on stores that don't |
| Barcode per variant | ⚠️ Only when the store chooses to publish it | Left null on stores that don't |
| Product description / full HTML body | ❌ Not returned by this Actor | Present on the storefront, outside this Actor's output |
| Catalogue on a password-protected store | ❌ | Store password |
| Catalogue on a headless/custom-frontend store | ❌ | No public JSON feed exposed |
| Order, customer and checkout data | ❌ | Shopify Admin authentication (store owner only) |
Shopify Products Scraper only returns publicly visible data — what any visitor sees. Nothing behind a login, store password or admin credential.
What data can I extract with Shopify Products Scraper?
Each product row carries identity and descriptive fields, computed pricing and stock metrics, and a nested variant/option/image structure. A second row type — one per store — reports catalogue coverage.
| Field | Description |
|---|---|
type | Row type: "product" for a product row |
store_url / store_domain | The storefront this product came from |
store_status | "ok" on every product row |
product_url | Public URL of the product page |
handle | The product's URL slug |
product_id | Shopify's numeric product ID |
title | Product title |
vendor | Vendor/brand as published by the store, or null |
product_type | Store-assigned product type, or null |
tags | Array of tags |
created_at / updated_at / published_at | Store-published timestamps |
detail_enriched | true if the per-product detail record was also fetched |
scraped_at | UTC timestamp this row was collected |
💰 Pricing, stock and variant metrics
| Field | Description |
|---|---|
price | Decimal-string price of the cheapest real variant |
compare_at_price | Compare-at price of that same cheapest variant, or null |
price_min / price_max | Lowest / highest variant price as numbers |
price_varies | true when variants don't all share one price |
currency | ISO currency code, or null if the store doesn't publish one |
discount_percent | Percentage discount, only when the compare-at price is genuinely higher than price — see note below |
is_on_sale | true when discount_percent is not null |
available | true if at least one variant is in stock |
variant_count | Number of variants on the product |
in_stock_variant_count / out_of_stock_variant_count | Variant stock rollup, or null when no variant published an available flag |
availability_ratio | In-stock variants ÷ total variants with a known flag |
sku_count | How many variants carry a SKU |
discount_percentis genuinely absent, not zero, when there's no real markdown. The source code notes a live measurement where 75% of one store's variants publish a compare-at price lower than the selling price — a percentage computed from that would be negative and meaningless, so it comes backnullinstead of a fake number.
🧩 Variants, options and images
| Field | Description |
|---|---|
variants | Full array — see the Output example below for every nested key |
options | Array of { name, position, values } — the store's declared option matrix |
option_names | Flat array of option names, e.g. ["Color", "Size"] |
image_count | Number of image URLs on the product |
featured_image | First image URL, or null |
image_urls | Every image URL — URLs only, no files are downloaded |
🤖 Add-on: Need additional Shopify data?
Pair this with Shopify Merchant Scraper for a store's contact details, socials and tech stack, or Shopify Store Scraper for a store-level profile (theme, installed apps, catalogue size). For product data pulled straight from a store's homepage or collection pages instead of the catalogue feed, see Shopify Scraper.
How does Shopify Products Scraper differ from the official Shopify API?
Shopify's own Admin and Storefront APIs only return data for stores that have issued you an access token — you install an app on that store, or the store owner authorizes it via OAuth, before a single request works (shopify.dev, checked 2026-08-15). That makes them unusable for reading a catalogue you don't own or control, which is exactly what most competitor research, sourcing and market-monitoring use cases need.
| Feature | Shopify Admin/Storefront API | Shopify Products Scraper |
|---|---|---|
| Read a store you don't own | ❌ needs an access token that store issues you | ✅ reads the public JSON feed any visitor's browser gets |
| Multiple unrelated stores in one run | ❌ separate token per store | ✅ one run, any number of startUrls, independent per store |
| Rate limit (standard plan) | 2 requests/second, leaky bucket, 40-request bucket (REST); cost-based points/second by plan tier — 100 (Standard), 200 (Advanced), 1000 (Plus), 2000 (Enterprise) for GraphQL (shopify.dev, checked 2026-08-15) | Self-paced with jittered request gaps and automatic connection escalation on refusal — no fixed number to configure |
| Setup | Create/install an app, complete OAuth or generate a custom-app token, per store | Paste a store URL and run |
| Output shape | Raw REST/GraphQL objects you normalize yourself | Pre-computed rollups (price range, stock counts, SKU coverage) on every row |
Use the Admin API when the store is yours and you need write access, orders or checkout data. Use Shopify Products Scraper for read-only catalogue collection across stores you don't control.
How to use Shopify Products Scraper
- Open Shopify Products Scraper on the Apify Store and click Try for free (or Start, if you already have it saved) to open the input form in Apify Console.
- Fill in
startUrlswith at least one Shopify storefront address — a full URL, a bare domain, or a.myshopify.comaddress all work. - Optionally raise
maxItemsabove the store's catalogue size if you want the whole thing, or leave the default to sample a store first. - Leave
proxyConfigurationon its default unless you specifically need to route through a chosen Apify Proxy selection. - Click Start, then open the run's dataset and export it as JSON, CSV, Excel or the other formats Apify supports.
How to scale to bulk product extraction
startUrls is an array — add as many storefront addresses as you need in one run. Each store is crawled independently: one store returning password_protected or unreachable never stops the others from completing, and every store gets its own product rows plus its own coverage-summary row so you can tell at a glance which ones succeeded.
What can you do with Shopify product data?
- 🏷️ Competitor price monitoring — pricing analysts use
price,price_min,price_maxandcompare_at_priceto track a rival's price bands and discount depth across a catalogue. - 📦 Assortment and inventory research — merchandisers use
variant_count,in_stock_variant_countandavailability_ratioto see how deep a competitor's size/color matrix runs and how much of it is actually in stock. - 🔍 Dropship and supplier sourcing — resellers use
sku_count,vendorand the per-variantskuinsidevariants[]to shortlist suppliers with full SKU coverage before reaching out. - 📊 Market and category analysis — analysts aggregate
product_type,tagsandpriceacross many stores in one run to map price bands and category mix in a niche. - 🤖 AI agent and RAG pipelines — AI engineers feed
title,product_type,tagsandvariants[]into a vector store or agent tool so an LLM can answer product questions or compare catalogues without re-scraping. The typed JSON output is a ready tool response for any agent framework that can call the Apify API.
How does Shopify Products Scraper handle rate limits and blocking?
Requests start on a direct connection with a jittered pacing gap between calls. A request that gets a soft-refusal status (403, 406, 407, 409, 429, 451, 500, 502, 503, 504) or no response at all is retried up to 4 attempts total, with capped exponential backoff (min(2^attempt + jitter, 8s)), and the connection steps up to Apify Proxy — first a datacenter tier, then residential — the first time a store pushes back. Once it steps up it stays up for the rest of the run; escalation is monotonic and shared across all concurrent requests to that run. A 404 or 401 is treated as deterministic (dead store / password-locked) and is never retried. If you set your own proxyConfiguration, that selection is used for every request instead of the automatic ladder.
If a store's catalogue can't be completed, whatever was already collected is still returned and the store's summary row reports partial_interrupted coverage rather than silently truncating with no explanation.
⬇️ Input
| Parameter | Required | Type | Description | Example value |
|---|---|---|---|---|
startUrls | Yes | array | One or more Shopify storefront addresses, e.g. https://tentree.com or https://nuts.com. A bare domain or a .myshopify.com address works too. Each store is scraped independently and gets its own coverage summary row. | ["https://tentree.com"] |
maxItems | No | integer | Upper limit on how many products to collect from each store. Minimum 1, maximum 100000, default 10. Set it above the store's catalogue size to collect everything. | 500 |
proxyConfiguration | No | object | Optional. Leave the default unless you need the run to go out through a specific Apify Proxy selection. | {"useApifyProxy": false} |
⚠️ Shopify's own catalogue-listing endpoint silently clamps any page-size request above 250 items back down to 250 — this Actor walks it page by page rather than requesting more at once, so it is never affected, but it's why maxItems values above a store's true size return everything the store has rather than erroring.
Example input
{"startUrls": ["https://tentree.com","https://nuts.com"],"maxItems": 500,"proxyConfiguration": {"useApifyProxy": false}}
⬆️ Output
Results land in the Actor's dataset as typed JSON, exportable as JSON, CSV, Excel, XML, RSS or HTML directly from Apify Console or via the API. Two row types share the same dataset, told apart by type. Only type: "product" rows are billed, under the row_result event — filter type == "store_summary" to see the free coverage rows, or type == "product" to see only what you were charged for.
Prices are always major-unit decimal strings ("45.00", not cents) — the Actor reads only the storefront surfaces that publish prices that way.
Example output
Product row (type: "product"):
{"type": "product","is_child": false,"store_url": "https://tentree.com","store_domain": "tentree.com","store_status": "ok","product_url": "https://tentree.com/products/treeblend-baker-t-shirt-cottage-red-heather","handle": "treeblend-baker-t-shirt-cottage-red-heather","product_id": 8462674067642,"title": "TreeBlend Baker T-Shirt","vendor": "tentree","product_type": "Mens","tags": ["basics", "mens", "organic-cotton", "t-shirts-tanks"],"price": "45.00","compare_at_price": null,"price_min": 45.0,"price_max": 45.0,"price_varies": false,"currency": "USD","discount_percent": null,"is_on_sale": false,"available": true,"variant_count": 5,"in_stock_variant_count": 5,"out_of_stock_variant_count": 0,"availability_ratio": 1.0,"sku_count": 5,"variants": [{"variantId": 46195127550138,"title": "COTTAGE RED HEATHER / S","sku": "TCM1869-6298-S","barcode": "661814898315","price": "45.00","priceValue": 45.0,"compareAtPrice": null,"currency": "USD","available": true,"inventoryQuantity": null,"inventoryPolicy": null,"option1": "COTTAGE RED HEATHER","option2": "S","option3": null,"position": 1,"grams": 400,"weight": 400.0,"weightUnit": "g","requiresShipping": true,"taxable": true,"createdAt": "2026-07-24T16:29:11-07:00","updatedAt": "2026-08-08T02:58:27-07:00"}],"options": [{ "name": "Color", "position": 1, "values": ["COTTAGE RED HEATHER"] },{ "name": "Size", "position": 2, "values": ["S", "M", "L", "XL", "XXL"] }],"option_names": ["Color", "Size"],"image_count": 3,"featured_image": "https://cdn.shopify.com/s/files/1/2341/3995/files/tree_blend_baker_2.jpg","image_urls": ["https://cdn.shopify.com/s/files/1/2341/3995/files/tree_blend_baker_2.jpg"],"created_at": "2026-07-24T16:29:11-07:00","updated_at": "2026-08-08T02:58:27-07:00","published_at": "2026-08-04T07:00:03-07:00","detail_enriched": true,"scraped_at": "2026-08-08T14:21:09Z"}
Store coverage summary row (type: "store_summary") — always exactly one per store, including stores that returned nothing:
{"type": "store_summary","is_child": false,"store_url": "https://tentree.com","store_domain": "tentree.com","store_status": "ok","status_detail": "Catalogue read successfully.","catalogue_coverage": "complete","catalogue_total": 412,"products_returned": 412,"variant_total": 1863,"detail_enriched_count": 412,"currency": "USD","sku_coverage_percent": 97.31,"inventory_coverage_percent": 41.06,"scraped_at": "2026-08-08T14:22:47Z"}
store_status on the summary row is one of ok, password_protected, no_published_products, catalogue_unavailable, not_shopify, store_not_found or unreachable. catalogue_coverage is one of complete, limited_by_max_items, partial_interrupted or none. catalogue_total is populated only when catalogue_coverage is complete — if maxItems cut the run short, the store's true size is unknown and is reported as null rather than guessed.
How does it work?
Shopify Products Scraper calls the same JSON endpoints a browser loads when it visits a storefront — the catalogue listing (/products.json, paginated 250 products at a time, up to 400 pages) and, for every product found, the individual product record (/products/<handle>.json) that fills in barcode, inventory quantity and currency. No browser is launched and no JavaScript is rendered — these are direct HTTP calls against Shopify's own public data surfaces. Requests start on a direct connection and only route through Apify Proxy if the store pushes back; only publicly visible data is ever returned, nothing behind a password or admin login. Because every store publishes this feed in the same shape, the output schema stays the same regardless of which storefront theme a store is running.
Integrations
Shopify Products Scraper works with the tools you already use for automation and analysis — no plugin required, since it's just an Apify Actor reachable over the standard Apify API.
Calling Shopify Products Scraper programmatically
from apify_client import ApifyClientclient = ApifyClient("<YOUR_API_TOKEN>")run = client.actor("scraper-engine/shopify-products-scraper").call(run_input={"startUrls": ["https://tentree.com"],"maxItems": 500,})for item in client.dataset(run["defaultDatasetId"]).iterate_items():print(item["title"], item.get("price"))
Works in Go, Ruby, Node.js, cURL — any language that can make an HTTP request against the Apify API.
No-code tools (n8n, Make, LangChain)
In n8n, use the HTTP Request node pointed at the Actor's run-sync-get-dataset-items endpoint with your API token, or the dedicated Apify node if you have it installed. In Make, the Apify app's "Run an Actor" module accepts the same startUrls / maxItems input and returns the dataset items to the next module. In LangChain, the ApifyWrapper / Apify Actor tool can call this Actor and load its dataset directly as Document objects for a retrieval pipeline.
Is it legal to scrape Shopify products?
Scraping publicly available product data is generally lawful — this Actor returns only what any visitor's browser can already load from a storefront, without bypassing a login, password or paywall. Product catalogues, prices and SKUs are business/product data, not personal data, so GDPR and CCPA do not attach to this Actor's output; the relevant frameworks instead are a store's terms of service and database-rights protections that may apply to a compiled catalogue in some jurisdictions. Consult legal counsel if your use case involves bulk storage or redistribution of a store's catalogue data.
Frequently asked questions
What Shopify product fields does Shopify Products Scraper return?
The top fields are title, price (with price_min/price_max), variants (SKU, per-variant price and stock), tags and available. See What data can I extract above for the full field tables.
Does Shopify Products Scraper require a Shopify account or login?
No. It reads the storefront's public catalogue feed exactly as a browser would — no Shopify account, API key or store login is needed. A store locked behind a store password cannot be read; that's reported as password_protected rather than silently returning nothing.
How many products can I extract in one run?
Up to maxItems per store (max 100000). Each store is crawled to completion, maxItems, or the point where the catalogue feed stops answering — whichever comes first — and the coverage summary row tells you which one happened.
What happens if a product's detail record can't be fetched?
The product still comes back from the catalogue listing with detail_enriched: false. Fields only available on the detail record — barcode, inventoryQuantity, weight, and the currency sourced from that record — are null for that product, while everything from the catalogue surface (price, stock, options, images) is still populated.
Can I scrape multiple Shopify stores at once?
Yes. startUrls accepts an array of storefront addresses; each store is scraped independently in the same run and gets its own summary row, so one problem store never blocks the rest.
Does Shopify Products Scraper work with Claude, ChatGPT and other AI agent tools?
Yes, as an HTTP endpoint any agent framework can call through the Apify API — the JSON output is typed and consistent, so it drops straight into a tool call or RAG pipeline without extra parsing.
What makes Shopify Products Scraper different from a basic Shopify catalogue scraper?
It returns the full variant array on every product — not just one headline price — with SKU, price and stock per variant, and it reports an honest store status (password_protected, catalogue_unavailable, not_shopify, etc.) instead of returning an empty result when a store can't be read.
Does Shopify Products Scraper return data in a format LLMs can use directly?
Yes. Typed, normalized JSON with consistent field names across runs — no HTML parsing, no 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 a store's layout or theme?
The catalogue feed this Actor reads is Shopify's own stable JSON data surface, not the rendered storefront page, so a theme or layout change on the store doesn't change the output schema. No specific update turnaround is published for anti-bot changes on Shopify's side.
Can I use Shopify Products Scraper without managing proxies or browser infrastructure?
Yes. There's no browser to configure — requests are direct HTTP calls — and connection escalation through Apify Proxy happens automatically if a store pushes back, with no proxy setup required unless you want to pick a specific configuration yourself.
Which fields work best for AI training data and RAG indexing?
For RAG, index title, product_type, tags and vendor as descriptive text alongside variants[] for structured lookups. For training data, price, variant_count, sku_count and availability_ratio are the most consistently structured numeric fields across every row. All fields return as typed primitives — strings, numbers, booleans or arrays — never mixed types.
Related scrapers
| Scraper | What it extracts |
|---|---|
| Shopify Merchant Scraper | B2B lead sheet per store — merchant identity, emails, phones, social profiles, product/collection counts and tech stack |
| Shopify Store Scraper | Store-level profile — identity, theme, installed apps, catalogue size, collection count and price range |
| Shopify Scraper | Product data pulled from a store's homepage, collection pages or single product pages |
| Amazon Product Details Scraper | Product details from Amazon listing pages |
| eBay Product Listing Scraper | Product data from eBay search and listing pages |
| Amazon Price Tracker | Amazon prices, ratings, reviews, sellers and stock |
Your feedback
Found a bug, or missing a field you need from a Shopify store's catalogue? Open an issue on this Actor's Issues tab in Apify Console — that's the fastest way to reach the maintainer directly.