Shopify Product Scraper
Pricing
from $0.79 / 1,000 products
Shopify Product Scraper
Export any Shopify store's full catalogue to CSV, Excel or JSON: variants, SKUs, prices, sale prices, stock and images. Opens stores that answer 403, 404 or 429 to other scrapers. From $0.79 per 1,000 products. No account, no API key.
Pricing
from $0.79 / 1,000 products
Rating
0.0
(0)
Developer
Stable
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
6 days ago
Last modified
Categories
Share
Unofficial. This Actor is not affiliated with, endorsed by or sponsored by Shopify. It reads only publicly available pages, does not log in and does not bypass any access control. All product names and trademarks belong to their respective owners.
Every product from any Shopify store — including the ones that refuse
Paste store domains. Get their full catalogues as CSV, Excel or JSON: variants, SKUs, prices, sale prices, stock, images and the exact time each product last changed. No account, no API key, no browser.
About four Shopify stores in ten answer a scraper with 403, 404 or 429. This one opens them anyway, and it does it without a proxy setting or anything for you to configure.
Measured on 27 August 2026 over sixteen well-known stores: fifteen opened. Four of those only through the second route — 94% against the 69% the ordinary route gives on its own.
One row, from a real run
Taken from mejuri.com on 27 August. That store refused the ordinary route and
came back through the second one — resolvedVia says which. Trimmed only in the
variant list, from 26 down to one.
{"recordType": "product","storeDomain": "mejuri.com","storeHandle": "mejuri-us","resolvedVia": "origin-host","productId": 10385306419384,"title": "Interconnected Initial Pavé Diamond Charm","handle": "interconnected-diamond-letter-charm","productUrl": "https://mejuri.com/products/interconnected-diamond-letter-charm","vendor": "Mejuri","productType": "Charm","tags": ["Interconnected: 14k Pave", "promo eligible"],"publishedAt": "2026-08-24T00:19:19-04:00","createdAt": "2026-04-28T10:19:59-04:00","updatedAt": "2026-08-27T04:07:51-04:00","priceMin": 248,"priceMax": 248,"onSale": false,"available": true,"variantsTotal": 26,"variantsAvailable": 23,"variants": [{"variantId": 53415571783864,"title": "14k Yellow Gold / Lab Grown Diamond / D","sku": "p538750114","price": 248,"compareAtPrice": null,"onSale": false,"available": true,"grams": 1,"options": ["14k Yellow Gold", "Lab Grown Diamond", "D"]}],"options": [{ "name": "Material", "values": ["14k Yellow Gold"] },{ "name": "Stone", "values": ["Lab Grown Diamond"] },{ "name": "Letter", "values": ["A", "B", "C", "D", "E", "F", "G"] }],"imageCount": 4,"featuredImage": "https://cdn.shopify.com/s/files/1/0631/8568/2616/files/0_Interconnected_Diamond.jpg","scrapedAt": "2026-08-27T12:07:44Z"}
Fields
27 per product. 9 more on every variant. Counted, not estimated — the list below is the whole of it.
| where it came from | recordType, storeDomain, storeHandle, resolvedVia |
| the product | productId, title, handle, productUrl, vendor, productType, tags, description |
| dates | publishedAt, createdAt, updatedAt |
| money | priceMin, priceMax, onSale |
| stock | available, variantsTotal, variantsAvailable |
| every variant | variants — variantId, title, sku, price, compareAtPrice, onSale, available, grams, options |
| options | options — each option name with all of its values |
| pictures | imageCount, featuredImage, images |
| when it was read | scrapedAt |
Every row names its own store, so a run over fifty domains needs no join.
And one status row per store
{ "recordType": "store", "storeDomain": "bombas.com", "storeHandle": null,"resolvedVia": null, "status": "refused","detail": "bombas.com refused the catalogue and its own home page","currency": null, "productsReturned": 0, "pagesFetched": 0 }
A store that could not be read is named, with the reason. It is never silently missing from the output, and it is never charged for.
Why some stores refuse, and what happens then
Shopify publishes a store's catalogue at a documented address on the store's own domain. Plenty of shops close that address — the answer is 403, 404 or 429 — and for most tools that is the end of the story.
Every Shopify shop also has a second, internal address, and it is usually still
open. The catch is that it is not the brand name: gymshark.com lives at
gymsharkusa, hiutdenim.co.uk at hiut, mejuri.com at mejuri-us. Four of
six are not guessable at all.
This actor reads the store's own front page, finds the internal name there, and
fetches the catalogue from it. That costs one extra request, and only for the
stores that needed it — resolvedVia tells you which ones those were.
The order is cheapest-first, so a store that answers normally costs one request:
- the catalogue on the store's own domain
- a second catalogue path on the same domain
- the front page, to read the internal name
- the catalogue on the internal address
Filters
Applied before a row is stored, so a product removed by a filter is never charged for.
onlyAvailable | in stock only |
onlyOnSale | only products marked down |
minPrice, maxPrice | a price band |
productTypes, vendors | by type or brand |
titleContains | a word in the title |
updatedSince | only what changed since a date |
includeVariants, includeImages, includeDescription | leave out what you do not need |
updatedSince is the one that pays for itself. Give it the date of your last
run and a daily check of a shop that changes twenty products costs twenty rows,
not the whole catalogue.
What it costs
Pay per event, and there is one paid event: a stored product.
| GOLD per 1,000 | |
|---|---|
| Product | $0.79 |
The status rows are free. Filtered-out products are free. A store that could not be read is free.
- A 250-product catalogue: $0.20.
- A 2,400-product catalogue: $1.90.
- Apify's $5 of monthly free credit: about 5,000 products.
A single store's catalogue takes one or two requests and finishes in under twenty seconds. Five stores at once, one of them needing the second route, took 23 seconds in testing.
Limits
Stated plainly, because these are properties of what Shopify shows the public and no scraper talks them away.
- Some stores stay shut. The second route opens most refusals, not all. One of sixteen stayed closed in testing, and it is named in the output.
- Refusals move. A shop that answers today may refuse next week, and the reverse — several stores that were closed nine days earlier were open again on the day of the last measurement.
- Public catalogue only. No customers, no orders, no traffic, no revenue.
- A site that is not on Shopify returns nothing and says so, rather than pretending the catalogue is empty.
- On the free plan everything works at the FREE rate until Apify's monthly platform credit runs out; the run then stops cleanly and keeps what it has.