September 1, 2026

Shopify store management is a queue, and the admin has no view of it

Shopify store management is a queue, and the admin has no view of it

Key takeaways

  • Store management gets described as a set of habits. It behaves like a queue: a stock of product records that need an edit, and an arrival rate of new ones. Both are countable.
  • The admin filters fields that already have values. Status, vendor, product type, tag, collection. There is no filter for an empty description, which is one of the fields that defines the queue. That is the whole reason this article ships a script.
  • What we found on 42 live storefronts (52,489 products, read 1 September 2026), reported in three groups rather than one number: the median store had 1 product page that is empty or self-contradicting (2.3% of its catalogue, 27 of 42 stores had at least one); adding products nobody can buy takes the median store to 12 records and 12.2%; adding the two fields that only matter if you use them takes it to 62.1%.
  • The 2.3% and the 12.2% are medians of shares; the 1 and the 12 are medians of counts. Four separate orderings over the same 42 stores. Do not multiply one by your own catalogue and expect the other.
  • Bigger catalogues carry a bigger share, not only a bigger count — 10.0% against 13.2% at a 100-product split, 10.2% against 15.4% at 300, 10.3% against 27.0% at 1,000. Removing the single largest catalogue in the sample entirely leaves the direction intact at all three.
  • The median store added nothing in the previous 30 days, and 14 of the 42 added nothing in 90. For most of these catalogues this is an old backlog, not a growing workload, which changes what you should do about it.

Published 1 September 2026 by Adot Technologies Inc, the team behind Arvio. Every storefront number here was read on 1 September 2026 from endpoints any browser can fetch. Method, sample and exclusions at the end.

The quick version

  • Count the stock. The snippet below reads your own storefront and prints the products, not just a total. If a browser console is not somewhere you go, the free audit reads the same surface for you.
  • Split what you find into three groups. Pages that are empty or contradict themselves, products nobody can buy, and fields that only matter if you use them. Only the first is unambiguous.
  • Then check the arrival rate. If you are not adding products, you are clearing a backlog, and a backlog finishes. If you add a hundred a month, you need the count inside the routine instead.

Count your own queue

Open your own storefront in a browser, open the developer console, paste this. It reads the same public endpoint a search engine reads, so it needs no app, no login and no permission. Opening a console is a step, and if it is not a step you want to take, skip to the audit further down — it reads the same thing.

const empty = [], decision = [], optional = [], seen = new Set();
let page = 1, total = 0, incomplete = '';
while (true) {
  if (page > 200) { incomplete = 'stopped at the 200-page safety limit'; break; }
  let products;
  try {
    const r = await fetch(`/products.json?limit=250&page=${page}`);
    if (!r.ok) { incomplete = `page ${page} returned HTTP ${r.status}`; break; }
    ({ products } = await r.json());
  } catch (err) {
    incomplete = `page ${page} did not return usable JSON (${err})`; break;
  }
  if (!products || products.length === 0) break;    // an empty page is the end; a short page is not
  for (const p of products) {
    if (seen.has(p.id)) continue;
    seen.add(p.id); total++;
    const text = (p.body_html || '').replace(/<[^>]*>/g, '')
                                    .replace(/&nbsp;|&#160;|&#xa0;/gi, ' ').trim();
    const vs = p.variants || [];
    const e = [], d = [], o = [];
    if (!text) e.push('no description');
    if (!(p.images || []).length) e.push('no image');
    if (vs.some(v => Number(v.compare_at_price) > 0 &&
                     Number(v.compare_at_price) <= Number(v.price)))
      e.push('compare-at not above price');
    if (vs.length && !vs.some(v => v.available)) d.push('nothing buyable');
    if (!p.product_type) o.push('no product type');
    if (!p.tags || !p.tags.length) o.push('no tags');
    if (e.length) empty.push({ handle: p.handle, why: e.join(' · ') });
    if (d.length) decision.push({ handle: p.handle, why: d.join(' · ') });
    if (o.length) optional.push({ handle: p.handle, why: o.join(' · ') });
  }
  console.log(`page ${page}: ${total} products so far`);
  page++;
  await new Promise(r => setTimeout(r, 400));       // be unhurried with your own storefront
}
console.log(`${total} products read` +
  (incomplete ? ` — INCOMPLETE: ${incomplete}. Every count below is a floor.` : ''));
console.log(`${empty.length} empty or self-contradicting · ` +
            `${decision.length} sold out and still published · ` +
            `${optional.length} missing a product type or tags`);
console.table(empty.slice(0, 50));
console.log(empty.map(x => x.handle).join('\n'));   // the whole list, not the first fifty

The three counts are taken independently, so each one is comparable to the matching row in the table below. If anything interrupts the read — a rate limit, a page that returns HTML instead of JSON — it says so and tells you the counts are floors, because a count like this failing quietly is the normal way it comes out too low.

Three things it does not see: unpublished products, because the endpoint only serves published ones; stock levels, because the endpoint gives a buyable flag per variant and no quantity; and image dimensions against Google's shopping requirements, which is a different question with a different threshold and is in the companion piece.

Three groups, not one number

Every list of "product data problems" mixes things that fail for different reasons, and mixing them produces a headline nobody believes.

Empty or self-contradicting. No description, so the page has nothing to read. No image at all. A compare-at price that is not above the price, which is a sale display that will not display. These do not depend on how you run the store: each one is a page a customer can reach where something that should be there isn't.

A decision you have not made. Every variant unavailable and the product still published. This is not a defect, and we are not going to count it as one — a discontinued line kept live on purpose holds its URL and its search history, and we measured what actually happens when nobody unpublishes it. It belongs in the queue because it is a decision sitting unmade, not because it is broken.

Only work if you use the field. An empty product type and an empty tag list cost nothing on a store navigated by hand-built collections. They cost a lot the day you switch on storefront filters or automated collections, because the filter, not the field, is what looks broken.

Six conditions, three groups. A product counts once within a group no matter how many of that group's conditions it hits.

What that looks like across 42 storefronts

Median store Stores with at least one
Group 1 — empty or self-contradicting 2.3% of the catalogue 27 of 42
↳ no description at all 0.0% 16 of 42
↳ no image at all 0.0% 10 of 42
↳ compare-at not above the price 0.0% 19 of 42
Groups 1 + 2 — the above, plus nothing buyable 12.2% 35 of 42
↳ every variant unavailable, still published 5.4% 33 of 42
Groups 1 + 2 + 3 — plus product type or tags 62.1% 41 of 42
↳ no product type 11.2% 36 of 42
↳ no tags 14.1% 37 of 42

Two rows we counted and deliberately left out of every group, because neither is clearly work: products where some variants are unavailable and the product is still buyable (1.3% at the median store, 29 of 42 stores), and products sharing a title with another product in the same store (187 across the sample, in 22 of 42). The second is sometimes a mistake and sometimes a deliberate second listing, and telling them apart is judgement.

Read the two columns against each other. The median store has no product with a missing description, and yet 16 of the 42 stores have at least one. Both are true, and the pooled count of products with no description — 21,262 across the whole sample — is true and almost useless on its own: 72.3% of those 21,262 belong to a single store, and the next worst has 5,343.

Missing images concentrate harder. 10 stores have any, and 99.4% of the products with none belong to one wholesale catalogue whose products endpoint carries no images at all. The next worst store has 43. That same wholesale catalogue is also the worst store for missing descriptions, missing product types and missing tags — one store sitting behind four of the sample's largest pooled counts. We cannot tell from outside whether it has no images or only serves none through that endpoint, which is a good reason not to build anything on it, and the robustness check below removes it entirely.

So the version of group 1 that survives contact with the store-level column is the one that reads worst in a summary: empty product pages are uncommon across stores and near-total inside the stores that have them. If you are one of those stores you already half-know it. If you are not, the work in your queue is products nobody can buy and sale prices that are not sales.

Does this look like your store?

A benchmark you cannot place yourself against is decoration. Here is the groups 1+2 figure cut by catalogue size at three cut points, chosen before we looked at the split.

Split at Smaller stores Larger stores Larger stores, biggest catalogue removed
100 products 10.0% (n=19) 13.2% (n=23) 12.7% (n=22)
300 products 10.2% (n=26) 15.4% (n=16) 13.2% (n=15)
1,000 products 10.3% (n=35) 27.0% (n=7) 23.4% (n=6)

The share rises with catalogue size at every cut point, and the last column is the check that matters: drop the 21,683-product wholesale catalogue — the store behind four of the pooled counts above — and the direction holds at all three. It also holds for group 1 alone at the 1,000 split, at 0.4% against 20.4%.

One caution about how much that table proves. The three larger-store brackets are nested: the seven stores above 1,000 products appear in all three rows. That makes this one signal read three ways with the sample removed, not three independent tests. It is enough to say the share does not fall as catalogues grow. It is not enough to turn into a multiplier for a store of a given size, so we are not going to give you one.

The arrival rate

We expected the queue to be a flow: products arrive, each slightly incomplete, the backlog grows. That is not what these 42 stores are doing.

The median store created nothing at all in the 30 days before we read it. Across the whole sample 2,593 products were created in that window and 6,349 in 90 days, both concentrated in a handful of catalogues, and 14 of the 42 stores had not created a single product in 90 days. The 90-day median is 4.

For most of these stores the queue is not filling up; it is old. A backlog is finite, which is the good news, and it is also why nobody clears it: work that is not arriving has no trigger, and work with no trigger waits until somebody is annoyed enough to go looking.

If your own arrival rate is high you have the opposite problem and a different fix — the count belongs inside adding a product, not in a separate afternoon.

One measurement we threw away. The obvious way to ask when a record was last touched is each product's updated_at. On 31 August 2026 we measured it across 5,358 products from this same public frame and the median gap was 0 days: the field refreshes for reasons unrelated to anyone editing anything. So it cannot tell you about maintenance, and nothing here uses it. Arrival is measured from created_at, which does not refresh.

What the admin will and will not do

Open Products, sort by when products were added so a batch sits together, select what you want and open the bulk editor — the spreadsheet-style grid where you pick the columns and edit many products in one screen. It is good for short repetitive fields: adding a product type, applying tags, fixing a vendor across a batch.

Finding the work is the part that does not go well. Look at the filter list on your own Products page: as far as we can see, every filter there is on a field that already has a value — status, vendor, product type, tag, collection. We have not found a filter for an empty description, and we could not find a way to sort or filter on it. The queue is defined by the fields that are empty, which are exactly the ones there is nothing to filter on.

We are stating that as what we looked for and did not find, not as a permanent fact about the platform: it is a claim about a user interface that Shopify can change, we have no measurement of it in the way we have measurements of the 42 storefronts, and if there is a filter we have missed we would rather hear about it than keep the sentence.

Doing it by CSV

Export the products, edit in a spreadsheet, re-import with the same handles. This is the right tool when the edit is a rule rather than a judgement: filling every blank product type with one value, normalising a vendor name, replacing a tag across a few hundred rows.

The import matches on Handle, so a changed handle creates a second product instead of updating the first. And a partial re-import is only as safe as the columns you include, so test it on two products before you run it on four hundred: the failure mode people describe is a column that was included with empty cells, which is read as an instruction rather than as "leave this alone". We have not measured that ourselves and would not want you to find out on your live catalogue. The column-by-column version is in our write-up of cleaning a catalogue after an import.

The split that decides what you can hand off

Deciding what a product should say is judgement. Which twenty products get a real description this week, what the category tree ought to be, whether a discontinued line comes back — that is yours, and no software takes it off you.

Working out which products are in which state is arithmetic. So is applying one decision to four hundred records. That half does not need you, and it is the half that never gets done, because the effort scales with the catalogue while the interest does not.

Arvio is built on that line: it reads the live store, ranks what it found, drafts the edits, and applies nothing until you approve. Being plain about what that requires, since everything else in this article deliberately needs no permission at all — it is an installed app with permission to edit your products, which is what makes it able to apply an edit rather than describe one, and its own claim is that nothing is applied without your approval and that edits can be undone. That is a product claim, not one of our measurements, and you should treat it as one until you have watched it work on your own store.

What the free audit tells you, and what it doesn't

If a console is not where you want to spend the next twenty minutes, the free Arvio store audit takes a domain, reads the public storefront across six stages — landing, browsing, product page, cart, checkout, after the order — and prints every finding with the measurement underneath it. On our own 69-product demo store it finished in fourteen seconds and returned seven findings, among them "7 distinct category spellings across 69 products | 19 products with no category at all" and "8 product URLs no longer match their titles — the title was edited, the URL was not."

That is one small store, and a friendly input: it says nothing about how long a 4,000-product catalogue takes. Two things about it are worth more than the findings. It marks each one auto or assisted, which is the judgement-versus-arithmetic line again. And it prints what it did not check, with the reason: on that run, "only 1 collections — too small a sample" and "the sampled products have no apparel/footwear size option — a size chart does not apply."

Its limit is the script's limit. It reads your storefront, not your admin and not Google. It cannot see drafts, stock levels or orders, and that is also why neither of them needs a password.

When leaving the queue alone is the right call

Clearing the queue to zero is usually a week spent in the wrong place.

  • A discontinued line published on purpose. Group 2 exists to keep this out of the defect count.
  • Product type and tags on a store that uses neither. That is what group 3 is for. Fill them in before you switch filters on, not after. If you are not sure which kind of store you are: open a collection and see whether its products were added by hand or by a set of conditions.
  • The long tail of a big catalogue. Twenty products you would most like to be found, fixed properly, beat four hundred products touched.

And the honest limit on all of it: we counted states on pages, and we have not tested that clearing any of them changes traffic or revenue. Counting is worth an afternoon because it replaces a vague sense that the catalogue is untidy with a list of specific records and a decision about each one. If you want a number that predicts revenue, this article does not have it and does not pretend to.

Method

Sample. 42 live Shopify storefronts, read on 1 September 2026, drawn at random from a public list of stores whose owners had posted their own URL on the Shopify Community's Store Feedback board. Everything here came from endpoints any browser can fetch. Merchants who ask strangers for feedback skew newer and smaller — the median catalogue here is 140 products, and the largest is 21,683.

The funnel, in full. 720 board topics read across the listing pages produced 474 candidate hostnames; 230 of those were still serving a public catalogue and 244 were not (unresolvable, 404, 402 payment-required, or password-protected). From the 230 we drew 60 at random with random.Random(20260826).sample, so anyone can draw the same 60. Of those 60 we excluded 16 stores with fewer than 10 published products and 2 that returned no readable public catalogue on the day, leaving 42 stores and 52,489 products.

The frame's bias. The largest one is survivorship: nearly half the candidate hostnames were already gone, so what is left is the stores still trading. The board also skews small. That is why every headline here is a store-level median with size slices next to it rather than one pooled percentage, and it is why "the median store" in this article means the median of these 42 rather than of Shopify.

Collection. Read-only GET requests at one request per second with a self-identifying user agent: the products endpoint for the whole catalogue, plus two product pages per store. No admin access, no authenticated session, no writes. Paging stops on an empty page, never on a short one, with a safety stop well above the largest catalogue here.

What counts. Six conditions in three groups, listed above, plus two counted-but-excluded rows. Every condition is a field that is empty or a pair of fields that contradict each other. Nothing here judges how good a description is, only whether one exists, because a public endpoint cannot tell us your brand guidelines.

Medians. Every percentage in the tables is the median across the 42 stores of that store's own share, and every count is the median of counts. Those are different orderings, so the median count and the median share do not describe the same store and should not be divided into each other.

The snippet and the survey are two implementations. We ran both against the same 69-product catalogue — our own demo store, which is not one of the 42 — and both returned 23 products in group 1, 18 in group 2 and 19 in group 3, each group counted independently. Note what that store looks like: 23 of 69 in group 1 is far worse than any median here, so it is an outlier and not an illustration of a typical store. The comparison is evidence that the two implementations agree, not evidence that the definition is right.

Limits, in one place

  • Published products only. Drafts and archived products are not on the endpoint, so every figure here is a floor for any store with a draft backlog.
  • No stock levels. The endpoint gives a buyable flag per variant and no quantity, so "low stock" is not measurable this way by us or anyone.
  • No alt text, no barcodes, no image dimensions in group 1. The first two are not on the endpoint at all. Image dimensions are, but they belong to a different question with a different threshold, so they are in the companion piece rather than in these counts.
  • The admin claims are not measurements. The filter and bulk-editor statements above are what we looked for and did not find in a user interface, unlike everything about the 42 storefronts.
  • One month of arrivals. A 30-day window on a seasonal catalogue is a snapshot, not a rate: a store that loads its year in two weeks looks idle for the other fifty.
  • Nested size brackets. The three larger-store rows share their stores, so they are one signal read three ways, and the largest bracket is seven stores.
  • Cause and effect, anywhere. We counted states on pages. Nothing here says clearing them changes traffic, conversion or revenue.

FAQ

What does Shopify store management actually involve?

The visible half is orders, customers and stock, and it has screens in the admin. The invisible half is the queue in this article: product records missing a field, contradicting themselves, or describing something nobody can buy. The second half has no screen, which is why it gets described as "keeping things tidy" rather than as a measurable amount of work.

Can I find the products that need work from inside the Shopify admin?

Partly. You can filter by status, vendor, product type, tag and collection, and sort by date added — all fields that already have values. We could not find a way to filter on an empty description, which is the condition that defines the queue. That is why the list in this article comes from the storefront JSON instead.

How big does a catalogue have to be before this matters?

It is less about the count than about whether one person can still hold the catalogue in their head. The size slices are the useful part: the share of the catalogue needing work rises with catalogue size at all three cut points we tested, and it survives removing the largest catalogue in the sample.

Is /products.json safe to use on my own store, and can anyone read mine?

It is a public read-only endpoint every Shopify storefront serves — the same one search engines and shopping feeds read. Reading it changes nothing. It is also worth knowing in the other direction: your published catalogue, including your compare-at prices, is readable by anyone who knows the URL, which is how the numbers in this article exist.

Should I bother filling in product types and tags?

Only if something depends on them, which is why they are group 3. Automated collections, storefront filters and navigation do; a hand-built menu does not. If you plan to switch filters on later, fill them in before you do, because the failure will present itself as broken filters rather than as an empty field.

What is the fastest way to fix a few hundred products?

The bulk editor for short repetitive fields, a CSV re-import for rule-shaped changes across many rows, and a per-product pass for anything that needs writing. The common mistake is using the third for work that is really the first.

Does any of this affect how my products show up on Google?

Some of it, and not the parts you would guess. The shopping-feed side of the same catalogue — image sizes, titles, identifiers — is in the companion piece, and the organic-search side is in why your Shopify products aren't showing on Google.

How often should I re-count?

The arrival rate answers this for your store rather than in general. If you add a hundred products a month, the count belongs in a monthly routine. If you add four in a quarter — which is the 90-day median across these 42 stores — you are clearing a backlog, and a backlog mostly needs counting once, at the start.

Arvio: AI Store Operator

Other AI tools give you a to-do list.
Arvio's AI store agent does the work.

Arvio scans your Shopify store daily, finds what needs fixing — SEO, prices, stock, product content — and drafts the fix. You approve it, Arvio ships it. Every edit can be undone.

Install on the Shopify App Store
4.9★ rated on Shopify App StoreFree trial availableEvery edit reversible