Shopify Product Image & Alt Text Inventory
Pricing
from $2.00 / 1,000 image records
Shopify Product Image & Alt Text Inventory
Audit image records and alt attributes from product exports before catalog migration. For Shopify media QA and accessibility teams. Check the live Pricing tab and input limits.
Pricing
from $2.00 / 1,000 image records
Rating
0.0
(0)
Developer
Practical AI Lab
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
a day ago
Last modified
Categories
Share
Export every product image from a store's public Shopify product feed, together with the product it belongs to, the variants it is linked to, its alt text, its reported size, and a list of quality flags. Use it to find missing images, missing or weak alt text, variants without their own picture, and images reused across products.
This Actor is built for catalog clean-up and migration prep. It is not a general product scraper: it does not export prices, descriptions or inventory, and it never downloads the image files.
What you get
One dataset, several record types (field recordType):
| recordType | Meaning |
|---|---|
image | One row per product image: product ID/handle/title/URL, image ID, position, image URL, filename, width, height, alt text, linked variant IDs/titles/SKUs, associationScope (variant or product), flags |
missing-image | A product that has no image at all (NO_IMAGES) |
variant-gap | A variant without its own image while other variants of the same product have one |
shop-summary | Per-store counts, flag totals, pages read, truncation status and a sample caveat |
error | A store that could not be read, with the reason |
Quality flags
| Flag | Rule |
|---|---|
MISSING_ALT | Alt text is empty |
SHORT_ALT | Alt text is shorter than minAltTextLength |
ALT_LOOKS_LIKE_FILENAME | Alt ends with an image extension or equals the file name (size suffixes like _800x ignored) |
DUPLICATE_ALT_IN_PRODUCT | Same alt text (case/punctuation-insensitive) on more than one image of a product |
DUPLICATE_IMAGE_IN_PRODUCT | Same image file listed twice in one product (cache query strings ignored) |
SHARED_IMAGE_ACROSS_PRODUCTS | Same image file used by several products in the sample |
MISSING_DIMENSIONS | The feed gives no width or height |
LOW_RESOLUTION | Shorter side reported by the feed is below minImageDimension |
INSECURE_URL | Image URL is not HTTPS |
TOO_FEW_IMAGES | Product has fewer than minImagesPerProduct images |
NO_IMAGES | Product has no image |
VARIANT_WITHOUT_IMAGE | See variant-gap above |
Flags are heuristics to build a work list, not verdicts. Your own accessibility or SEO guidelines decide what counts as a problem.
Input
Copy-and-run example:
{"shopUrls": ["https://www.allbirds.com"],"maxProductsPerShop": 50,"minImagesPerProduct": 3,"minImageDimension": 800,"minAltTextLength": 10,"includeProductsWithoutImages": true,"includeVariantGaps": true,"onlyFlagged": false,"requestDelayMs": 1000}
Replace the store with one you are allowed to analyse. The example store is only a placeholder; whether its feed is enabled and permitted by its robots.txt can change at any time, in which case you get an error record instead of data.
Accepted addresses: a store domain (https://example.com) or a collection (https://example.com/collections/sale). Product pages, IP addresses, custom ports, credentials and non-HTTPS URLs are rejected.
Example image record (illustrative, not a real result)
{"recordType": "image","shop": "shop.example.com","productId": 1,"productTitle": "Tee","imageId": 11,"position": 1,"imageUrl": "https://cdn.shopify.com/s/files/img11.jpg","imageWidth": 1200,"imageHeight": 1200,"altText": "","associationScope": "variant","variantIds": [101],"variantTitles": ["Red"],"variantSkus": ["R1"],"flags": ["MISSING_ALT"]}
Limitations and sample-size caveats
- Default is a sample.
maxProductsPerShopdefaults to 50 and products come in the feed's own order, which is not random. Percentages from a sample must not be generalised to the whole catalog. The summary says when more products were available (truncatedByProductLimit). The hard maximum is 5,000 products per store. - Only stores with an enabled, robots-permitted
/products.jsonfeed can be read. Password-protected stores, stores that disabled the feed, or stores that block the request return anerrorrecord. No proxies or workarounds are used. - Feed data, not the rendered page. Alt text, image order and variant links come from the feed. A theme may render different alt text or hide images, and some stores assign images to variants in ways the feed does not expose.
- Variant gaps are reported only when at least one variant has its own image. If no variant is linked to any image, all variants share the product gallery and no gap is reported.
- Resolution is the width/height the feed reports. Images are not downloaded, so blur, watermarks, wrong crops or duplicate pictures with different file names are not detected.
- Shared/duplicate image checks only compare products inside the same run and sample.
- Collection feeds list the products of that collection only.
- DNS is checked before every request, but a host that changes its DNS answer between the check and the connection cannot be fully excluded.
- robots.txt is evaluated with a built-in parser (user-agent groups,
Allow/Disallow,*and$); unusual files may be interpreted differently by other tools. A robots.txt that cannot be read (other than 404/410) skips the store. - Requests are sequential, one at a time, with a configurable delay (500 ms minimum), at most 21 feed pages per store and no retries. A 429 response stops that store.
- Public product data may still be subject to a store's terms and to copyright of the images themselves. This Actor lists image URLs; it does not grant any right to reuse the images.
Billing note
The code tags each image, missing-image and variant-gap row with the pay-per-event name image-record. No payable plan or price is configured in this project. shop-summary and error records carry no event, and a store is analysed only after it was read completely, so a store that fails is never charged for rows. Pricing must be decided and configured separately after cost checks.
Project layout
src/urlsafety.py– URL validation, public-IP check, same-host redirectssrc/robots.py– robots.txt policysrc/feed.py– bounded HTTP reader and paginationsrc/analysis.py– compaction, association and flag logicsrc/pipeline.py– one-store fetch without Apify dependenciessrc/main.py– Apify entrypointtests/– offline unit tests (fake transport and DNS, no network)
Run the offline tests from the project folder with python -m unittest discover -s tests -t ..