How Much Storage Are We Really Using on Shopify? I Wrote a Script Because Shopify Doesn't Tell You (Or at Least My Grow!)


☢️ The Problem

Every Shopify plan has a file storage cap: Basic 100GB, Grow 300GB, Advanced 500GB, Plus 1TB. Exceeding it does not block product imports, but it blocks uploading images or files in the Content > Files section. It’s a “ninja” error — to realize it, you have to go check it yourself, as you are not explicitly notified. I am used to using Matrixify to import products: at the end of the import, you can download the import report, and that is how I noticed the problem. Furthermore, Shopify does not show anywhere how much storage you are actually occupying compared to the limit, and even asking support won’t be of much help!

1️⃣ First Attempt

We found ourselves in this situation: imports were successful and products were correctly uploaded, but they were all without images. The first attempt we made was to go to the Content > Files section, filter for images NOT linked to any product, and delete them to free up some memory, but it was not enough.

🔗 Admin GraphQL API

I wrote shopify-storage-audit to answer two questions with real data, bypassing both Matrixify and support: how much each single file in the store actually weighs in bytes, and which of these files are no longer linked to any product (active, draft, or archived). And above all, to know for sure how much space I am actually using.

The script works in two phases, both via the Admin GraphQL API with cursor-based pagination.

Phase 1 — file list. It queries files(first: 250, after: $cursor) and for each node distinguishes MediaImage (real weight in originalSource.fileSize — this field does not exist in the standard Files export) from GenericFile (originalFileSize). It pages until it has downloaded the entire store.

Phase 2 — media attached to products. It queries products(first: 50, after: $cursor) and for each product its media(first: 250), building a set of all the IDs of the images actually linked to a product. If a product has more than 250 media (the cap of the nested page), the script flags it separately: that product must be checked manually because it could have extra images not counted.

At this point, a simple merge with pandas is enough: files of type MediaImage whose ID does not appear in the set of attached media are the “orphans”. The script prints a summary (total files, total weight in GB, number and weight of orphans) compared against plan limits, and saves two timestamped Excel files, one with all files sorted by weight descending, and one with orphans only.

One detail that turned out to be useful: calls are protected by retries with exponential backoff and jitter, both on network errors, on 429s, and on GraphQL errors with extensions.code THROTTLED. On a store with hundreds of thousands of files, Shopify’s rate limiting occurs almost immediately, and without this, the script would have stopped after a few minutes.

🛠️ Setup and Use

pip install -r requirements.txt, then cp .env.example .env and fill in SHOPIFY_SHOP / SHOPIFY_TOKEN / API_VERSION. The Shopify custom app (Settings → Apps and sales channels → Develop apps) must have the Admin API scopes read_products and read_files.

audit_shopify_file_storage.py for the full audit. Optional flags: --limit 5000 for a quick test without downloading everything, --output-dir for a custom output folder, --env-file for a .env in a different path. (The version published on GitHub is more generic than the internal one used in production: hardcoded paths removed, these flags added to make it reusable by anyone.)

What We Found

The numbers on our store: 303.01 GB used across about 441,714 files. Practically at the limit of the Grow plan (300GB). This alone explained why imports were blocking: it wasn’t a Shopify recalculation delay, we were genuinely full.

The counterintuitive part: the orphans were almost irrelevant. Only 788 files, for 0.25 GB — so just background noise, not the cause. Deleting them wouldn’t have solved anything.

The real cause was another: 60,185 products in the catalog still had 440,902 images attached. It was the legacy of an old import flow that loaded everything via the Image Src field of the import tool, never cleaned up on products already live. A subsequent effort to split hero image and gallery in imports (to reduce the average weight per product) only prevents the problem on new imports — it doesn’t touch the historical data already accumulated.

⚠️ A Warning Before Deleting Anything

The only signal the script uses to say “orphan” is “not attached to any product”. This produces false positives by design: theme logos, downloadable PDFs, blog or page images are not attached to a product by definition, but are legitimate. They still end up in the orphans list and must be checked by eye one by one before any bulk deletion. On Shopify, deletion is permanent, so be careful!

🤝 Why Share It

I believe it is essential to know the available space on the platform in order to make decisions in the most precise way possible.

Repo: shopify-storage-audit.