Broken size run sheet for clothing and shoe shops: which sizes each style has sold out of, from the shop's own product data
What you get: a broken size run list in one Google Sheet, built from a web shop's public product data. It has one row per product: product name, shop, link, how many sizes or variants are offered, how many are in stock, which ones are sold out (for example White / M), which of your core sizes are missing, a share-in-stock figure and a Status column (Sold out / Broken run / Some sold out / Full run). Sort by Status every Monday and you know which styles to reorder before Black Friday and Christmas, and which to move to markdown. Formulas are below. The example rows are made up.
The finished sheet
sizerun tab (example rows are made up; core sizes in settings: S, M, L)
| Product | Offered | In stock | Sold out | Core sizes missing | Share in stock | Status | Next step |
|---|---|---|---|---|---|---|---|
| Linen shirt, white | 4 | 2 | M, L | M, L | 50% | Broken run | Reorder M and L |
| Cotton tee | 6 | 5 | White / M | M | 83% | Broken run | Reorder white M |
| Wool jumper, navy | 5 | 4 | XS | 80% | Some sold out | Leave | |
| Canvas tote | 1 | 1 | 100% | Full run | |||
| Silk scarf, rust | 1 | 0 | Default Title | 0% | Sold out | Hide or reorder |
The cotton tee row shows why the sheet works per variant, not per product: the product is still "in stock" because five of six variants are, but a customer looking for a white medium finds nothing.
Who this is for
Owners and merchandisers of small clothing, footwear and lingerie shops that sell online, and buyers at multi-brand boutiques who want to see which sizes a brand's own web shop has run out of. A style that has sold out of its middle sizes but still has XS and XXL looks "in stock" on a product list, yet it no longer sells. This sheet puts those styles at the top.
Step 1: get the products into a tab named scrape
With the product data scraper
Open the E-commerce Product Data Scraper (Shopify, WooCommerce, any shop) on the Apify Store and use this input with the shop's address:
{ "startUrls": [{ "url": "https://your-shop.example" }],
"maxProductsPerShop": 2000, "includeDescription": false }
maxProductsPerShop: set it above the shop's number of products (the default is 100). You pay only for products delivered.includeDescription: this sheet does not need descriptions, so switch it off to keep the file small.- You can also give a single collection or category page, for example only the dresses collection.
The scraper follows robots.txt and reads the shop's terms page. If the terms forbid automated access (Shopify's default terms do), it skips the shop and charges nothing. Check the run's Storage → Key-value store → SHOPS_SUMMARY: the shop should show ok. If it shows blocked-by-terms, use the free admin route below for your own shop, and leave other shops alone.
In the run, export the dataset as CSV. In your Google Sheet use File → Import → Upload, "Insert new sheet", and name the tab scrape. Do not rename the headers.
What the variant columns contain
- Shopify shops (read from the shop's public product JSON): one set of columns per variant,
variants/0/title,variants/0/available,variants/1/titleand so on. The variant title is the option values joined with a slash, for exampleWhite / M.availableis TRUE or FALSE. It says whether the variant can be bought, not how many are left. - Shops that publish schema.org product data with variants: the same columns, with the variant name the shop publishes. Some shops write names like
Tee - M; see the note on core sizes below. - WooCommerce shops (read from the WooCommerce Store API): one row per product with
inStock, but no variant columns. These rows show "In stock, no size data" or "Sold out". - A Shopify product with only one variant has the variant title
Default Title.
Free, from your own Shopify admin
Products → Export gives one row per variant with the option values (Option1 Value, Option2 Value and so on), and the inventory export gives stock per variant and location. With those you can count sold-out sizes per product in a pivot table instead of using the formulas below. Your admin also shows quantities, which public data does not.
Step 2: the settings tab
Add a tab named settings. In B1 type your core sizes, separated by commas, exactly as they appear in variant titles: for example S, M, L, or 8, 10, 12, 14, or UK 6, UK 7, UK 8. A style missing any of these counts as a broken run.
Step 3: build the sizerun tab
Add a tab named sizerun. Row 1 holds the headers below; row 2 holds the formulas, which you copy down one row per row of scrape.
| Col | Header | Formula (row 2) |
|---|---|---|
| A | Product | =IFERROR(INDEX(scrape!$A2:$ZZ2, MATCH("title", scrape!$A$1:$ZZ$1, 0)), "") |
| B | Shop | same pattern with "shop" |
| C | Link | same pattern with "url" |
| D | Offered | =IFERROR(COUNTA(FILTER(scrape!$A2:$ZZ2, REGEXMATCH(scrape!$A$1:$ZZ$1, "^variants/\d+/title$"), scrape!$A2:$ZZ2<>"")), 0) |
| E | In stock | =IFERROR(COUNTA(FILTER(scrape!$A2:$ZZ2, REGEXMATCH(scrape!$A$1:$ZZ$1, "^variants/\d+/available$"), UPPER(scrape!$A2:$ZZ2&"")="TRUE")), 0) |
| F | Sold out | =IFERROR(TEXTJOIN(", ", TRUE, FILTER(FILTER(scrape!$A2:$ZZ2, REGEXMATCH(scrape!$A$1:$ZZ$1, "^variants/\d+/title$")), UPPER(FILTER(scrape!$A2:$ZZ2, REGEXMATCH(scrape!$A$1:$ZZ$1, "^variants/\d+/available$"))&"")="FALSE")), "") |
| G | Core sizes missing | =IF(F2="", "", IFERROR(TEXTJOIN(", ", TRUE, FILTER(TRIM(SPLIT(settings!$B$1, ",")), ISNUMBER(SEARCH("/ "&TRIM(SPLIT(settings!$B$1, ","))&" /", "/ "&SUBSTITUTE(F2, ", ", " / ")&" /")))), "")) |
| H | Share in stock | =IF(N(D2)=0, "", E2/D2) (format as %) |
| I | Status | =IF(A2="", "", LET(s, UPPER(IFERROR(INDEX(scrape!$A2:$ZZ2, MATCH("inStock", scrape!$A$1:$ZZ$1, 0)), "")&""), IF(s="FALSE", "Sold out", IF(D2=0, IF(s="TRUE", "In stock, no size data", "Check"), IF(G2<>"", "Broken run", IF(F2<>"", "Some sold out", "Full run")))))) |
| J | Price | same pattern as A with "price" |
| K | Next step | you type: reorder / markdown / hide / ask brand / leave |
Notes on the formulas:
- Sold out pairs each
variants/N/titlewith itsvariants/N/available. Every variant has both columns, so the two lists line up. - Core sizes missing wraps the sold-out list in slashes and looks for
/ M /, soLdoes not matchXLandWhite / Mcounts as a missing M. SEARCH ignores upper and lower case. If a shop names variants likeTee - M, change the slashes in the formula to the shop's separator, or check column F by eye. - A missing core size in one colour counts as a broken run for the whole product, because column F tells you which colour it is.
- Status uses the product's own
inStockfirst, so a fully sold-out product shows "Sold out" even when it has no variant columns.
Step 4: the Monday routine
- Run the scraper again (or re-export from your admin) and re-import the
scrapetab with "Replace current sheet". Thesizerunformulas and your Next step notes stay, but check that row order still matches if products were added; sortingscrapebytitlebefore importing keeps it stable. - Add a filter on
sizerunand show only "Broken run". These are the styles to reorder first. - Styles that stay "Broken run" for several weeks and that you will not reorder are markdown candidates: the remaining sizes are what customers can still buy.
- For a brand you stock, a broken run on the brand's own web shop can mean the brand itself is short of those sizes. Ask your rep before you plan a reorder around it.
Check before you act on it
SHOPS_SUMMARYshowsokfor the shop, or you used your own admin export.settings!B1sizes are spelled exactly as in the variant titles.- Rows marked "In stock, no size data" are WooCommerce or similar shops: check sizes on the product page.
- Public data says whether a size can be bought, not how many are left. For your own shop, confirm quantities in your admin before you reorder.
Partner programme opening soon
Partner programme opening soon: join the free waitlist. Planned terms are 50% of each referred sale (75% once you have 5+ paying customers), single level, paid through Gumroad only from revenue received.
Sources
- Product data scraper inputs and outputs (
startUrls,maxProductsPerShopdefault 100,includeDescription; Shopify variants withtitleandavailablefrom the public product JSON; schema.orghasVariantnames and availability; WooCommerce Store API rows withinStockand no variants;SHOPS_SUMMARYstatuses; skipped shops not charged): Apify Store page (Actor input schema, README and source read 24 Oct 2026) - Google Sheets FILTER, REGEXMATCH, TEXTJOIN, SEARCH, SPLIT and LET functions: Google Docs Editors Help.
Related
- Competitor store: out of stock and new products, weekly
- Wholesale line sheet from your own Shopify or WooCommerce store
- Brand stockist price and stock check sheet
Unadhi Guides are free, independent explainers. We correct errors as soon as we find them: write to mininglife7@gmail.com.