ScreenshotNeo

BlogHow-to

How to Download All Product Images from Shopify

Shopify’s CSV export includes image URLs, not image files. Learn the safest ways to collect every product image for migration, backup, or local use.

By the ScreenshotNeo team1 October 20268 min read

Short answer: Shopify’s product CSV export does not contain the image files themselves. It contains product data and image references. To download image files, export the catalog, extract the public image URLs, and download those URLs with a script. For individual media, use Content > Files. For a managed bulk export, compare a dedicated Shopify App Store export app and verify its current permissions, limits, and price.

1. Understand what Shopify exports

Shopify states: “When you export your products, the images associated with each product aren’t included in the CSV file.” The CSV can still contain image references, including product image URL and variant image URL fields. Those URLs are useful for migration or for downloading the files yourself.

Goal Best route What you receive
Spreadsheet, backup data, or migration record Products > Export Product records and image URLs; no image files
Find one or several uploaded media files Content > Files File management and individually copied links
Download many local image files with filters or naming controls Shopify App Store export app Depends on the app; verify coverage, access, limits, and pricing
Capture the rendered appearance of product pages ScreenshotNeo PNG, JPEG, WebP, or PDF screenshots of pages

2. Export your product CSV

  1. In Shopify Admin, open Products.
  2. Select Export.
  3. Choose all products, the current page, selected products, or products matching your search and filters.
  4. Choose the CSV export option and download the file, or wait for Shopify’s email when Shopify indicates that delivery is required for the selection or variant volume.

Keep the original store and product listings available until any migration finishes. Shopify requires image URLs to remain publicly viewable while the destination imports them.

3. Inspect the image columns

Open the CSV in a spreadsheet or text editor and locate the product image URL and variant image URL columns described in Shopify’s CSV documentation. A product can have multiple image rows, and a variant can have its own image reference.

Do not sort the CSV casually. Shopify warns that sorting can disassociate product variants or image URLs; importing a damaged, sorted file can overwrite product data.

4. Download the image files with Python

The following script reads a Shopify product CSV, finds URL-looking values, downloads each unique image, and writes a manifest that preserves the source URL. It uses only the public URLs already present in your export.

import csv
import hashlib
import mimetypes
import pathlib
import re
import sys
from urllib.parse import urlparse

import requests

CSV_PATH = sys.argv[1] if len(sys.argv) > 1 else "products.csv"
OUTPUT_DIR = pathlib.Path("shopify-product-images")
OUTPUT_DIR.mkdir(exist_ok=True)

URL_RE = re.compile(r"^https?://", re.I)
IMAGE_EXTENSIONS = {".jpg", ".jpeg", ".png", ".webp", ".gif", ".avif", ".tif", ".tiff"}


def filename_for(url, content_type):
    path_name = pathlib.Path(urlparse(url).path).name
    suffix = pathlib.Path(path_name).suffix.lower()
    if suffix not in IMAGE_EXTENSIONS:
        guessed = mimetypes.guess_extension((content_type or "").split(";")[0])
        suffix = guessed or ".bin"
    digest = hashlib.sha256(url.encode("utf-8")).hexdigest()[:16]
    stem = pathlib.Path(path_name).stem or "image"
    safe_stem = re.sub(r"[^A-Za-z0-9._-]+", "-", stem)[:80]
    return f"{safe_stem}-{digest}{suffix}"


session = requests.Session()
session.headers.update({"User-Agent": "shopify-image-downloader/1.0"})
seen = set()
manifest_rows = []

with open(CSV_PATH, newline="", encoding="utf-8-sig") as csv_file:
    for row_number, row in enumerate(csv.DictReader(csv_file), start=2):
        for column, value in row.items():
            url = (value or "").strip()
            if not URL_RE.match(url) or url in seen:
                continue
            seen.add(url)
            try:
                response = session.get(url, timeout=60)
                response.raise_for_status()
                filename = filename_for(url, response.headers.get("Content-Type"))
                destination = OUTPUT_DIR / filename
                destination.write_bytes(response.content)
                manifest_rows.append({
                    "csv_row": row_number,
                    "column": column,
                    "source_url": url,
                    "local_file": str(destination),
                    "bytes": len(response.content),
                    "status": "downloaded",
                })
                print(f"downloaded {url} -> {destination}")
            except requests.RequestException as error:
                manifest_rows.append({
                    "csv_row": row_number,
                    "column": column,
                    "source_url": url,
                    "local_file": "",
                    "bytes": 0,
                    "status": f"error: {error}",
                })
                print(f"failed {url}: {error}", file=sys.stderr)

with open(OUTPUT_DIR / "manifest.csv", "w", newline="", encoding="utf-8") as manifest_file:
    writer = csv.DictWriter(manifest_file, fieldnames=["csv_row", "column", "source_url", "local_file", "bytes", "status"])
    writer.writeheader()
    writer.writerows(manifest_rows)

print(f"Processed {len(seen)} unique URLs; manifest: {OUTPUT_DIR / 'manifest.csv'}")

Run it with:

python -m pip install requests
python download_shopify_images.py products.csv

What the script handles

  • Duplicate URLs are downloaded once.
  • Files without a useful extension use the HTTP content type when available.
  • Names include a short hash, preventing collisions when two products use the same filename.
  • A manifest records the CSV row, column, source URL, local filename, byte count, and error status.
  • HTTP failures are recorded so you can retry only failed URLs.

5. Download with cURL, Python, or Node.js

For a single known image URL, these minimal commands save the response locally. Replace the URL with one from your CSV.

cURL

curl -L "https://cdn.shopify.com/s/files/1/0000/0000/products/example.jpg" -o example.jpg

Python

import requests

url = "https://cdn.shopify.com/s/files/1/0000/0000/products/example.jpg"
r = requests.get(url, timeout=60)
r.raise_for_status()
with open("example.jpg", "wb") as f:
    f.write(r.content)

Node.js

import { createWriteStream } from "node:fs";
import { pipeline } from "node:stream/promises";

const url = "https://cdn.shopify.com/s/files/1/0000/0000/products/example.jpg";
const res = await fetch(url);
if (!res.ok || !res.body) throw new Error(`${res.status} ${res.statusText}`);
await pipeline(res.body, createWriteStream("example.jpg"));

6. Use Content > Files for individual media

Shopify’s Content > Files area contains files uploaded to the store. Filter by Product media to narrow the list, open a file, and copy its link. This is useful for locating or downloading selected assets. Shopify’s documentation does not establish a one-click bulk download of every product image from this page.

7. Consider a Shopify bulk-export app

The Shopify App Store lists CS ‑ Export Product Images, whose listing describes bulk export of product images and store media, with filtering and naming options. Treat this as vendor-listing information. Before installing, verify:

  • Whether it includes product images, variant images, and other store media you need.
  • Whether it creates files locally, a downloadable archive, or links.
  • Permission requests and data retention.
  • Image-count limits, retries, folder or filename behavior, and current pricing.

The listing showed a free plan for 100 images and paid tiers of $29/month for 1,000 images, $69/month for 10,000, and $99/month for unlimited images when the research was retrieved. These details can change, so confirm them in the listing before purchase.

8. Migration and image-association safety

  • Keep the source store and products intact until the destination confirms that all images loaded.
  • Keep the original URLs in a manifest so you can trace every local file back to a product and CSV row.
  • Do not assume every URL is an original-resolution file; download exactly what the exported URL returns.
  • Shopify’s CSV guidance documents up to 250 images per product. Treat that as a Shopify workflow constraint, not as a limit promised by a third-party exporter.
  • Preserve variant-image relationships from the CSV instead of rebuilding them from filenames.

9. Performance, reliability, and cost

Performance

For a large catalog, stream downloads rather than loading every file into memory. Use a small worker pool, such as 4–8 concurrent requests, and write each file as it arrives. Excessive concurrency can trigger throttling or make failures harder to retry.

Reliability

Use connect and read timeouts, retry transient 429 and 5xx responses with exponential backoff, and save progress after each successful file. Compare the number of unique URLs, downloaded files, failed URLs, and total bytes before deleting the source export.

Cost

Shopify’s native CSV export is the no-cost option for product data and image references. Your own downloader incurs normal compute and storage costs. A third-party app may charge by image count or subscription; check its current listing. Avoid paying for an app when URLs and a short script meet your requirements.

10. Troubleshooting

Problem Likely cause Fix
The CSV has no image files CSV exports contain references, not binaries Extract the image URL columns and download those URLs.
Some rows have blank image URLs The product or variant has no associated image reference Check the product in Admin and confirm whether media exists in Content > Files.
Downloads return 403 or 404 The URL is private, expired, mistyped, or no longer available Open the URL, re-export current data, and retry. Keep the source store available during migration.
Images are assigned to the wrong variants The CSV was sorted or edited without preserving row relationships Start from a fresh export and avoid sorting; Shopify warns this can disassociate variants and image URLs.
The script stops midway Timeout, network interruption, or process termination Use the manifest, skip files already present, and retry only failed URLs.
Files have unusable names CDN URLs may omit an extension or reuse names Use the response content type and a URL hash, as in the script above.
A migration cannot fetch images The destination cannot reach the source URL Make URLs publicly viewable and leave the original listings intact until import completes.

11. Or skip the browser setup

If your real goal is a visual copy of product pages rather than the original image binaries, ScreenshotNeo captures a rendered page as PNG, JPEG, WebP, or PDF through one request. It can remove cookie banners, newsletter popups, and chat widgets before capture. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and the response identifies the verdict and billing status.

See the ScreenshotNeo API documentation for the available options.

cURL

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://your-store.myshopify.com/products/example -o product.webp

Python

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://your-store.myshopify.com/products/example"}, timeout=90)
open("product.webp", "wb").write(r.content)

Node.js

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://your-store.myshopify.com/products/example' }); const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

ScreenshotNeo also provides an MCP server with take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.

12. FAQ

Can Shopify export all product images in one ZIP?

The native product CSV export does not include image binaries. Use the exported URLs with a downloader or evaluate a dedicated export app.

Are variant images included?

The CSV format documents variant image URL fields. Preserve those rows and relationships when downloading or migrating.

Can I use Shopify Files to bulk-download everything?

Files helps you filter product media and copy individual links. The cited Shopify documentation does not promise a complete bulk-download workflow there.

Should I delete the old store after exporting?

No. Keep the source available until the destination has fetched and verified every image.