Shopify Store Product Scraper
Pricing
from $0.14 / 1,000 variants
Shopify Store Product Scraper
Pricing
from $0.14 / 1,000 variants
Rating
0.0
(0)
Developer
Galdrcode
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
a day ago
Last modified
Categories
Share
Shopify Product Scraper — One Row Per Variant
Scrape the full product catalog of any number of Shopify stores at once — one clean row per variant, with prices, discounts and stock status.
Paste in a list of domains. Get back a flat table you can open in Excel, Google Sheets, or load straight into a database. No API keys, no store login, no configuration.
Real sample output
Five actual rows from a live run against allbirds.com, gymshark.com and kith.com:
| store_domain | product_title | variant_title | sku | price | compare_at_price | discount_pct | on_sale | available |
|---|---|---|---|---|---|---|---|---|
| allbirds.com | Trino® Tubers - Navy Night | S (W5-7) | ST1UNNT101 | 4.00 | 16.00 | 75.0 | true | false |
| allbirds.com | Trino® Tubers - Navy Night | M (W8-10 / M8) | ST1UNNT102 | 4.00 | 16.00 | 75.0 | true | false |
| gymshark.com | Gymshark Everyday Holdall Small - Black | One Size | I3A1V-BB2J-OS | 44.00 | false | true | ||
| kith.com | Kith Baby & '47 for the New York Yankees Acrylic Beanie | OS | 2000376136 | 210.00 | false | true | ||
| allbirds.com | Free Returns Coverage | $0.80 | x-redo | 0.80 | 0.80 | false | true |
Note the last row: a compare-at price equal to the price is not counted as a discount. on_sale stays false and discount_pct stays empty, so your "what's discounted" filter doesn't fill up with noise.
Every row also carries vendor, product_type, tags[], options{}, image_url, grams, product_url, published_at and updated_at.
Why this one
Most Shopify scrapers return one row per product, with variants buried in a nested array. That is unusable in a spreadsheet — you cannot sort by price, filter by SKU, or pivot on size when every product is a single cell containing twenty variants.
This Actor returns one row per SKU. In the run above, 75 products became 523 rows — the rows you actually wanted.
| Bulk multi-domain | Pass 1 store or 500. Built for competitor sets and market scans, not one-off lookups. |
| Variant-level flat output | One row per SKU. Spreadsheet-ready, no post-processing, no JSON flattening step. |
| Detection + graceful skip | Non-Shopify, password-protected and bot-protected domains are reported in a run summary with a reason — never silently dropped, and never charged. |
| Discount + inventory signals | discount_pct and on_sale are computed for you, available is surfaced per variant. The analysis you were going to do in Excel is already done. |
Input
| Field | Type | Default | What it does |
|---|---|---|---|
storeUrls | array | required | Store domains. example.com, https://example.com, or any page URL on the store — anything after the host is ignored. |
maxProductsPerStore | integer | 0 | Cap per store. 0 = unlimited. Counts products, not variants. |
includeOutOfStock | boolean | true | When off, out-of-stock variants are dropped and not charged. |
onlyOnSale | boolean | false | When on, returns only discounted variants. Everything else is dropped and not charged. |
includeImages | boolean | true | Adds image_url — the variant's own image, falling back to the product's first. |
maxConcurrency | integer | 5 | Requests in flight across all stores. Each store paginates one page at a time. |
proxyConfiguration | object | off | Usually unnecessary — the product feed has no anti-bot. Turn on only for stores behind a checkpoint. |
The example input is prefilled with three real stores, so the Actor produces output on the first click without you typing anything.
Output
One row per variant in the dataset. Alongside it, a run summary is written to the key-value store under RUN_SUMMARY:
{"stores_requested": 4,"stores_scraped": 2,"stores_skipped": 2,"total_variants": 31,"scraped": [{ "store_domain": "allbirds.com", "currency": "USD", "products": 3, "variants": 28 }],"skipped": [{ "store_domain": "example.com", "reason": "not_shopify" },{ "store_domain": "bombas.com", "reason": "bot_protected" }]}
Skip reasons: not_shopify, password_protected, bot_protected, empty_catalog, robots_disallowed, request_failed.
How it works, and its limits
The Actor reads Shopify's public products.json feed — the same data the storefront serves to anyone. No login is bypassed and no customer data is touched. robots.txt is respected and each store is paginated sequentially to stay polite.
Known limits, stated up front:
- 25,000 products per store. Shopify's public feed refuses to page beyond this. Larger catalogs are truncated.
- No
barcode. It is not in the public feed — it exists only in Shopify's authenticated Admin API. No scraper readingproducts.jsoncan return it. currencycomes from/meta.json. If a store blocks that endpoint, currency isnulland everything else still works.- No collection membership. Shopify's feed does not carry it, and deriving it costs one request per collection — real stores have 250+, so it is neither cheap nor complete. Left out rather than shipped half-working.
- Password-protected stores return nothing by design. They are reported, not charged.
Pricing
Pay-per-event. You are charged per variant row actually returned to you.
Because this Actor returns a row per SKU rather than per product, compare the per-catalog total rather than the headline unit price — a store with 300 products and 5 sizes each is 1,500 rows here versus 300 elsewhere, for the same underlying data at finer granularity.
Filtered-out variants (includeOutOfStock: false, onlyOnSale: true) and every skipped domain cost nothing.
Other Actors
- Dubai Property Data Enricher — property listing enrichment for the Dubai market.
Found a store that should work but doesn't? Open an issue on the Actor — the skip reason in RUN_SUMMARY tells me exactly what to look at.