Shopify Products Scraper & AI Shopping Readiness Audit
Pricing
from $0.64 / 1,000 product returneds
Shopify Products Scraper & AI Shopping Readiness Audit
Shopify products scraper and catalog export with a 0-100 AI shopping readiness audit. Every store gets a row explaining its result; can read past Shopify's 25,000-product feed limit and says if the catalogue is complete.
Pricing
from $0.64 / 1,000 product returneds
Rating
0.0
(0)
Developer
Your Evenings
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
an hour ago
Last modified
Categories
Share
Shopify Products Scraper & AI Shopping Readiness Audit turns a list of Shopify store domains into two things:
- A Shopify catalog export: one clean row per product with title, vendor (brand), product type, tags, variant count, price range, availability, SKUs, barcodes, image count, product URL and dates.
- An AI shopping readiness audit for each store: how much of the catalogue is missing the product data that AI shopping assistants and product feeds rely on (GTIN/barcode, brand, product type, a real description, image alt text, SKUs, unique titles), rolled up into a 0–100 readiness score with the top issues and example product handles to fix first.
Why this one
- Find variants with missing, invalid or duplicate barcodes across the whole catalogue. Turn on
readAllProductDetailsand every variant's barcode is checked: missing, invalid (wrong length or check digit for GTIN-8, 12, 13 or 14), or the same barcode on more than one variant. You get a status on each variant, totals in the audit row and aBARCODE-ISSUESCSV listing only the problem variants. It's read-only: nothing in the store is changed. - No run ends with unexplained zero results. Every store gets a row: its audit, or a status with a plain-English
note(not Shopify, password-protected or headless, no products, blocked, or disallowed by robots.txt). - Whole catalogues, with completeness you can check. Paging continues past short pages until the feed is really empty, and above Shopify's 25,000-product feed limit the rest comes from the store's public sitemap, collection feeds and product pages. Each audit row says whether the whole catalogue was read (
catalogueComplete,trueonly when verified) and the store's known product total; if anything was missed, the note says what and why. - Free "is this a Shopify store?" check. Paste any domain; sites that aren't Shopify return one free
not_shopifyrow. - Your limits hold, and status rows are free.
maxProductsPerStoreis a hard cap on products returned and charged per store. You pay for product rows and store audits plus the small per-run start fee; status rows are never charged. - Every image and every variant, not just a summary. Each product row lists all image URLs in position order and every variant with SKU, price, compare-at price, options and availability, and there's a one-row-per-variant view. Turn on
readAllProductDetailsto add barcodes, image alt text, weights and stock counts where the store publishes them. Nothing is estimated. - Polite by default. robots.txt (including
Crawl-delay) is respected, requests are limited to one per second per store, and the Actor backs off when a store answers HTTP 429.
Quick start
Paste this into the JSON view of the Input tab and press Start. It reads up to 20 products from one public Shopify store and returns a row per product plus the store's AI shopping readiness audit.
{"stores": ["partakefoods.com"],"maxProductsPerStore": 20}
For more ready-made inputs, see the example tasks.
Works with: Shopify stores
Runs on
Apify. Use it from Make, Zapier,
n8n or the Apify API.
It reads only the public storefront product JSON that Shopify stores serve (/products.json, /collections.json, each product's own .json and the store's public /meta.json), plus the store's public product sitemap for catalogues above 25,000 products. No login, no browser, no proxies, and no customer data.
What you can use it for
- Shopify products scraper / catalog export. Pull a competitor's or supplier's full public catalogue into a spreadsheet, a database or a price-monitoring pipeline.
- AI shopping readiness checks. See which catalogue data gaps commonly keep products from showing up well in AI shopping answers and product feeds, and which products to fix first.
- Agency and freelancer audits. Run it over a prospect list and lead with a concrete, measured data-quality finding per store.
- Product feed QA. Check GTIN, brand and product-type coverage before sending a catalogue to Google Merchant Center or other feeds.
Example tasks (ready-made inputs)
- Find Shopify variants with missing or invalid barcodes
- Export a full Shopify catalogue with all images and variants
- Export product catalogues from DTC Shopify brands
- Audit Shopify stores for AI shopping readiness
- Compare product ranges of Shopify beauty brands
- Find Shopify products missing GTIN barcodes
- Track food and drink brand prices on Shopify
What the audit checks
All rules are deterministic (no AI model is involved), so the same catalogue always gets the same score.
| Check | What counts as a problem | Measured on | Weight |
|---|---|---|---|
| GTIN / barcode | Variant has no barcode, or the barcode is not a valid GTIN-8, UPC-A (12), EAN-13 or GTIN-14 check digit | Detail sample of variants | 20 |
| Description | Product has no description or fewer than 30 words after removing HTML | All scanned products | 20 |
| Product type | Product type is blank | All scanned products | 10 |
| Vendor (brand) | Vendor is blank | All scanned products | 10 |
| Image alt text | Image has empty alt text | Detail sample of images | 10 |
| SKU | Variant SKU is blank | All scanned variants | 10 |
| Images | Product has no image | All scanned products | 10 |
| Duplicate titles | Title is identical to another product's (case and spacing ignored) | All scanned products | 10 |
Readiness score formula (version 1.0):
score = round( 100 × (1 − Σ(weight × problem% / 100) / Σ weight) )
The sums run over the checks that were measured. If a check can't be measured (for example, the detail sample is turned off), it drops out and the remaining weights are rescaled. Grades: good 90–100, fair 70–89, needs work 50–69, poor under 50. Every audit row includes a scoreBreakdown with each check's weight, problem percentage and penalty points, and topIssues ranked by how many score points each issue costs.
Why a detail sample? Shopify's catalogue-wide /products.json doesn't include variant barcodes or image alt text. Those are only in each product's own JSON, so the Actor reads that for a sample of products spread evenly across the catalogue (20 by default, up to 250). The GTIN and alt-text percentages are measured on that sample, and the sample size is in every audit row. Products in the sample have barcodeChecked: true and detailRead: true, with their barcode and alt-text fields filled. With readAllProductDetails on, every product's own JSON is read and the checks cover the whole set of products read.
What the score is not. It measures catalogue data quality only. It is not a ranking factor of any AI assistant or search engine and doesn't guarantee that products will appear in ChatGPT, Google or any other shopping surface. Those platforms also weigh eligibility, merchant settings, reviews, pricing and much else.
Barcode check: missing, invalid and duplicate barcodes
The Actor checks the barcode of every variant it reads from each product's own public JSON. With readAllProductDetails on, that's every variant of every product read. With it off, it's the detail sample only (detailSampleSize, 20 products by default). Each checked variant gets a barcodeStatus:
valid: a GTIN-8, UPC-A (12 digits), EAN-13 or GTIN-14 with a correct check digit.missing: the variant has no barcode.invalid: the barcode isn't a GTIN of those lengths, or its check digit is wrong.duplicate: an earlier variant in the same store has the same barcode (a 12-digit UPC and the same code as a 13-digit EAN count as the same).barcodeDuplicateOfnames that earlier variant, andbarcodeValidGtinstill says whether the code itself is a valid GTIN.
Where to find the results:
BARCODE-ISSUESCSV in the run's key-value store (linked from the Output tab and the run's status message): one line per problem variant, with store, product title, handle, product URL, variant ID and title, options, SKU, barcode and status. For each duplicated barcode, the first variant that has it is listed too (withvariantsWithThisBarcode), so every variant sharing a barcode is in the file. If nothing is wrong, the CSV has only its header line.- Audit row
barcodeCheck: variants checked and how many are valid, missing, invalid or duplicate, plus the number of duplicated barcodes and up to 10 examples. Thevalidcount includes the first variant of each duplicated barcode. - Variants view: every variant with its
barcodeStatus.
To check barcodes only, run with includeCatalogueRows: false and readAllProductDetails: true. You pay only the store audit (no product rows), and the product JSON requests are free. The check reads only what the store publishes and changes nothing in Shopify.
Input
| Field | What it does | Default |
|---|---|---|
stores | Shopify store domains or URLs, one per line | 3 example stores |
includeCatalogueRows | One row per product | on |
includeAudit | One readiness-audit row per store | on |
maxProductsPerStore | Stop after this many products per store (0 = whole catalogue, up to 100,000) | 250 |
readAllProductDetails | Read every product's own JSON, so every row gets barcodes, image alt text, weights and inventory fields where the store publishes them. One extra request per product (about 1 s each); raise the run timeout for big catalogues. No extra charge | off |
detailSampleSize | Products checked for barcodes and image alt text (0 = skip these two checks) | 20 |
requestTimeoutSecs | Per-request timeout; failed requests are retried 3 times with backoff | 15 |
Example input:
{"stores": ["beardbrand.com", "https://www.allbirds.com/collections/mens", "partakefoods.com"],"includeCatalogueRows": true,"includeAudit": true,"maxProductsPerStore": 250,"detailSampleSize": 20}
Paths in URLs are ignored: the whole store is read. Audit-only runs (includeCatalogueRows: false) are the cheapest way to score many stores.
Output
The dataset has three kinds of rows, told apart by recordType. The Output tab has Products, Variants (one row per variant), Readiness audits and Store status views.
A product row (recordType: "product"):
{"recordType": "product","status": "ok","store": "partakefoods.com","storeUrl": "https://partakefoods.com","productId": "7776301088866","handle": "girl-scouts-sparkables","title": "Girl Scout Sparkables™","vendor": "Partake Foods","productType": "Cookies","tags": ["no_purchase"],"variantsCount": 1,"availableVariantsCount": 1,"anyAvailable": true,"priceMin": 0,"priceMax": 0,"compareAtPriceMax": null,"currency": "USD","options": [],"skus": [],"variantsMissingSku": 1,"barcodeChecked": true,"barcodes": [],"variantsWithBarcode": 0,"variantsWithValidGtin": 0,"imageCount": 4,"imagesMissingAlt": 4,"featuredImageUrl": "https://cdn.shopify.com/s/files/1/0012/2296/7353/files/Girl_Scout_Sparkables_Partake_Cookie.png?v=1787935354","images": [{ "imageId": "35504456368226", "position": 1, "url": "https://cdn.shopify.com/s/files/1/0012/2296/7353/files/Girl_Scout_Sparkables_Partake_Cookie.png?v=1787935354", "width": 3000, "height": 3000, "alt": null, "variantIds": [] },"… 3 more images"],"variants": [{ "variantId": "43229654909026", "title": "Default Title", "position": 1, "sku": null, "barcode": null, "barcodeStatus": "missing", "barcodeValidGtin": null, "barcodeDuplicateOf": null, "price": 0, "compareAtPrice": null, "options": {}, "available": true, "grams": null, "weight": null, "weightUnit": null, "inventoryQuantity": null, "inventoryTracked": null, "sellsWhenOutOfStock": null, "requiresShipping": false, "imageId": null }],"detailRead": true,"descriptionWordCount": 120,"url": "https://partakefoods.com/products/girl-scouts-sparkables","createdAt": "2026-08-21T13:00:05.000Z","updatedAt": "2026-10-21T17:40:01.000Z","publishedAt": "2026-09-12T17:03:23.000Z"}
An audit row (recordType: "store-audit", shortened):
{"recordType": "store-audit","status": "ok","store": "partakefoods.com","storeName": "Partake Foods","currency": "USD","publishedProductsCount": 15,"catalogueComplete": true,"totalProductsKnown": 15,"totalProductsKnownSource": "products.json (read to the end)","productsRead": 15,"productSources": { "products.json": 15 },"productsScanned": 15,"variantsScanned": 28,"detailSampleProducts": 15,"pctVariantsMissingBarcode": 71.4,"pctVariantsWithoutValidGtin": 71.4,"pctProductsMissingVendor": 0,"pctProductsMissingProductType": 26.7,"pctProductsMissingOrThinDescription": 0,"duplicateTitleGroups": 0,"pctImagesMissingAlt": 100,"pctProductsWithoutImages": 0,"pctVariantsMissingSku": 17.9,"readinessScore": 71,"readinessGrade": "fair","topIssues": [{"issue": "missingGtin","description": "Variants without a valid GTIN/barcode (UPC, EAN or ISBN)","affectedPercent": 71.4,"affectedCount": 20,"outOf": 28,"unit": "variants (detail sample)","impactPoints": 14.3,"exampleHandles": ["girl-scouts-sparkables", "mini-chocolate-chip-grahams", "crunchy-ginger-snap"]}],"collectionsCount": 35,"thinDescriptionThresholdWords": 30,"scoreFormulaVersion": "1.0"}
Images and variants. images lists every product image in position order (url, width, height, variantIds). variants lists every variant with sku, price, compareAtPrice, options (for example {"Size": "M", "Colour": "Red"}), available and grams, all from the store's product feed. Some fields are only in each product's own JSON, so they are filled only when that was read (detailRead: true, which is every product with readAllProductDetails on, or the audit sample otherwise): image alt, variant barcode, weight with weightUnit, and the inventory fields.
Stock. Shopify's public feed shows only whether each variant is available. Some stores also publish a stock count in each product's own JSON. Where one is published, inventoryQuantity copies it, along with inventoryTracked and sellsWhenOutOfStock (Shopify's "continue selling when out of stock" setting). Many stores hide these, and the store controls the number, so it may be rounded, capped or out of date. When it isn't published, the field is null. Stock counts are never estimated.
One row per variant. The Variants view in the Output tab shows one row per variant. To export that shape, add unwind=variants to the dataset items API URL (for example https://api.apify.com/v2/datasets/{datasetId}/items?format=csv&unwind=variants). You're charged per product row either way.
A status row (recordType: "store") is returned, free of charge, for inputs that can't be read:
not_shopify: no Shopify product feed and no Shopify markers on the homepage.shopify_products_unavailable: the site is a Shopify store, but its public product feed is closed (password-protected, headless or blocked).blocked_by_robots: the store'srobots.txtdisallows/products.jsonor/products/for this crawler (or for all crawlers), so it was skipped without reading the catalogue.blocked: the site refused the requests (for example HTTP 429), so it couldn't be checked. Try again later.no_products,duplicate(same store as an earlier line),invalid_inputorerror(with anote).catalogue_incomplete: only whenincludeAuditis off. The catalogue couldn't be read in full, and the row carries the same completeness fields and note as the audit row would.
A run summary (status, product count, score and timing per store) is saved as SUMMARY in the run's key-value store.
Pricing
This Actor uses pay-per-event pricing. You pay for results, not for compute:
| Event | When it is charged | Price |
|---|---|---|
| Actor start | Once per run (per GB of memory; the default 256 MB run counts as one start) | $0.00005 |
| Product | Each product row returned | $0.0008 |
| Store audit | Each store readiness-audit row returned | $0.02 |
Status rows (not_shopify, shopify_products_unavailable, errors and so on) are never charged. Higher Apify plans get tier discounts (Silver −10%, Gold −20%). Prices on the Actor's Pricing tab are authoritative.
Examples at the base price:
- Audit only, 100 stores: 100 × $0.02 + $0.00005 = about $2.00.
- Catalogue and audit for 10 stores of 250 products each: 2,500 × $0.0008 + 10 × $0.02 = about $2.20.
- The default input (3 stores, 128 products when tested): 128 × $0.0008 + 3 × $0.02 = about $0.16.
The Actor respects the maximum cost per run you set. When the limit is close it stops adding product rows, still delivers the audit for the store it's working on if that fits, and says so in the status message.
Large catalogues and completeness
Shopify's public product feed lists 250 products per page and stops at page 100 (25,000 products); page 101 returns an error. Some stores also return short pages in the middle of the catalogue, so the Actor keeps paging until a page is empty rather than stopping at the first short page. It also stops if a page only repeats products it has already read.
Above 25,000 products the Actor:
- Reads the store's public product sitemap (
/sitemap.xml→sitemap_products_N.xml) to get the full list of product handles. - Reads public collection feeds (
/collections/{handle}/products.json), largest first, to pick up products the main feed didn't reach. A collection that only repeats products already read is skipped after three pages, and collections stop altogether once they find fewer new products than requests. - Reads each product that is still missing from its own public JSON (
/products/{handle}.json).
Every audit row reports completeness honestly:
| Field | Meaning |
|---|---|
catalogueComplete | true only when verified: the main feed was read to an empty page (or to the exact count in /meta.json), or every product in the store's sitemap was found. false when maxProductsPerStore, the run budget, the run timeout, robots.txt or an error stopped it. |
totalProductsKnown | The store's product total when it's known: the count read when the catalogue is complete, otherwise /meta.json (exact only below 25,000; Shopify reports 25001 for anything larger) or the sitemap. null when unknown. |
totalProductsKnownSource | Where that total came from: products.json (read to the end), meta.json or sitemap. |
productsRead, productSources | Products read, and how many came from each route. |
When the catalogue is truncated, the audit's note says so, for example "Shopify's public feed stops at 25,000 products; catalogue truncated…", with how many products the sitemap lists and how many weren't read.
Run time. Requests are rate-limited (one per second per store, or longer if robots.txt asks), so about 25,000 products take at least 100 seconds through the main feed, and anything above that takes one request per collection page or per product. The Actor's default run timeout is 300 seconds. For catalogues above about 20,000 products, raise the run timeout in the run options (in our test, 35,140 products took about 30 minutes). If time runs out, the Actor stops fetching about 45 seconds before the timeout, still delivers the audit, and marks the catalogue incomplete.
Speed and politeness
Each store's robots.txt is read first and respected: stores that disallow the product feed are skipped, disallowed paths (including collection feeds, sitemaps and product pages) are never fetched, and a Crawl-delay for this crawler or for all crawlers is honoured. Requests to each store are limited to one per second, with at most three stores in parallel, a 15-second timeout, and retries with backoff on HTTP 429 and 5xx (using Retry-After when the store sends it). In local tests (21–22 Oct 2026, 256 MB memory), a 250-product store with the default 20-product detail sample took about 22–27 seconds, a full 9,357-product catalogue took about 50 seconds, and a full 35,140-product catalogue (above the 25,000 feed limit) took about 30 minutes.
Use it from code, spreadsheets or an AI agent
-
Schedule it: Console > Schedules, for example weekly, to re-export catalogues or re-check readiness scores.
-
Google Sheets, Make, Zapier, n8n, Slack: use the Actor's Integrations tab to send each run's dataset where you need it.
-
API (Python):
from apify_client import ApifyClientclient = ApifyClient("<YOUR_APIFY_TOKEN>")run = client.actor("yourevenings/shopify-catalogue-audit").call(run_input={"stores": ["allbirds.com"]})for item in client.dataset(run["defaultDatasetId"]).iterate_items():print(item["recordType"], item.get("store"), item.get("title"), item.get("readinessScore")) -
AI agents: the Actor can be run through the Apify MCP server, so Claude, ChatGPT or Cursor agents can call it with their own input.
AI agents can pay for runs themselves
AI agents can run this Actor and pay with stablecoins such as USDC through the x402 or MPP protocols, with no Apify account needed. The agent buys a prepaid, spend-capped Apify API token from Apify's Agent General Interface (AGI) and then runs the Actor with it as normal. Prepaid tokens only run Actors that use limited permissions, and this Actor does. Agentic payments are an experimental Apify feature, and the terms (minimum amount, expiry, usage limits) are set by Apify. See Apify's docs: Let your AI agent pay for Actor runs, x402 and MPP.
FAQ
Is this an official Shopify, OpenAI or Google product? No. It's an independent tool and isn't affiliated with or endorsed by Shopify, OpenAI, Google or any store it reads. The names are used only to describe what it works with.
Where does the data come from?
From the public storefront JSON that Shopify stores serve to anyone: /products.json, /collections.json, /collections/{handle}/products.json, /products/{handle}.json and /meta.json, plus the public product sitemap for catalogues above 25,000 products. It doesn't log in, use the Admin API or read checkout, cart or customer data. From /meta.json it uses only the store name, currency and published product count; it never outputs the store's address, city or contact details.
Does it collect personal data or emails? No. It reads published product information (merchants' business data) only. It never collects customer data, reviews or email addresses.
Why is a store returned as shopify_products_unavailable?
Some Shopify stores close their public product feed (password pages, headless storefronts, or bot protection). There's nothing to read, so the row is free.
Will fixing the issues get my products into ChatGPT or Google AI shopping? Nobody can promise that. Complete GTINs, brands, product types, descriptions and alt text are the kind of structured product data AI shopping and product feeds commonly depend on, so the audit tells you where those gaps are. Inclusion and ranking are decided by each platform.
Why do barcode and alt-text percentages use a sample?
Shopify only publishes them in each product's own JSON, which means one request per product. The sample keeps runs fast and polite. Set detailSampleSize up to 250 for a fuller check.
Did I get the whole catalogue?
Check catalogueComplete on the audit row. It's true only when the Actor verified it read every product; totalProductsKnown and the note tell you what's missing when it isn't. With the default maxProductsPerStore of 250, larger stores are deliberately capped; set it to 0 for the whole catalogue (and raise the run timeout for very large stores).
How do I check if a store is on Shopify?
Paste the domain: stores that aren't Shopify return one free not_shopify row. Shopify stores that close their public product feed return a free shopify_products_unavailable row instead.
Is it legal to use this data? The Actor reads only product information that merchants publish on their public storefronts, with polite rate limits. You're responsible for using the data in line with the laws that apply to you and the terms of the sites you use it with.
Feedback
Found a store that should work, a wrong figure or a check you'd like added? Open an issue on the Issues tab with the store and run ID, and it'll be looked at quickly.