It’s an unexpected battlefield. Schema.org is a mutual effort amongst search engines to create a code meant to deliver essential information about a webpage in the most succinct and direct manner possible. It’s reasonable to expect structured data that works for Google Search should work for Product Listing Ads (PLAs) in Google Merchant, right?
We discovered incongruity between Google Search Product structured data and Google Merchant Product structured data.
Offer vs. AggregateOffer
The markup for a product page seems straightforward at first. You’re selling a widget and that widget has a price, name, availability, image, etc. One widget, one price, and one offer in the eyes of structured data. This is when you use the “Offer” type with the “offers” property. Using the example code provided by Google, we can test it with the Rich Results Test and see an example of what this looks like in the SERP (Search Engine Results Page).
Often a merchant will sell widgets that vary in size and price. This is when you use “AggregateOffer”—one widget, several prices, and several offers in the eyes of structured data. Schema allows for price variances based upon color as a property, not size. Given the number of products that vary in price due to size, this seems like a big omission.
Represent a range of prices (product variants)
Product variants are marked-up with the “AggregateOffer” type with the “offers” property. The most common example is when multiple sellers are offering the same product on one page, but that’s not the only use case. Every product variant is an offer. The small, medium, and large widgets are actually three individual products on offer. Again, using Google’s example code, we can test it with the Rich Results Test and see an example of what this looks like in the SERP.
Unlike the “Offer” type, “AggregateOffer” displays a range of prices in the rich results. This is what should be done for Google Search, but it can’t be done with Google Merchant.
Google Merchant is looking to match the PLA listing price with a price on the page and in the code. “AggregateOffer” has “lowPrice” and “highPrice”. “AggregateOffer” doesn’t list all the possible prices; therefore, Google Merchant cannot properly match the listing price with a price in the code.
What happens when Google Merchant can’t properly match the listing price with a price in the code and on the screen? The PLAs are rejected due to mismatched price.
When this happened to a client, the Merkle Feeds and SEO teams investigated and discovered a few issues:
- The page contained product variants, but the structured data only listed one product and price.
- A dropdown menu hid the correct on-screen price.
- The preselected product variant didn’t match the product variant targeted by the PLA.
Typically, the initial response is to turn on automatic price matching. Google Merchant accepted that fix, but it displayed the incorrect price due to the product variant pre-selection and erroneous structured data. Users would click on an ad showing one price and be greeted with a different price on the landing page. Had the ads run for any length of time, this would have hurt the conversion rate and served a terrible user experience.
After checking other brands in various industries, it quickly became apparent that this is a wide-spread problem.
Fixing the structured data emerged as the most direct fix to (hopefully) resolve the problem.
Structured data for product variants for Google Merchant
Just like curly brackets denote an array formula in Excel, brackets denote an array or grouping in JSON-LD. There are two different types of brackets in JSON-LD; curly brackets and square brackets.
Using Google’s example code for product variants in the Structured Data Testing tool, we see that it detects two sets of Product schema.
Note the opening square bracket just after the opening script tag. There’s a corresponding closing square bracket just before the closing script tag. If we remove the square brackets, the testing tool only detects one set of Product schema.
How to markup JSON-LD structured data for product variants for Google Merchant:
- Wrap the code in square brackets
- Write Product schema for every variant
- Use “Offer” type schema for the “Offers” property.
- Make sure the Structured Data Testing Tool detects Product schema for every variant.
Structured data for product variants for Google Search
Google Search needs “AggregateOffer” as the Offer type to properly display the range of prices in the rich results. Don’t use the square brackets in this version of the code. Using Google’s example code in the Structured Data Testing Tool, we see that it detects one set of Product Schema when AggregateOffer is used.
How to markup JSON-LD structured data for product variants for Google Search:
- Do not wrap the code in square brackets
- Write one Product Schema or use a schema generator
- Use “AggregateOffer” type schema for the “Offers” property
- Make sure the Structured Data Testing Tool detects one set of Product schema
Can a webpage use “Offer” and
“AggregateOffer” simultaneously?
For tracking purposes, PLAs’ target URLs usually include a noindex or canonical tag and are served with a parameterized URL in ad campaigns. This isn’t usually a problem “in the wild.” We have not tested using both styles of markup on a single page in real-world settings. Google’s documentation does not support running both types of code on one page, but the Structured Data Testing Tool validates with both markups. The Rich Results Test says that the code is eligible for rich results, but the preview doesn’t show the proper rich results. Due to a lack of a preview in the Rich Results Test and the existence of documentation from Google specifying what to use, we don’t recommend running both types of markup on one page.
What were the results of the updated markup?
The client implemented the Google Merchant version of the code on the noindex, parametrized URLs used specifically with PLAs and deployed the Google Search version of the code on indexable pages that already appeared in Google Search results. The correct rich results showed in the SERP less than 24 hours later. The price mismatch rejection from Google Merchant remained.
The support team at the Google Merchant Center informed us that they rejected the PLAs due to the wrong variant being pre-selected.
The developers updated the code so that the pre-selected variant matched the PLA and Google Merchant approved the ads. The ads were approved with:
- Google Merchant version of Product Schema with correct prices
- Correct pre-selected product variant on the landing page
- Correct on-screen price
Would correcting the pre-selected product variant have solved the problem by itself? What role did the structured data play in this?
A few weeks later, the prices changed. Again, Google Merchant rejected the PLAs due to price-mismatch. The ads were rejected with:
- Google Merchant version of Product Schema with incorrect prices
- Correct pre-selected product variant on the landing page
- Correct on-screen price
This indicates that, as the documentation says, Google looks to structured data for the price and compares it to what’s on the screen. All three elements (the structured data, on-screen price, and preselected variant) work in conjunction to resolve the price-mismatch issue. Moving forward, the price in the structured data will be programmatically updated to help prevent this problem and maximize up time for the PLAs.