Merchlint
Available now
Next, in this order
  • DeutschDE
  • EspañolES
  • FrançaisFR
  • ItalianoIT
  • PortuguêsPT
  • NederlandsNL
  • SvenskaSV
  • TürkçeTR

A language shows up here once its pages are actually written. Nothing on this site is machine-translated.

Install free

Disapproved in Google Merchant Center? The WooCommerce fields behind it

Two symptoms, one cause. Fewer products in the feed than your shop holds, and items disapproved after upload, both usually come down to a field in the product — and you can find every one of them from the command line, without installing anything.

Google Merchant Center is where a shop sends its product feed — the list that Google Shopping listings and Shopping ads are built from. Two different things go wrong on the way, and from the outside they look alike.

A product can be missing from the feed — never exported at all, because the shop gave the feed plugin nothing to export. Or it can reach the channel and be disapproved there. Either way the feed plugin is usually just the messenger: the requirement that failed is a field in the product, not a line in the feed.

Knowing which of the two you have saves a day. If the feed simply holds fewer products than your catalogue, start with price, availability and visibility — an item with no price, with nothing in stock, or hidden from the catalogue is dropped before the export ever happens, and no feed setting will bring it back. If the products are in the feed and the channel rejects them, start with images and identifiers.

This page goes through those requirements one at a time — what each one is, what breaks it in a WooCommerce catalogue, and how to find it yourself. Every check below is a command you can run today, on your own store, with no plugin and no account.

The percentages come from Zero clean: 72 WooCommerce stores, where we scanned the public catalogues of 72 Polish shops; the per-shop data is published under CC BY 4.0. Replace wp_ with your own table prefix.

Price — an item with no price is not an offer

Google requires price for every product. WooCommerce will publish a product with the price field empty without a word of warning; on the storefront it simply cannot be bought.

38 % of the stores had products with no price or no way to buy them — 1 138 items. Of the three figures quoted on this page, the report marks this one as the least reliable, and says why.

wp db query "SELECT p.ID, p.post_title FROM wp_posts p LEFT JOIN wp_postmeta m ON m.post_id = p.ID AND m.meta_key = '_price' WHERE p.post_type = 'product' AND p.post_status = 'publish' AND (m.meta_value IS NULL OR m.meta_value = '' OR m.meta_value = '0')"

Availability — the feed says in stock, the shop says no

availability has to match what a customer actually finds. Two states break it, and they need two different queries.

The first is the simple one — everything currently out of stock:

wp db query "SELECT p.ID, p.post_title FROM wp_posts p JOIN wp_postmeta m ON m.post_id = p.ID AND m.meta_key = '_stock_status' WHERE p.post_type = 'product' AND p.post_status = 'publish' AND m.meta_value = 'outofstock'"

The second is the one that hides: a variable product whose every variation is sold out. The parent still reports itself as in stock, so it looks fine in a listing and in the query above, and a customer who clicks it finds nothing they can put in a basket.

wp db query "SELECT par.ID, par.post_title FROM wp_posts par \
  JOIN wp_posts v ON v.post_parent = par.ID AND v.post_type = 'product_variation' AND v.post_status = 'publish' \
  JOIN wp_postmeta s ON s.post_id = v.ID AND s.meta_key = '_stock_status' \
  WHERE par.post_type = 'product' AND par.post_status = 'publish' \
  GROUP BY par.ID, par.post_title HAVING SUM(s.meta_value = 'instock') = 0"

Neither query knows whether the item ever sold — that needs the order tables, and it is the difference between a list of 400 out-of-stock products and the four that were earning money until they ran out.

Image — no picture, no listing

image_link is required, and three different states end the same way: the product has no image at all, the database row points at a file no longer on disk, or the file is there but is empty or a few pixels across.

29 % of the stores had products with no image of their own — 421 items.

wp db query "SELECT p.ID, p.post_title FROM wp_posts p LEFT JOIN wp_postmeta t ON t.post_id = p.ID AND t.meta_key = '_thumbnail_id' WHERE p.post_type = 'product' AND p.post_status = 'publish' AND (t.meta_value IS NULL OR t.meta_value = '' OR t.meta_value = '0')"

For the other two, wp media regenerate --dry-run walks the library and complains about every file it cannot read.

Description — too short to be an offer

description is required, and a one-line description is treated as one. It is also the field that decides whether the person who clicked through finds an answer or leaves.

wp db query "SELECT ID, post_title, CHAR_LENGTH(post_content) AS znakow FROM wp_posts WHERE post_type = 'product' AND post_status = 'publish' AND CHAR_LENGTH(post_content) < 200 ORDER BY znakow"

link must lead to a page a customer can see. A product that sold in the past and was later hidden from the catalogue, or set back to draft, still sits in the feed source while its page is gone.

wp db query "SELECT p.ID, p.post_title FROM wp_posts p JOIN wp_term_relationships tr ON tr.object_id = p.ID JOIN wp_term_taxonomy tt ON tt.term_taxonomy_id = tr.term_taxonomy_id JOIN wp_terms t ON t.term_id = tt.term_id WHERE p.post_type = 'product' AND p.post_status = 'publish' AND tt.taxonomy = 'product_visibility' AND t.slug = 'exclude-from-catalog'"

A unique id — two products, one identity

Google wants id unique for each product, and most feed plugins map it to the SKU. Two products sharing an SKU means one of them loses.

10 % of the stores had a duplicated SKU — 134 items.

wp db query "SELECT meta_value AS sku, COUNT(*) AS n FROM wp_postmeta pm JOIN wp_posts p ON p.ID = pm.post_id WHERE pm.meta_key = '_sku' AND pm.meta_value <> '' AND p.post_type IN ('product','product_variation') AND p.post_status = 'publish' GROUP BY meta_value HAVING n > 1"

Variations are included on purpose: a feed exports each variation as its own item, so a variation sharing an SKU with a product is a real collision there. Worth knowing before you compare numbers with any tool: in WooCommerce, Merchlint’s own duplicate checks — SKU and GTIN alike — compare published products only, so they can report fewer pairs than the two duplicate queries on this page.

If your shop is multilingual, translations of one product legitimately share an SKU. Those are one product, not a duplicate — count the translation groups, not the posts.

GTIN — the number that matches your offer to the same product elsewhere

gtin is strongly recommended if available, and mpn becomes required when a product has none. In practice an item with neither tends to end up alone: no competing offers on the listing, no reviews, and a much weaker position.

Video, 1:11 — WooCommerce GTIN: why products drop out of Google Shopping

WooCommerce has had the field since 9.2 — Product data → Inventory → GTIN, UPC, EAN, or ISBN. Nothing in the admin points at it, and nothing tells you when two products carry the same number.

wp db query "SELECT p.ID, p.post_title FROM wp_posts p LEFT JOIN wp_postmeta g ON g.post_id = p.ID AND g.meta_key = '_global_unique_id' WHERE p.post_type IN ('product','product_variation') AND p.post_status = 'publish' AND (g.meta_value IS NULL OR TRIM(g.meta_value) = '')"
wp db query "SELECT TRIM(meta_value) AS gtin, COUNT(*) AS n FROM wp_postmeta pm JOIN wp_posts p ON p.ID = pm.post_id WHERE pm.meta_key = '_global_unique_id' AND TRIM(pm.meta_value) <> '' AND p.post_type IN ('product','product_variation') AND p.post_status = 'publish' GROUP BY TRIM(meta_value) HAVING n > 1"

On a variable product the identifier belongs on each variation — each size and colour is a separate trade item with its own number, and a variation does not inherit the parent’s. A parent with a number and six variations without one exports as six items with no identifier.

If your shop does not use identifiers at all — handmade goods, services, your own production — there is nothing to invent, and inventing is the worse mistake: a made-up number collides with a real product somewhere, and the offer gets rejected rather than merely ignored.

There is one thing to set in that case, and it belongs to the feed rather than to the product. Google expects identifier_exists to be no when an item has neither a GTIN nor a combination of MPN and brand, and most feed plugins can set it — as a fixed value or per product. The reverse is a trap as well: sending no for a product that does carry an identifier can get it disapproved.

What this page does not check

Being straight about the edges is the point of the page. These are real requirements that nothing above will find for you:

RequirementWhy it is not here
brandRequired for most new products, but WooCommerce has no single native field for it — it lives in a taxonomy or in a plugin, so there is no honest one-line check.
mpnRequired when a product has no GTIN. Same reason: no native field.
A GTIN kept outside WooCommerce’s own fieldThe query above reads _global_unique_id, the field WooCommerce added in 9.2. A shop that keeps the number somewhere else — a dedicated EAN plugin, Yoast’s WooCommerce fields, a product attribute such as pa_ean — will look entirely empty to it. Check where your feed maps gtin from before you trust the count. And the reverse trap: if your feed plugin lists WooCommerce’s field only under a generic “custom attributes” group, that list may be cached — and saving a product does not refresh it — or built from a single product, the most recently published one. A filled field can then stay invisible in the mapping until you clear the plugin’s attribute cache; do that first, before concluding the field is empty.
Price matching the landing pageNeeds the feed and the rendered page compared side by side. A database query cannot see what the page shows after tax, currency and dynamic pricing.
Title matching the landing pageThe same. Length can be measured; agreement cannot.

Image resolution deserves a note as well: Merchant Center begins enforcing a 500 × 500 px minimum on 31 January 2027, and undersized images are a common quiet rejection.


Everything above is a query you run yourself, on your own database. If you would rather have these checks run over the whole catalogue at once — with counts, the list of affected products and what each one costs — Merchlint does that. It is free, it reads only, and it changes nothing in your shop. The one thing it leaves to you is the third image state above — a file that exists but cannot be displayed — which wp media regenerate --dry-run finds.