Paste a Shopify store URL and this tool lists every product image the storefront exposes: the full CDN URL, the pixel dimensions, and the position each image holds inside its product gallery. You get a plain text list you can copy in one click, a CSV you can open in a spreadsheet, and a thumbnail grid so you can see what you are actually looking at before you do anything with it.
Three kinds of URL work. A store root (example.com or example.myshopify.com) returns the first 250 products, with a button to pull the next page after that. A collection URL returns only the products in that collection. A single product URL returns one product and nothing else, which is the quickest way to grab one gallery without wading through a whole catalog.
Be clear on what this page is, because the name promises slightly more than the page delivers. It collects image URLs and image metadata. It does not package the JPG and PNG files themselves into a ZIP. That is a decision rather than a technical wall, and we explain the reasoning further down: pulling thousands of full size originals means putting that load on a store that never agreed to it, and repackaging other people's photography is not something we want this page doing. If you want the files from a catalog you own, CS Export Product Images pulls the actual image files out of your own store as a downloadable archive, running inside your Shopify admin where that access is authenticated. This page is the URL layer, and the URL layer turns out to be what most image jobs actually start with.
Why is a list of links worth anything on its own? Because almost every image task begins with an inventory. Migrating to a new platform, hunting for photos that were never resized, checking whether a variant has its own shot or is borrowing the product's default one, feeding a spreadsheet into a bulk download script, sending a photographer a list of what needs reshooting. None of those need the binaries in your hand on step one. They need to know what exists.
For every image you get six fields: the product handle, the product title, the image position in the gallery, the width and height in pixels as Shopify stored them, and the canonical CDN URL. That is enough to sort by size and find the 4000 pixel monsters, enough to spot products with a single lonely photo, and enough to reconcile against a product export you already have.
One thing to say plainly before you start. Product photography belongs to whoever shot it or paid for it, which is normally the store owner. Reading public URLs is not the same as having the right to reuse the images behind them. Use this on catalogs you own, or where you have permission, and treat everything else as reference material only.
| Detail | Value |
|---|---|
| Data source | The public products.json endpoint on the storefront |
| Products per request | 250, the maximum the endpoint accepts |
| Accepted input | Store root, collection URL, or single product URL |
| Fields per image | Product handle, product title, position, width, height, URL |
| Copy output | Plain text, one URL per line |
| File output | CSV built in your browser, six columns |
| Thumbnails | Shopify CDN size suffix, _200x inserted before the file extension |
| Media files per product on Shopify | Up to 250 |
| Login required | None. Only public storefront data is read |
| Not included | The image binaries themselves, and anything behind a password page |
What this tool does, and what it deliberately does not do
Every Shopify storefront publishes a JSON feed of its products at /products.json. It is public by design, it powers plenty of legitimate integrations, and it includes an images array for each product. This tool reads that feed, flattens the images out of it, and hands you the result in three shapes: copyable text, a CSV, and a visual grid.
What it will not do is zip up the photos, and we are not going to dress that up as a law of physics. Shopify's image CDN sends permissive cross origin headers on product images, so a script can read those bytes and a browser built archive is technically possible. We choose not to build one. Downloading every full size original off a storefront is real bandwidth and real load on a store that never opted in, it turns this page into a one click copier of photography that usually belongs to someone else, and our server side fetch endpoint rejects anything that is not a text response specifically so it can never be pressed into service as an image proxy.
Plenty of pages on the internet promise the archive anyway. Some quietly hand you a text file. Some run a browser extension you have to install. Some funnel every image you touch through a server you know nothing about. We would rather say where our line is and point at the better route.
That route, for your own catalog, is a tool with real admin access. CS Export Product Images is our app for exactly that job: it runs inside your Shopify admin, reads the product media through the authenticated API, and exports the files as a ZIP, with filters by collection, vendor or tag and SEO friendly filenames applied on the way out. Different access level, better result. If the store is not yours, the honest answer is that you should be asking the owner rather than looking for a clever workaround.
How to use it, step by step
The whole flow is about thirty seconds. The interesting part is what you do with the output afterwards.
- Pick your scope first. Whole store, one collection, or one product. Whole store is the default and usually what you want for an audit. A collection URL is better when you only care about one category. A product URL is right when you are debugging a single item's gallery.
- Paste the URL and run it. Protocol is optional. A bare handle with no dot in it is treated as a myshopify.com subdomain, so typing "examplestore" resolves to examplestore.myshopify.com.
- Read the count before anything else. The summary line tells you how many products came back and how many images that adds up to. If the ratio looks off, that is already a finding. Sixty products and sixty two images means almost nothing has a second angle.
- Copy the URL list or download the CSV. The text list is for pasting into a script, a download manager, or a message to a developer. The CSV is for sorting, filtering, and pivoting in a spreadsheet.
- Scan the thumbnail grid. This is where duplicates jump out. The same white background shot repeated across five colour products is obvious in a grid and invisible in a list of URLs.
- Load the next page if you have more than 250 products. The endpoint pages at 250, so a large catalog needs a few clicks. Each click appends to the same list, so the CSV always covers everything you have loaded.
What the products.json endpoint actually gives you
It helps to know what is in the feed, because it explains both the useful fields and the missing ones.
Each product carries an id, a title, a handle, a vendor, a product type, created and updated timestamps, an options array, a variants array, and an images array. Each image inside that array has a src, a width, a height, a position, and a variant_ids list showing which variants that image is attached to. Our CSV uses handle, title, position, width, height, and src, because those are the six that survive contact with a spreadsheet.
What is not there is worth noting too. Draft and unpublished products are absent, as is anything hidden from the sales channel. Password protected stores return the password page instead of JSON, which is why a locked store gives you a parse error rather than a list. Alt text is the interesting exception: the bulk feed omits it, but the single product endpoint does include an alt key on each image. Our CSV leaves it out in all three modes so the six columns always mean the same thing, and because a store wide alt audit is a different job. That job belongs to the Product Image Audit Checklist, and if what you need is help writing the text rather than checking it, the Image Alt Text Generator drafts it per variant.
The variant_ids field is the one people underestimate. It is the raw truth about which photo belongs to which variant, and on most stores it is thinner than merchants expect. Shopify links one featured image to a variant. Everything else in the gallery is attached to the product as a whole, which is exactly why a shopper who picks the green version still scrolls past every other colour on the page. If you want to see that mapping laid out per variant rather than per image, you need something that reads variants and not just images. On the storefront side, Rubik Variant Images assigns a separate image set to every variant so the gallery filters instead.
Reading the CSV columns
Six columns, no surprises, but each one earns its place.
| Column | What it holds | What to do with it |
|---|---|---|
| product_handle | The URL slug of the product | Join against a product export, or rebuild the product URL as /products/handle |
| product_title | The storefront title | Human readable grouping and sorting |
| image_position | 1 for the first gallery image, then 2, 3 and so on | Filter to position 1 to get one hero shot per product |
| width | Stored pixel width | Sort descending to find oversized originals |
| height | Stored pixel height | Divide into width to find inconsistent aspect ratios |
| url | The full CDN address | Feed a download script, or paste into a browser to view the original |
A pivot on product_handle with a count of rows gives you images per product in about ten seconds. Sort that ascending and the top of the list is your reshoot queue. Sort width descending and the top of that list is your oversized originals, the files worth a downscale pass.
The file is UTF-8 with a byte order mark, so accented product titles open correctly in Excel rather than turning into mojibake. If you are pushing the file into another system and it complains about the first character, that mark is what it is complaining about, and most CSV readers have a setting for it.
Shopify CDN URLs and the size suffix trick
The thumbnails in the grid are not the original files. They are the same images requested at a smaller size, using a naming convention Shopify's CDN understands.
The rule is simple: write the size directly before the file extension. An image at /files/blue-shirt.jpg becomes /files/blue-shirt_200x.jpg for a 200 pixel wide version. You can ask for _400x, _800x, or a fixed box like _600x600. Any version query string on the end stays where it is. This tool strips a numeric size suffix if one is already present, along with any crop or retina marker attached to it, then applies _200x, which is why the grid loads quickly even when the originals are enormous.
That trick is worth remembering outside this page. If you are embedding a product photo in an email, a blog post, or a support ticket, requesting a small version instead of the 3000 pixel original is the difference between a page that loads and a page that crawls. Shopify also serves modern formats automatically to browsers that support them, so you do not need to hand build a WebP version yourself.
One caveat. The suffix only works on Shopify's own CDN. If a store serves product images from somewhere else, which happens with certain page builders and custom setups, the thumbnail will silently fall back to the full size file. You will notice because that row of the grid takes a moment longer.
Seven things people use an image URL list for
Almost nobody wants a list of links for its own sake. Here is what the list is normally a step toward.
- Platform migrations. Moving a catalog somewhere else means the new system needs to fetch each image from a reachable address. A URL column in your import file is often all it wants.
- Finding oversized originals. Sort by width. Shopify recommends 2048 pixels for square product photos, and much past that you are paying in storage, exports, backups and slower admin work. Shoppers only pay for it where a theme section, an app block or a zoom viewer serves the raw source with no width parameter, because otherwise the CDN hands over a resized copy. The Product Image Weight Checker ranks the offenders for you instead of making you pivot the CSV.
- Reshoot planning. Count images per product, sort ascending, and you have a prioritised list for your photographer that took no manual counting at all.
- Duplicate hunting. The same filename appearing under five different products usually means one photo is doing five jobs. Sometimes that is fine. Often it means the colour variants were never shot separately.
- Feed debugging. Shopping feeds reject images that are too small, wrong aspect, or unreachable. Having every URL with its dimensions in one sheet turns a vague feed error into a specific row. The Google Shopping Feed Checker handles the other half of that job.
- Competitive reference. Counting how many angles a competitor shoots per product tells you what shoppers in your category have been trained to expect. Look, count, learn, and shoot your own.
- Handing work to a developer. A plain text list of URLs is the least ambiguous brief you can give. No screenshots, no "the third one on the blue product", just addresses.
Why this page hands you URLs and not a ZIP
This comes up often enough that it deserves its own explanation rather than a footnote. The honest version first, because the internet is full of the dishonest one.
It is not impossible. Shopify's image CDN answers requests for product images with an access control allow origin header of asterisk, which means a script on any page is permitted to read those bytes, which means a browser side ZIP is buildable. Anyone telling you the same origin policy forbids it has not checked. We had that wrong on this page too, and we would rather correct it than keep a tidy excuse.
Here is the actual reason. Building the archive means pulling every full size original from the store's CDN, and multiplying that by everyone who runs the tool, which is bandwidth and load nobody at that store agreed to. It also means this page becoming a one click copier of product photography that in most cases belongs to someone who did not publish it for that purpose. And our server side fetch endpoint rejects any response that is not text, which is deliberate: an endpoint that hands back arbitrary binaries is an open proxy waiting to be found.
For a catalog you own there is a better route anyway. CS Export Product Images runs inside your Shopify admin, reads the media through the authenticated API, and exports the files as a ZIP with the folder structure kept and the filenames renamed on the way out if you want them SEO friendly. It can also filter by collection, vendor or tag, which no scrape of a public feed will do for you. So the split is a choice we stand behind. Public tool, public data, addresses. Authenticated app, your own store, files.
Copyright, permission, and what counts as fair use of this page
Product photography is a creative work. Copyright in it normally sits with the photographer or, more often in ecommerce, with the merchant who commissioned it. A public URL is not a licence. Neither is a right click, a screenshot, or a tool like this one.
Where this page is clearly fine: auditing your own catalog, inspecting a store you manage for a client, checking a supplier's images that you have distribution rights to, pulling reference for a migration you have been hired to run, or looking at how a competitor structures their galleries so you can plan your own shoot.
Where it is not: taking a competitor's photos and putting them on your own product pages. Dropshippers do this constantly and it is both a copyright problem and a commercial one, because identical images across a dozen stores tell shoppers immediately that nobody here owns the product. Supplier catalogs are the usual grey area, and the answer there is boring: read your supplier agreement, and if it does not say you can use the images, ask.
We are app developers, not your lawyers, and this is not legal advice. But the practical rule holds up well. If you could not comfortably tell the store owner what you are doing with their images, do not do it.
Limits and troubleshooting
| What you see | Why | What to try |
|---|---|---|
| Not product JSON | The URL is not a Shopify storefront, or the store shows a password page | Confirm the store is public. Run it through the Store Analyzer to check it is Shopify at all |
| Zero products | The collection is empty, or the endpoint is disabled on that store | Try the store root instead of the collection URL |
| Exactly 250 products | You hit one page of the feed | Click the load button to append the next 250 |
| Fewer images than expected | Draft products and unpublished media are not in the public feed | Use an admin side export for the full picture |
| A thumbnail does not load | The image is not on Shopify's CDN, so the size suffix does not apply | Click through to the full URL, which still works |
| Could not reach the store | Network trouble, a redirect loop, or a rate limit | Wait a minute and retry. Requests are capped per minute to keep the endpoint healthy |
One more limit worth naming: the thumbnail grid caps how much it draws. Rendering two thousand images at once turns a useful page into a slideshow of a browser tab dying. The text list and the CSV always contain everything that was loaded, whatever the grid chooses to show.
Related Tools
- Product Image Audit Checklist. Ten checks for gallery quality, alt text, filenames, and variant coverage.
- Product Image Weight Checker. Ranks every product image by pixel size so you do not have to pivot the CSV yourself.
- Bulk Image Renamer. Rename a batch of files to a consistent, searchable pattern before you upload them.
- Image Compressor. Shrink oversized originals in the browser once this tool has told you which ones they are.
- Shopify Store Analyzer. Products, variants, theme, and pricing for any public store in one pass.
- Product CSV Generator. Build a valid product import file, image column included.
Our Shopify Apps
Rubik Variant Images Rubik Combined ListingsSmart Bulk Image Upload Export Product Images Bulk Delete Products
Does this tool download the actual image files?
No, and that is a decision rather than a technical limit. It collects image URLs, dimensions, and gallery positions, then gives you those as copyable text and as a CSV. Packaging the files themselves would mean pulling every full size original off a store that never opted in to that load, and turning this page into a one click copier of photography that usually belongs to someone else, so the server side fetch behind it accepts text responses only. For the real files from a store you own, use an app with admin access such as CS Export Product Images.
Can I get a ZIP of all product images from a Shopify store?
Not from this page. Shopify's CDN does allow cross origin reads of product images, so a browser tool could technically assemble one, but we will not: it means hauling every full size original off a storefront that never agreed to the traffic, and handing anyone a one click copy of a catalog they may not own. From inside your own Shopify admin it is straightforward and better, because an authenticated app can request the media properly and rename the files as it goes. That is the split: this public tool gives you addresses, an authenticated app gives you archives.
What URL should I paste in?
Any of three. A store root such as example.com or example.myshopify.com pulls the whole catalog, 250 products at a time. A collection URL such as example.com/collections/summer pulls only that collection. A product URL such as example.com/products/blue-shirt pulls a single product. If you type a name with no dot in it, it is treated as a myshopify.com subdomain.
Why does it stop at 250 products?
That is the maximum the public products.json endpoint returns in one request. It is a Shopify limit, not ours. When a run comes back with exactly 250 products, a button appears to fetch the next page, and each page appends to the same list so your CSV keeps covering everything loaded so far.
Does it work on password protected stores?
No. A store with the password page enabled serves that page instead of product JSON, so you get a parse error rather than a list. This is also true of stores in a trial that have not been opened to the public yet. If it is your own store, you can temporarily disable the password, run the tool, and switch it back on.
Are draft or hidden products included?
No. The public feed only contains products that are published to the online store sales channel. Draft products, archived products, and media hidden from the storefront never appear. If your on screen count is lower than the number in your admin, that gap is usually the explanation.
How are the thumbnails generated so quickly?
Shopify's CDN accepts a size suffix in the filename. Write the size directly before the extension, so blue-shirt.jpg becomes blue-shirt_200x.jpg, and the CDN returns a 200 pixel wide version of the same image. The grid uses that instead of loading full size originals. It works for other sizes too, which is handy any time you need a small copy of a product photo.
Is it legal to collect image URLs from another store?
Reading a public endpoint is ordinary web activity, the same thing your browser does when it renders the page. Reusing the photography behind those URLs is a separate question with a different answer. Copyright in product images normally belongs to the merchant or their photographer, and a public address grants you no licence. Audit your own catalog freely, use permitted images freely, and treat everything else as reference only.
Can I use the CSV to bulk download images with a script?
Yes, and that is the most common use. The url column feeds straight into curl, wget, a download manager, or a few lines of Python. Be considerate about rate: hammering a CDN with thousands of parallel requests is rude and may get you throttled. For your own store, an admin side export is faster and kinder than scripting against the storefront.
What do the width and height columns tell me?
They are the stored dimensions of the original upload. Sort width descending and you find the files sitting far above Shopify's recommended 2048 pixels, which cost you in storage, exports, backups and admin speed. They cost shoppers bytes only where a theme section, an app block or a zoom viewer serves the raw source with no width parameter, since the CDN otherwise generates a resized copy. Compare width to height across a collection and inconsistent aspect ratios show up as ragged product cards on the collection page.
Why do some products show the same image more than once?
Usually because the same photo was uploaded separately to several products, which is normal for colour families that share a flat lay or a size chart. Occasionally it means a variant is borrowing the product default because no dedicated shot was ever assigned to it. The gallery position column helps you tell the two apart.
Does this show which image belongs to which variant?
Not in the CSV, deliberately. The feed does expose a variant_ids list per image, but on most stores it is sparse, because Shopify links a single featured image to a variant and leaves the rest of the gallery attached to the product. Flattening that into a spreadsheet produces more confusion than insight. If per variant imagery is the thing you are actually chasing, that is what our Rubik Variant Images app is built for.
Will the store owner know I ran this?
Yes, if they read their logs. The request comes from our server rather than your browser, and it carries a user agent that identifies it as CraftshiftToolsBot with a link back to this page, so a store owner can see exactly what it was. If our own endpoint is unavailable or rate limited, the request may be routed through a public CORS proxy instead, which means the store URL you typed is handed to a third party service, so do not paste a URL you would not want seen. We keep a ten minute cache of the fetched response and a short per IP request counter, nothing else.
Can I run it on a store that is not on Shopify?
No. It depends on the products.json endpoint, which is a Shopify convention. Other platforms either do not publish an equivalent feed or publish it at a different address with a different shape. If you are not sure what platform a store runs on, the App Detector and Store Analyzer will tell you in a couple of seconds.
How often is the data refreshed?
Each run fetches live from the storefront, with a short cache on identical URLs so that repeated clicks do not pile requests onto the store. If you have just uploaded new photos and they are not showing, wait a few minutes and run it again.