Key takeaways
- Missing GTIN or MPN is the most common reason individual products get disapproved.
- Google compares your feed against your landing page. A price mismatch can suspend the whole account.
- Every variant needs its own
idand a shareditem_group_id. - A scheduled fetch URL beats a manual upload, because the failure mode of "I forgot" is account suspension.
Merchant Center rejections are rarely mysterious. Google publishes exactly what it wants, checks it mechanically, and disapproves anything that does not match. Almost every rejection falls into one of four buckets: a missing required attribute, a missing product identifier, a mismatch between the feed and the landing page, or a policy issue with the product itself.
Here is how to build a WooCommerce feed that clears all four.
The required attributes
For a standard retail product sold online:
| Attribute | Requirement | Notes |
|---|---|---|
id | Required | Unique and permanent. Use the SKU, never a database ID that changes on re-import. |
title | Required | 150 characters max. Brand plus product plus key attribute performs best. |
description | Required | 5,000 characters max. Strip HTML. No promotional text. |
link | Required | Full product URL including https. Variant-specific where applicable. |
image_link | Required | Minimum 100x100, no watermarks, no promotional overlays. |
availability | Required | in_stock, out_of_stock, preorder, backorder |
price | Required | Value plus currency: 24.99 USD. Must match the landing page. |
brand | Required for most | Omit only for genuinely unbranded goods. |
gtin or mpn | Conditionally required | See below. This is the big one. |
condition | If not new | new, refurbished, used |
Apparel and accessories add color, size, age_group, gender and size_type. If you sell clothing and skipped these, that is your disapproval.
Product identifiers, the number one rejection cause
Google wants to know which real-world product each listing refers to, so it can group offers and show comparison results. It infers this from unique product identifiers.
The rule:
- If the product has a manufacturer-assigned GTIN (a UPC, EAN or ISBN), you must supply it. Omitting a GTIN that exists is a disapproval, not a warning.
- If you manufacture the product yourself and there is genuinely no GTIN, supply
brandplusmpnand setidentifier_existstono. - Do not invent GTINs. Google validates the check digit, and a wrong one is worse than an absent one.
In WooCommerce, GTIN lives in the barcode field on modern versions, or in a custom field on older ones. If your data came from a migration, check it survived: barcode is one of the fields most commonly dropped in a CSV round trip.
id must be permanent. Google builds performance history against it. If your feed regenerates ids on each import, every product looks brand new every time, and you lose all accumulated performance data. Use the SKU.
Variants
Anything a customer chooses between needs its own feed entry. A shirt in four sizes and three colours is twelve entries, not one.
Each entry needs:
- Its own unique
id, typically the variation SKU. - A shared
item_group_idacross the whole group, typically the parent SKU. - Its own
link, ideally with the variation pre-selected:?attribute_pa_size=large. - Its own
price,availabilityandimage_linkwhere these differ. - The distinguishing attributes filled in:
size,color,material,pattern.
Submitting only parent products is a common shortcut that costs you impressions, because Google cannot match a size-specific query to a listing that has no size.
Price and availability mismatch
Google crawls your landing pages and compares what it finds against what you submitted. A discrepancy triggers a warning, then a disapproval, and persistent mismatches can suspend the account.
The usual causes are all timing problems:
- The feed refreshes daily, prices change hourly.
- A sale started and the feed still carries the old price.
- Stock ran out and the feed still says
in_stock. - Currency or tax display differs between feed and page.
This is why refresh frequency is not a nice-to-have. If your prices or stock move, a daily feed is not enough, and a feed you upload by hand is a suspension waiting for a busy week.
Use XML, not a spreadsheet
Merchant Center accepts RSS 2.0 and Atom 1.0 XML from a URL it fetches on a schedule. This is better than uploading a file for reasons that have nothing to do with the format itself:
- Google pulls on a schedule, so there is nothing to remember.
- XML has unambiguous encoding and no delimiter problems, which removes an entire class of CSV failure.
- Nested attributes such as shipping and tax are expressible without inventing column conventions.
A minimal item looks like this:
<item>
<g:id>NB-TEE-M-BLK</g:id>
<g:item_group_id>NB-TEE</g:item_group_id>
<title>Northbound Premium Cotton Tee, Black, Medium</title>
<description>Heavyweight 240gsm combed cotton tee...</description>
<link>https://example.com/product/cotton-tee?attribute_pa_size=medium</link>
<g:image_link>https://example.com/img/tee-black.jpg</g:image_link>
<g:availability>in_stock</g:availability>
<g:price>24.99 USD</g:price>
<g:brand>Northbound</g:brand>
<g:gtin>0123456789012</g:gtin>
<g:condition>new</g:condition>
<g:size>Medium</g:size>
<g:color>Black</g:color>
</item>
Generating the feed
Which route you need comes down to a single question: where does the catalogue come from?
A Scrapify live feed
Scrape any WooCommerce or Shopify store, save the result as a live feed, and you get an XML URL that refreshes every hour. Point Merchant Center's scheduled fetch at it and the price and availability mismatch problem above stops being something you manage.
This is the option that works when the products are not natively yours: a supplier catalogue, a dropship range, a store you are mirroring. A feed plugin cannot help there, because it can only publish products that already exist in your WordPress database. Scrapify populates the catalogue and publishes the feed, and the same feed URL drives our WordPress import plugin, so one feed both stocks your store and advertises it.
Included on every account: free accounts get 1 feed up to 15 products, Premium raises that to 5 feeds of up to 100,000 products.
A feed plugin
If every product already lives in your WooCommerce database and you only need to reshape it into Google's attribute names, a feed plugin does that job. It reads your existing products and generates an endpoint.
The limits worth knowing before you buy one: it publishes only what is already in your store, it cannot fix missing GTINs or thin descriptions, and refresh frequency is bounded by WP-Cron, which on a low-traffic site fires late or not at all. That last one is how a "daily" feed quietly becomes a weekly one and your account picks up mismatch warnings.
Build it yourself
A WordPress endpoint that queries products and echoes XML. Total control, and you own the maintenance forever. Worth it only if your requirements are genuinely unusual.
A feed that refreshes itself
Turn any store into an hourly auto-refreshing XML feed and point Merchant Center at it. Included on every Scrapify account.
Getting approved faster
- Submit a 10 product test feed first. Fix what it flags before submitting 5,000 and receiving 5,000 errors.
- Complete the account setup. Verified and claimed website, shipping settings, returns policy, tax. Google will not approve products in an incomplete account regardless of feed quality.
- Match the landing page exactly. Same price, same currency, same availability, same product.
- Fix the Diagnostics tab weekly. Warnings become disapprovals if ignored.
- Write titles for search, not for shelves. "Northbound Premium Cotton Tee, Black, Medium" beats "Premium Tee" because it contains the words people type.
One feed, several uses
Once the feed exists it is worth more than Merchant Center alone. The same URL typically feeds Facebook and Instagram catalogues, Pinterest product pins, Bing Merchant Center, affiliate networks, and comparison shopping engines.
Each platform wants slightly different attribute names, but the underlying data is identical, and getting it right once pays out across every channel.