Paste a Shopify store URL, or a single collection URL, and this tool reads the public product catalogue, then flags products that look like copies of each other. It checks four separate signals: exact title matches after case and whitespace are normalised, near matches found by stripping a trailing colour or size word off the title, the same SKU sitting on two different products, and the same image URL used by two different products. Nothing is changed in the store. You get a list of candidates plus the evidence behind each one.
The results are split into three sections on purpose, because the first two need opposite fixes. A true duplicate is one product listed twice, and the answer is to keep one, redirect the other, and delete. A colour sibling is the same garment sold as two products, one Red and one Blue, and the answer is to group them so shoppers see one listing with swatches. Deleting a colour sibling destroys a real product, its reviews, and its sales history. That is why the two never share a section. The third section holds everything the scan cannot confidently put on either side, and none of it reaches the delete list.
This tool suggests candidates. It does not deliver verdicts. It reads titles, SKUs, and image URLs from public storefront data, which means it cannot see your orders, your inventory levels, your draft products, or the reason a particular listing exists. Every finding below is something for a person to look at, and the tool shows exactly which signal fired so you can disagree with it.
Duplicates rarely arrive on purpose. They pile up from CSV re-imports that change a handle by one character, from a staff member duplicating a product to create a new colour instead of adding a variant, from supplier feeds that push the same item under two vendor codes, and from platform migrations where the same catalogue lands twice. Nobody notices until search results show the same shirt three times.
We build variant image and combined listing apps, so we read a lot of other people's public catalogues. A pattern shows up constantly: a merchant asks how to group products, and the real problem underneath is that half the catalogue is accidental copies and the other half is deliberate colour splits, with nothing separating the two. That is the gap this tool fills.
Run it on your own store before a big import, after a migration, or any time your product count looks higher than your actual range. Run it on a competitor if you want to see how they structure colourways. Either way the scan only reads, needs no login, and writes nothing back. Worth knowing before you point it at someone else's store: the fetch is made by our server rather than by your browser, and it identifies itself as CraftshiftToolsBot, so it lands in that store's logs like any other crawler.
| Detail | What this tool does |
|---|---|
| Data source | Public products.json on the storefront, no login or API key |
| Scan scope | The whole storefront, or one collection if you paste a collection URL |
| Products per request | 250, which is the Shopify public limit. If the very first page comes back too large to read, that page is retried once at 100 |
| Scan depth | Up to 1,000 products per run |
| Signals checked | Exact title, near title after token stripping, shared SKU, shared image URL |
| Result sections | True duplicate candidates, colour or size siblings, and collisions that could be either |
| Products it cannot see | Drafts, archived items, and anything not published to the online store |
| Certainty | None claimed. Every row is a candidate for human review |
| Fix for bucket one | Pick a survivor, add a redirect, then delete |
| Fix for bucket two | Group or merge into variants. Do not delete |
| Fix for bucket three | Open both products yourself. Nothing here reaches the delete list |
| Changes made to the store | Zero. The scan only reads, and the request comes from our server |
Why duplicate products pile up in a Shopify catalogue
No merchant sets out to list the same product twice. Duplicates are a side effect of how catalogues actually get built, and every source leaves a slightly different fingerprint.
CSV re-imports. The Shopify product importer matches on the handle column. Change a title from "Merino Crew" to "Merino Crew Neck" between two imports and you get two products, not one updated product. Import the same file twice with a blank handle column and you get the whole catalogue twice.
Duplicating a product to make a colour. This is the most common one. Somebody needs the shirt in Sand as well as Black, the fastest path in the admin is Duplicate, and now there are two listings. Shopify even helps you along: when the new handle collides with the old one it quietly appends a numeric suffix, so you end up with merino-crew and merino-crew-1. That suffix is a useful tell, and this tool points it out when it sees one. It is never treated as proof though, because creating a colourway is exactly how most of those suffixes get made.
Supplier and dropshipping feeds. Two suppliers carry the same item under different vendor codes, both feeds sync, and the catalogue now holds the same product with two SKUs and two sets of photos. Sometimes the photos are literally the same file, because both suppliers pulled from the same manufacturer pack.
Platform migrations. Migration apps are usually run more than once, because the first pass always misses something. Without careful handle mapping the second pass creates rather than updates.
Hitting the variant ceiling. Shopify gives you 3 option types and 100 variants per product. A store selling 12 colours across 8 sizes in 2 fits runs out of room, so the catalogue gets split into one product per colour. That split is deliberate, it is not a duplicate, and it is exactly the case that gets mistaken for one.
True duplicates and colour siblings need opposite fixes
Mixing these two up is the expensive mistake, so the tool keeps them in separate sections and labels the reasoning on every group.
| True duplicate | Colour or size sibling | |
|---|---|---|
| What it is | One product accidentally listed twice | One product deliberately split across several listings |
| Typical titles | "Merino Crew" and "Merino Crew" | "Merino Crew Sand" and "Merino Crew Black" |
| Handles | Often x and x-1 | Usually distinct and meaningful |
| Inventory | Split across two listings by accident | Correctly held per colour |
| What it costs you | Split reviews, split stock, two URLs competing in search | Shoppers miss colours that exist, and each listing starts from zero social proof |
| Correct fix | Keep one, redirect the other, delete | Group them into one shopper-facing listing with swatches |
| What deleting does | Cleans up the catalogue | Destroys a real product and its history |
Read that last row twice. A bulk delete run against a list that quietly included colour siblings is not recoverable by undo, and the sales history goes with the product. That is the whole reason this tool refuses to hand you one flat list.
There is a third state, and pretending otherwise is how tools like this cause damage. Two products can share a SKU or an image file while their titles do not match at all, and from public data alone that is genuinely ambiguous: it is what an accidental copy looks like, and it is also what a colour split looks like when the colour word is one the tool does not recognise. Those groups go into a third section, they are shown with the evidence, and their handles are deliberately left out of the delete list. Only a title match puts a group in bucket one.
The four signals this tool checks
Each signal has a different strength and a different way of being wrong. The tool shows which ones fired on every group so you can weigh them yourself.
| Signal | How it is computed | Strength | How it can be wrong |
|---|---|---|---|
| Exact title match | Title lowercased, whitespace collapsed, then compared | High | Two genuinely different items can share a generic name such as "Gift Card" |
| Near title match | Trailing colour or size words are stripped, then the remaining base titles are compared | Medium, and it depends on the token | A word like Sage or Rose can be a product name rather than a colour |
| Shared SKU | Every variant SKU is indexed, then any SKU appearing on more than one product is flagged | High | Bundles, gift wrap lines, and made to order items sometimes reuse a code on purpose |
| Shared image URL | Image src compared after the cache busting query string is removed | High for a pair, worthless at scale | Size charts, care labels, and brand banners appear on hundreds of products |
That last row is why the tool filters. Any image URL that shows up on more than three products is treated as a shared asset rather than a duplicate signal, and it gets reported separately with a count. Same rule for SKUs: a code sitting on more than three products is far more likely to be a placeholder than a real collision, so it is set aside instead of being used as evidence.
One rule sits above all four: a SKU match or an image match, on its own, is never enough to put products in the delete list. Both of those fire on the single commonest thing a merchant does by accident, which is duplicating a product to make a new colour and leaving the SKU and the photos alone. Without a title match the scan has no way to separate that from a real copy, so it says so instead of picking.
How the near match token stripping works
Near matching is the part that finds colour splits, and it is also the part most likely to be wrong, so it is built to be inspectable rather than clever.
The tool looks at the end of each title and tries three shapes in order: a bracketed tail such as Merino Crew (Sand), a tail after a separator such as Merino Crew, Sand or Merino Crew | Sand, and a bare trailing word such as Merino Crew Sand. In every case the candidate tail is only removed if it matches a known colour or size, which is the guard that keeps Cotton T-Shirt from being split into "Cotton T" plus "Shirt".
The colour vocabulary covers the common named colours plus modifier pairs, so "light heather grey" and "burnt orange" both resolve. Size covers letter sizes from XXS to 5XL, the spelled out equivalents, "one size", and numeric sizes only when they carry a prefix such as Size 10 or UK 8. Bare numbers are never stripped, because "iPhone 14" and "Model 3" would come apart instantly if they were.
Up to three tails are removed per title, which handles Merino Crew, Sand, XL. Every removed token is printed next to the group, tagged colour or size. If you look at a group and think the tool stripped a word that was actually part of the product name, you can see that immediately and throw the group out. That transparency matters more than accuracy here, because you are the one deciding.
One more detail worth knowing: base titles are compared with punctuation flattened, so "T-Shirt" and "T Shirt" collapse together. That catches real duplicates created by two different people typing the same product name, which is common when a catalogue has more than one editor.
What duplicate listings actually cost a store
The obvious cost is a messy admin. The real costs are quieter.
Reviews get split. Ten reviews on one listing and four on its twin reads as a weaker product than fourteen reviews on one. Review count is one of the first things a shopper checks, and duplicates halve it for free.
Inventory gets split. Two listings mean two inventory records. One sells out while the other holds stock, and the sold out one is the one your ad is pointing at.
Search engines have to choose. Two near identical product pages on the same domain compete for the same query. Google picks one canonical, and it is not always the one you optimised, linked to, or ran ads against.
Ad spend gets diluted. Product feeds export both listings. Now you are bidding against yourself, with two different conversion histories, neither of which accumulates enough signal to optimise well.
Merchandising gets unreliable. Collection sorting by best selling puts the two halves of one product in different positions, so the collection page misrepresents what is actually popular.
Colour siblings carry a version of the same tax, which is why the second bucket is not just tidying. When Red and Blue are separate listings, a shopper who lands on Red often never learns Blue exists, and every listing accumulates its own thin review count instead of one strong one.
How to review a candidate before you delete anything
Work through this in order. It takes a couple of minutes per pair and it is the difference between a clean catalogue and an angry Monday.
Step 1: open both products side by side. Compare description, price, weight, and options. If any of those differ meaningfully, they are not the same product, whatever the title says.
Step 2: check sales history on each. A listing with orders against it has customers who may return to that URL. That listing is usually the survivor, even if the other one looks tidier.
Step 3: check inventory on both. Stock sitting on the listing you are about to delete has to be moved first, or you write it off silently.
Step 4: check what links to each URL. Collections, menus, blog posts, email campaigns, and ad destinations all point at a specific handle. The survivor should ideally be the one everything already points at.
Step 5: set the redirect before you delete. Shopify does not create a redirect for you when a product is deleted, so the old URL returns a 404 and any link equity it held evaporates. Create the URL redirect first, from the doomed handle to the survivor.
Step 6: move reviews if your review app supports it. Most review apps store reviews against a product ID. Deleting the product usually deletes the reviews with it. Migrate them first or accept the loss knowingly.
Step 7: delete, then re-run this scan. Cleanups often reveal a second layer once the first is gone.
Doing that for five pairs by hand is fine. Doing it for two hundred is not, and that is the point where a bulk deletion tool earns its place, once you have already made the keep or delete decision yourself. Our own guide to finding, merging, and deleting duplicate products walks through the merge step in more detail.
When the answer is grouping, not deleting
If the scan puts a group in the sibling bucket, the products are real and they should stay. The question becomes how to present them as one thing to a shopper.
There are two honest routes, and which one fits depends on why the catalogue was split in the first place.
Merge into variants. If the split was accidental, or the range is small, fold the separate products into one product with a Colour option. You get one URL, one review count, one inventory view. The limit is real though: 3 option types and 100 variants per product. Twelve colours times eight sizes is already ninety six, and adding a third option puts you over.
Keep them separate and group them. Larger catalogues split for a reason and cannot merge back. In that case the products stay as they are and a grouping layer connects them, so the product page and the collection page show swatches that jump between the listings. That is what grouping means in practice, and it is the job our combined listings app does.
A related problem shows up inside sibling groups: the scan sometimes finds that Red and Blue are pointing at the same image file. That is not a duplicate product, it is a photo mapping error, and it means at least one colour is showing the wrong picture. On a single product with many colours the same class of problem appears as a gallery that shows every colour to every shopper, which is what variant image filtering fixes. Use the variant image checker to see how a product page handles it today.
The limits of a public data scan
Being straight about what this cannot do is more useful than overselling what it can.
- It sees published products only. The public products.json endpoint returns products available to the online store sales channel. Drafts, archived products, and anything unpublished are invisible, so a duplicate hiding in drafts will not appear.
- It stops at 1,000 products per run. Shopify caps each request at 250 products and the tool pages through to a 1,000 product ceiling. Larger catalogues get scanned partially, and the tool tells you when it hit the ceiling. Pasting a collection URL scans that collection only, which is how you cover a big catalogue in slices.
- It cannot read orders or inventory. That data is not public, which is why the tool never recommends which listing to keep. You have information it does not.
- It cannot read metafields or tags reliably. Some stores already track a grouping key in a metafield. The public endpoint does not expose those, so an already grouped catalogue may still show sibling groups here.
- The colour vocabulary is English. Stores that put the colour at the front, name colours in another language, or encode colour in a numeric style code will get fewer sibling matches, and that has a consequence worth stating plainly. When the tool cannot recognise the colour word, two colourways that share a SKU or a photo look exactly like an accidental copy to it. Those groups are not treated as duplicates: they go to the third section, and their handles never enter the delete list. Where the titles differ only by a word or two at the front or the end, the group also carries a note saying that word may be a colour the scan cannot read.
- Password protected and headless stores return nothing. If the storefront is locked or the catalogue is served from a custom frontend without the standard endpoint, there is nothing public to read.
None of that makes the scan useless. It makes it a starting list rather than a work order.
Related Tools
- Store Analyzer: overall catalogue size, theme, and structure for any Shopify store.
- Product CSV Validator: catch the handle and column errors that create duplicates on import in the first place.
- Redirect CSV Generator: build the redirect file before you delete anything, so no old product URL 404s.
- SKU Generator: give colourways their own codes when the scan finds two products sharing one.
- Bulk Price Editor: reprice a range once the duplicates are gone and the survivor list is settled.
- Product Grouping Planner: plan how separate colour listings should be grouped into one shopper facing product.
- Collection Analyzer: count products, variants, and prices in any collection, and spot grouping patterns.
- Separate Products vs Variants: work out whether a range belongs in one product or several.
- Variant Image Checker: see whether a product page filters its gallery per variant or shows everything to everyone.
Our Shopify Apps
Rubik Variant Images Rubik Combined ListingsSmart Bulk Image Upload Export Product Images Bulk Delete Products
What counts as a duplicate product on Shopify?
A true duplicate is the same product existing as two separate product records, each with its own handle, URL, inventory, and reviews. That is different from a product that has several variants, which is one record, and different again from two products that represent the same garment in two colours. This tool treats only the first case as a duplicate. The other two are either correct as they are or a grouping question.
How does the tool find duplicates without admin access?
Every Shopify storefront publishes a products.json endpoint containing the products available to the online store. It includes titles, handles, vendors, variants with their SKUs, and image URLs. The tool reads that public data, up to 250 products per request and up to 1,000 products per scan, then compares the records in your browser. No login, no API key, and nothing is written back to the store.
Why does the tool separate colour siblings from true duplicates?
Because the fixes are opposite. A true duplicate should be deleted after you redirect its URL. A colour sibling is a real product and deleting it destroys its sales history, its reviews, and any links pointing at it. A single mixed list invites a bulk delete that takes out real products, so the two categories are never combined here. Anything the scan cannot place with confidence goes into a third section instead of being guessed into one of the first two, and only groups that matched on the title itself appear in the delete list.
Which words does the near match check strip?
Named colours plus modifier pairs such as light, dark, deep, heather, and burnt combined with a base colour. Letter sizes from XXS to 5XL and their spelled out forms, plus "one size". Numeric sizes only when prefixed, as in "Size 10" or "UK 8". Bare numbers are never stripped, because product names contain numbers all the time. The tool prints every token it removed next to the group.
Why does the tool show the tokens it stripped?
So you can overrule it. Words like Sage, Rose, Olive, and Charcoal are colours in one catalogue and product names in another. If the tool grouped two candles because it treated Sage as a colour, seeing the stripped token tells you instantly that the grouping is wrong. A duplicate finder that hides its reasoning is asking you to trust a guess.
Can two products legitimately share a SKU?
Yes, and it happens more than you would expect. Bundles that contain a component sold separately, made to order items keyed to a production code, gift wrap or insurance lines applied across a range, and multi channel setups that key on a supplier code all produce shared SKUs on purpose. A shared SKU is a strong signal, not a proof. If a code appears on more than three products the tool assumes it is a placeholder and sets it aside instead of reporting a collision. A shared SKU on its own never puts products in the delete list either, because duplicating a product to make a new colour and leaving the SKU alone produces exactly the same pattern.
Two products share an image. Does that prove they are duplicates?
For a pair, it is one of the stronger signals available, because Shopify stores each uploaded file at its own path. If two products point at the identical URL, the same file was deliberately attached to both. At scale it means nothing: size charts, care instructions, and brand banners get attached to entire catalogues. The tool treats any image on more than three products as a shared asset and reports it separately with a count.
How many products can the tool scan?
Up to 1,000 per run, read 250 products at a time. That ceiling is ours, not Shopify's. The public endpoint keeps paging well past 1,000 products, so the limit is a choice we made: 1,000 products is four requests at the default page size, and every one of them is fired by our server at a store we do not own. If your catalogue is larger, the scan covers the first 1,000 products in the order Shopify returns them and tells you it stopped early. To get past it, paste a collection URL such as example.com/collections/knitwear: Shopify publishes the same endpoint per collection, so the scan reads that collection only and you can work a big catalogue in slices.
Does it see draft, archived, or hidden products?
No. The public endpoint only returns products published to the online store sales channel. Duplicates sitting in drafts, in archive, or published only to a different channel will not appear. That is worth remembering before concluding a catalogue is clean, since half finished duplicates often live in drafts.
Should I delete a duplicate or redirect it?
Do both, in that order. Create a URL redirect from the handle you are removing to the one you are keeping, then delete. Shopify does not add a redirect automatically when a product is deleted, so without that step the old URL returns a 404 and any inbound links, ad destinations, and saved bookmarks pointing at it are wasted. Set the redirect first because once the product is gone the admin makes it harder to find the handle.
What happens to reviews and inbound links when I delete a product?
Most review apps store reviews against a Shopify product ID, so deleting the product usually removes its reviews too. Check whether your review app has a merge or transfer function before you delete anything, since running it afterwards is not possible. Inbound links are handled by the redirect. Internal links from collections, menus, and blog posts should be repointed by hand, because a redirect works but adds a hop.
How do I merge two products into one?
Shopify has no merge button, so a merge is a manual sequence: decide which product survives, copy anything unique from the other one into it as a variant or an image, move the inventory across, transfer or accept the loss of reviews, create the redirect, then delete. Doing it in that order means you never have stock or traffic pointing at something that no longer exists. Our walkthrough of the duplicate products problem covers the sequence with screenshots.
Should colour siblings be merged into variants or grouped?
It depends on size. If the range fits comfortably inside 3 option types and 100 variants per product, merging into one product with a Colour option is the cleaner answer and consolidates reviews and inventory. If it does not fit, or the store split colours deliberately for merchandising or feed reasons, keep the products separate and add a grouping layer so shoppers see swatches that move between the listings. Forcing a large range into one product to avoid grouping usually ends in a variant limit error.
Why do so many stores end up with one product per colour?
Three reasons, and none of them are laziness. Shopify allows 3 option types and 100 variants per product, which a colour by size by fit range exceeds quickly. Duplicating an existing product is the fastest way to add a colour in the admin, so it becomes the default habit. And separate listings per colour give each colourway its own URL, which some merchants want for ads and for feeds. The split is often rational. The missing piece is a layer that puts it back together for the shopper.
Does this tool change anything in my store?
No. The scan only issues read requests for public storefront pages and the comparison runs in your browser. The fetch itself is made by our server, which identifies itself as CraftshiftToolsBot, so it will appear in the target store's logs like any other crawler. If our server cannot reach the store, the request falls back to a public proxy service, which means the URL you scanned passes through a third party as well. Nothing is written to the store and no login is involved.
Why did the scan find nothing on a store I know has duplicates?
Usually one of four things. The duplicates are unpublished, so the public endpoint hides them. The catalogue is bigger than 1,000 products and the copies sit past the cut off. The titles differ enough that no signal fires, which happens when duplicates were created by two people describing the same product differently. Or the store is password protected or headless, in which case nothing was read at all. Paste a collection URL to scan one collection at a time, check the third section as well as the first, and compare the product count the tool reports against what you see in the admin.