Shopify tags cleanup
Key takeaways
- You can read every tag on the products published to your online store from a public endpoint — no app, no login. The script is in the next section. If you'd rather not open a terminal at all, the CSV round-trip in Method 2 does the editing half with nothing but a spreadsheet — it just won't tell you which tags are duplicates.
- Tags accumulate silently, and the damage is in the tail, not the average. Across 41 storefronts — drawn from a public Shopify Community list of feedback-seeking stores, so skewed newer and smaller, not a cross-section of Shopify — the median tagged product carried a sane 3 tags, but 1,291 of 20,604 products (6.3%) carried more than twenty, and one product wore 123.
- The mess isn't usually per-product — it's across products: near-duplicates (
Sale/sale/on-sale), one-off tags, and casing drift. Of the 9,674 distinct tags (once lowercased), 5,591 — 57.8% — were used on exactly one product. And in 17 of the 41 stores, at least one tag was live in two spellings at once, leaving 552 products on a minority spelling — a list to check against your own collections. - The audit takes a minute; the cleanup — merging and normalising across hundreds of products — is the part that stalls, because Shopify has no "rename this tag everywhere" button.
By Adot Technologies Inc, the team behind Arvio. The numbers below come from a public sample of real storefronts, read in August 2026 from endpoints any browser can fetch. Method in full at the end.
Audit your own tags first
Before any advice, look at your own store. Shopify exposes the tags of every product published to your online store on a public endpoint — your-store.myshopify.com/products.json — which returns them as a plain array, no login, no app. (Products you have unpublished or restricted to another sales channel are not in there.) Paste this into a terminal, swap in your domain:
# save as tags.py, then: python3 tags.py
import json, urllib.request, collections
STORE = "your-store.myshopify.com" # swap in your own domain
counts, spellings, worst = collections.Counter(), collections.defaultdict(set), []
page = 1
while page <= 40:
url = f"https://{STORE}/products.json?limit=250&page={page}"
prods = json.load(urllib.request.urlopen(url))["products"]
if not prods:
break # stop on an EMPTY page, not a short one
for p in prods:
tags = p.get("tags") or []
if isinstance(tags, str): # some stores return one comma-joined string
tags = tags.split(",")
tags = [t.strip() for t in tags if t.strip()]
worst.append((len(tags), p["title"]))
for t in tags:
counts[t.lower()] += 1
spellings[t.lower()].add(t)
page += 1
worst.sort(reverse=True)
print(f"{len(worst)} products, {len(counts)} distinct tags (lowercased)")
print("\nMost tags on one product:")
for n, title in worst[:5]:
print(f" {n:3} {title[:60]}")
print("\nSame tag, more than one spelling:")
for t, forms in sorted(spellings.items()):
if len(forms) > 1:
print(f" {t} -> {sorted(forms)}")
once = [t for t, n in counts.items() if n == 1]
print(f"\nTags used exactly once ({len(once)} of them, candidates to strip):")
for t in sorted(once)[:50]:
print(f" {t}")
if len(once) > 50:
print(f" ... and {len(once) - 50} more")
/products.json pages 250 products at a time, so the script walks the pages itself and stops on the first empty one. That matters for the "used exactly once" list specifically: a tag used once on page 1 and once on page 2 looks like a one-off to each page separately, and that list is the one you were about to delete from — we ran both versions against a live 2,693-product store, and the single-page read returned exactly one "used exactly once" tag — tag__hot_oferta, which is on 5 products once you read all eleven pages. One suggestion, and it was wrong. (Stop on an empty page, not a short one — a short page mid-catalogue is normal.) Nothing here can change your store.
Three things fall out of that in about a minute:
- Your worst-tagged products — the ones carrying twenty, forty, a hundred tags. Those are almost never deliberate; they're layers left by old apps, imports and campaigns.
- Your distinct-tag count. If you have 400 products and 900 distinct tags, most of those tags are doing nothing — a tag that appears once filters exactly one product.
- Casing collisions. The "same tag, more than one spelling" block lists any tag your store is carrying in two forms at once —
Salealongsidesale. Each spelling is a separate tag, and no single product page will show you both spellings.
No terminal? The CSV round-trip in Method 2 below needs nothing but a spreadsheet — export, find-and-replace, re-import — and it's built into Shopify, so it costs you nothing but the time. What it won't do is decide which tags are duplicates. That part is what our own app, Arvio (paid, disclosure in Method 3), does from inside your admin: it reads the whole tag set, proposes the canonical form of each, and holds every change behind your approval.
What "tag sprawl" actually looks like
We ran that same read across a sample of storefronts (the how is in the Method section). Read it for the spread rather than the averages; the averages are the part that hides the problem.
| What we measured | Value | Out of |
|---|---|---|
| Products carrying at least one tag | 16,509 — 80.1% | all 20,604 products |
| Median tags on a tagged product | 3 | the 16,509 tagged products |
| Mean tags on a tagged product | 6.8 | the 16,509 tagged products |
| Products with more than 20 tags | 1,291 — 6.3% | all 20,604 products |
| Most tags on a single product | 123 | — |
| Distinct tags once lowercased | 9,674 | pooled across all 41 stores |
| …of those, used on exactly one product | 5,591 — 57.8% | the 9,674 distinct tags |
| Stores with at least one casing split | 17 — 41.5% | the 41 stores |
| Tags live in two or more spellings at once | 162 | pooled across all 41 stores |
| Products sitting on the minority spelling | 552 | all 20,604 products |
Look at the gap between the median (3) and the mean (6.8). Most products are tagged sanely — three tags, roughly what you'd choose on purpose. Then a long tail drags the average up: 1,291 products with more than twenty tags each, and at the far end, one product wearing 123 of them.
The 9,674 distinct tags are the same problem seen from the other side. That is not 9,674 useful filters: 5,591 of them (57.8%) were used on exactly one product — applied once and never reused — and much of the rest is the same idea spelled three ways.
The third pattern is the one you cannot see from inside a product page. In 17 of the 41 stores — 41.5% — at least one tag was live in more than one spelling at the same time: Sale alongside sale, Pre-Order alongside pre-order. Across the sample that split 162 tags two or more ways and left 552 products carrying the minority spelling. Those 552 are the ones a collection or filter keyed to the other spelling walks straight past. Whether it is costing any given store depends on whether a collection actually keys on that tag — a condition we cannot read from outside — but the split itself is not hypothetical: it was already there in four stores out of ten.
Nothing removes a tag for you. Imports add them, the apps you trial add their own, and a seasonal campaign leaves a bfcm-2024 behind long after the sale is over.
Why this is worth cleaning up
Tags aren't cosmetic. They're wired into three things a shopper actually touches:
- Automated collections. If you run an automated collection off a tag, a casing split is worth ruling out: a condition set for one spelling may not pull in products tagged another. We could not read any store's collection conditions from outside, so the 552 split products above are a list to check, not a count of lost sales.
- Storefront filters and search. A filter menu built on tags shows every distinct tag, including the one-offs and the typos. A cluttered tag set is a cluttered filter menu.
- Anything downstream that reads tags — theme logic, apps, feeds to Google or Meta. Each near-duplicate is a fork those systems have to handle, or silently get wrong.
So the cleanup goal is specific, not "tidy up": merge near-duplicates into one canonical tag, strip the one-offs that filter nothing, and normalise casing so Sale and sale stop being two different things.
Method 1 — the built-in bulk editor, tag by tag
Shopify's bulk editor can edit tags. Products → select → Bulk edit → Columns → tick Tags. You get a grid with a tag cell per product, and you can add or remove tags in place.
This is the right tool when the fix is small and specific — you know the exact products, and you're touching a handful of tags. Removing bfcm-2023 from the fifteen products that still carry it is a two-minute job here.
Where it stops being the right tool is a catalogue-wide rename. The bulk editor has no concept of "rename this tag everywhere" or "merge these two tags." To turn every sale into Sale, you find each affected product, remove the old tag, add the new one. Across a few hundred products that's the same grind as any manual bulk change — and Shopify's own grid loads products lazily, so the count in the header (it may say it's editing 15 when you selected 200) fills in only as you scroll. It's not a cap, but it means you're working the list in pages, by hand.
Method 2 — export tags to a CSV, fix, re-import
For a catalogue-wide rename, a CSV round-trip is more honest about the scale. Products → Export → Plain CSV. The Tags column holds all of a product's tags as one comma-separated string per row.
That format is what makes a find-and-replace possible. Back up first — Products → Export → All products — and keep that file untouched as your way back. Then: open the export, run a real search-and-replace across the Tags column. Match the tag with its delimiters (, sale, → , Sale,) so you don't also catch flash-sale — then handle the first and last tag in each cell separately, because those have a comma on one side only and a both-sides match silently skips them. Then Products → Import with Overwrite products with matching handles ticked.
Two things about the Tags column will bite you on import:
- The
Tagscolumn is destructive on import — whatever's in the cell replaces the product's existing tags entirely. It doesn't merge with what's there; it overwrites. So the cell has to contain the full, corrected tag list, not just the changes. - A blank
Tagscell wipes that product's tags. If your spreadsheet drops the column or empties a row, you've stripped tags you meant to keep.
The CSV route works, and for a single mechanical rename across a catalogue you know, it's the fastest of the manual options. What it won't do is decide which tags are worth keeping — a spreadsheet has no opinion about whether mens and men are the same tag.
Method 3 — have it read the catalogue and draft the cleanup
Past a few hundred products, the binding constraint stops being "how do I edit tags" — both methods above answer that — and becomes "somebody has to look at the whole tag set and decide which are duplicates, which are dead, and what the canonical version of each should be." That's the part neither the grid nor the spreadsheet does for you.
That decision is what Arvio does before anything gets changed. It reads your live catalogue, groups the near-duplicates (Sale / sale / on-sale), flags the tags used on only one or two products, spots the casing drift, and drafts the cleanup as a set of changes — merge these into one, strip these, rename these — for you to review. Nothing is applied until you approve it, and each change can be undone.
Disclosure: Arvio is our own product. If your cleanup is one rename across a catalogue you already understand, Methods 1 and 2 need nothing from us. Arvio is for the other case — a tag set that grew for years, where the work isn't editing the tags but deciding what they should be.
Arvio's plans start at $9.90 per 30 days with a 5-day trial.
Five ways a tag cleanup goes wrong
- Renaming in the CSV and forgetting a collection still points at the old tag. Your automated "Sale" collection was matching
sale; you rename everything toSaleand the collection empties. Update the collection condition in the same pass. - A blank
Tagscell on import. It doesn't leave tags alone — it wipes them. Every row you import must carry the product's full, corrected tag list. - Merging tags that only look like duplicates.
mensandmenmight be the same idea;saleandflash-saleare not. Eyeball the groups before you collapse them. - Stripping a one-off tag that a theme or app depends on. A tag used once isn't always dead — some themes key logic to a specific tag. Search your theme and app settings for a tag before you delete it.
- Doing it once and calling it done. The next import and the next app trial start the sprawl again. Put the script from the first section on a quarterly reminder.
Which method for which cleanup
| Situation | Use |
|---|---|
| A handful of products, a tag or two | Built-in bulk editor |
| One mechanical rename across a catalogue you understand | CSV export → find-and-replace → re-import |
| Years of accumulated tags, nobody remembers the logic | Read the whole set, draft the merges and strips, review |
| One product carrying 40 tags | Open the product and prune it by hand |
FAQ
How do I clean up tags in Shopify?
Start by reading what you have: your store's products.json endpoint lists every product's tags publicly, so a short script (above) shows your worst-tagged products, your one-off tags, and any casing duplicates in about a minute. Then edit — the built-in bulk editor's Tags column for small fixes, a CSV export/import for a catalogue-wide rename, or a tool that reads the whole set and drafts the merges for anything larger.
How do I bulk edit tags in Shopify without an app?
Two built-in ways. Products → select → Bulk edit → Columns → tick Tags gives you a grid to add or remove tags in place. For a catalogue-wide change, export a Plain CSV, edit the Tags column, and re-import with Overwrite products with matching handles ticked. Neither costs anything.
Can I merge two Shopify tags into one?
Not with a single button — Shopify has no "merge tags" action. You do it by replacing the old tag with the new one on every product that carries it: find-and-replace across the Tags column in a CSV export, or edit each product in the bulk editor. Remember to update any collection or theme logic that referenced the old tag.
Are Shopify tags case-sensitive?
As stored values, yes — Sale and sale are two separate tags, and our sample had that split live: 162 tags across 17 of the 41 stores, leaving 552 products on a minority spelling. Whether that casing difference changes what a given collection or storefront filter returns depends on how each condition is configured, so the safe move is to test it on a single tag in your own store rather than assume. Normalising to one casing is what stops the question coming up at all.
How many tags can a Shopify product have?
More than you will ever want to use. We did not test the platform's ceiling, so we won't quote one — what we measured is the practical range: the median tagged product carried 3, a long tail ran past 20, and one product carried 123. We can't say what that cost the store — we read its catalogue from outside, not the storefront its shoppers see. Past a handful, extra tags don't help discovery; they add noise to your filter menu.
Where can I see all the tags on my store?
The tags of every product published to your online store are on the public your-store.myshopify.com/products.json endpoint as an array (your own domain works too) — no login needed. The script in the first section reads that and lists your distinct tags, your one-offs, and your most-tagged products. Inside the admin, there's no single "all tags" view, which is part of why sprawl goes unnoticed.
Do tags affect Shopify SEO?
Not directly — product tags aren't meta keywords and search engines don't read them as ranking signals. They affect SEO indirectly, through the collection pages and filtered URLs they generate: a bloated, duplicated tag set produces thin, near-duplicate collection pages, which is the part worth tidying.
Method
Sample. 41 live Shopify storefronts, read in August 2026. The list they were drawn from is public: stores whose owners had posted their own URL on the Shopify Community's Store Feedback board. The draw was random within that list — the list itself is not. Everything here came from endpoints any browser can fetch — the public products.json catalogue, nothing authenticated.
Whether it looks like you. Stores that post asking for feedback skew newer and smaller. The largest single bias is survivorship, and here is its size: of the 474 candidate domains on the public list, 244 were no longer serving products and were excluded — 118 no longer resolved, 56 returned 404, 31 returned HTTP 402 (Shopify's unpaid-account freeze), 18 were password-protected or no longer Shopify, and the remaining 21 returned other errors (401, 403, 409 and the like). The 230 that were still live are the pool these storefronts were drawn from, so what's left is the stores that survived.
How 230 became 41. From those 230 we drew 60 at random, with a fixed seed so anyone can redraw the same 60. Reading those 60 dropped 19 more: 16 carried fewer than ten products, 2 were larger than our read cap of 5,000 products and would have given a floor rather than a total, and 1 would not return its catalogue. The 41 that remain are every store that survived both steps — we did not choose among them after seeing their tags.
What we counted. Across the 41 stores, 20,604 products — everything those stores publish to their online store channel, which is all products.json exposes. Anything unpublished, still a draft, or restricted to another channel is outside this count, and old tags may well sit there too. A product was "tagged" if it carried at least one non-empty tag: 16,509 did (80.1%). Tag counts are per product; the median across tagged products was 3 and the mean 6.8. 1,291 products (6.3%) carried more than 20 tags; the maximum on any single product was 123. Distinct tags were counted after lowercasing and trimming whitespace, which collapses casing duplicates — 9,674 remained. A "casing split" is a lowercased tag that appeared in more than one exact spelling within the same store; the "minority spelling" count is every product carrying any spelling other than that store's most-used one for that tag — 162 splits and 552 products across 17 stores. Four of the 41 stores had no tagged products at all.
What this isn't. A convenience sample of feedback-seeking stores is not a representative sample of Shopify. Read the numbers as "this is common enough to be worth checking on your own store," which is what the script in the first section is for — not as a platform-wide rate.
Which method fits depends on how much of the tag set you still recognise. If the answer is "not much", that is what Arvio is for.
Written by Adot Technologies Inc, the team behind Arvio: AI Store Operator — it reads your live store, ranks what's costing you, drafts each fix against your real catalogue, and holds every change for your approval.
