Category Archives: JSON-LD

Schema.org: Product Variants with Different Prices

In my last post, I argued that Google is increasingly able to handle multiple product / offer entities in product pages and still generate Rich Snippets for products. Some people have pointed out that the markup in the live pages I cited was misleading, because it “abused” the http://schema.org/offers property and wrongly puts the multiple offers / products into a http://schema.org/SearchResultsPage entity as a container. While I stick to my original prediction that Google will increasingly select or summarize multiple entities from a page to generate Rich Snippets customized to a single query, I want to provide a clean approach for this frequent scenario.

Below, I will explain how you can properly model a product detail page with multiple product variants that have different prices in a way that almost certainly triggers Google Rich Snippets for products.

The main challenge is to get a price range shown in a Rich Snippet for a page with multiple product variants, like so:

Google Rich Snippet for multiple cars

Before we start, it is important to understand that, to my knowledge, Google has previously shown price ranges in Rich Snippets for Products only in one of the following three cases:

  1. For http://schema.org/AggregateOffer, like so:
    {
      "@context": "http://schema.org",
      "@type": "Product",
      "image": "dell-30in-lcd.jpg",
      "name": "Dell UltraSharp 30\" LCD Monitor",
      "offers": {
        "@type": "AggregateOffer",
        "highPrice": "$1495",
        "lowPrice": "$1250",
        "offerCount": "8",
        "offers": [
          {
            "@type": "Offer",
            "url": "save-a-lot-monitors.com/dell-30.html"
          },
          {
            "@type": "Offer",
            "url": "jondoe-gadgets.com/dell-30.html"
          }
        ]
      }
    }
    
  2. For GoodRelations price specifications in RDFa syntax using gr:hasMinCurrencyValue / gr:hasMaxCurrencyValue, like so:
    <div typeof="gr:Offering" about="#offer">
      <div property="gr:name">GoodRelations T-Shirt - Various Designs</div>
      <div rel="gr:hasBusinessFunction" 
         resource="http://purl.org/goodrelations/v1#Sell"></div>
      <div rel="gr:hasPriceSpecification">
        <div typeof="gr:UnitPriceSpecification">Price: 
        <div property="gr:hasCurrency" content="USD">$</div>
        <span property="gr:hasMinCurrencyValue">10.00</span> to $
        <span property="gr:hasMaxCurrencyValue">99.99</span>
        <div property="gr:validThrough" datatype="xsd:dateTime" content="2012-11-30T23:59:59Z"></div> 
      </div>
    </div>
    
  3. For multiple http://schema.org/Offer entities linked to the same product via http://schema.org/offers, like so:
    {
      "@context": "http://schema.org",
      "@type": "Product",
      "image": "dell-30in-lcd.jpg",
      "name": "Dell UltraSharp 30\" LCD Monitor",
      "offers": [
    	{
    		"@type" : "Offer",
    		"price" : "35",
    		"priceCurrency" : "EUR"
    	},
    	{
    		"@type" : "Offer",
    		"price" : "55",
    		"priceCurrency" : "EUR"
    	}
    	]
    }
    

So far, there was no documented markup pattern for Web pages with multiple product variants that have differing prices. Still, there is a straightforward way to achieve this by combining

Before we start, remember that there are two alternative approaches for modeling product and offer information in schema.org:

  1. You can start with the http://schema.org/Product entity and link it to one or more http://schema.org/Offer entities via http://schema.org/offers.
  2. Or you can start with a http://schema.org/Offer entity and link to the http://schema.org/Product included via http://schema.org/itemOffered.

The essential trick is to use both in combination, which is perfectly valid from the underlying GoodRelations model in schema.org:

  1. First, model an abstract version of the product, e.g. omitting size, color, or configuration information, as a http://schema.org/Product.
  2. Then, link this to two or more http://schema.org/Offer entities via http://schema.org/offers.
  3. Finally, link each of these offers to a more detailed product entity via http://schema.org/itemOffered.
    1. Here is a complete example:

      {
      	"@context" : "http://schema.org",
      	"@type" : "Product",
          "description" : "The Hepp Smart Watch is a unique wristwatch.",
          "name" : "Hepp Smart Watch",
      	"offers" : [
      		{
      			"@type" : "Offer",
      			"availability" : "http://schema.org/InStock",
      			"price" : "50",
      			"priceCurrency" : "EUR",
      			"itemCondition": "http://schema.org/NewCondition",
      			"itemOffered" : 
      			{
      				"@type" : "Product",
      		        "description" : "The Hepp Smart Watch in Silver is robust and has enough memory for most tasks.",
      		        "name" : "Hepp Smart Watch in Silver with 16 GB RAM"
      			}
      		},
      		{
      			"@type" : "Offer",
      			"availability" : "http://schema.org/InStock",
      			"price" : "400",
      			"priceCurrency" : "EUR",
      			"itemCondition": "http://schema.org/NewCondition",
      			"itemOffered" : 
      			{
      				"@type" : "Product",
      		        "description" : "The best Hepp Smart Watch ever: Gold-plated and with 64 GB.",
      		        "name" : "Hepp Smart Watch in Gold with 64 GB RAM"
      			}
      		}
      	]
      }
      

      I have not seen this in the wild yet, but I am very confident that it is the best approach for pages with multiple product variants.