I am setting a knowledge graph, data being linked though the JSON-LD format. Currently the data structures are stored in separate scripts.

I am trying to figure out about the most appropriate way to distribute the data strutures among web pages from different websites, link them together, and avoid duplication of data structures.


  • Local business (LocalBusiness), with a corporate website.

  • Several specialized services, with one website per service.

  • Each service has several offers (offerCatalog).

    Example: - The sport equipment rental service can rent different models of ice skates or skis. - The sport equipment maintenance service can string rackets and sharpen ice skates

  • Several category pages

    Example: On the ski rental page, all ski models are listed.

  • One or several offers per "product" page.

    Example: One model of ski can be rent with several durations.

Considered way of distrinbuting JSON+LD data structures among pages

Although all te knowledge graph can be stored in a unique file, I assume it is more appropriate for maintenance reasons to have each web page describe its own content.

In this regard, the location that I am currently considering for each data structure is:

  1. LocalBusiness: on the corporate website, "About us" page if any, else on home page.

  2. Service: on the service website, "About us" page if any, else on home page.

  3. itemListElement: on each category page (e.g. list of ski models for rental), or bigger list on home page.

  4. OfferCatalog: on the homepage of each Service website

  5. Products for rental being described as Thing.

  6. Offer or AggregateOffer on each "product" page.

    Example: AggregateOffer on each "product for rental" page, with one offer per rental duration, each offer with a different leaseLength.

  7. WebPage: one such structure on each web page. mainEntity set to the data structure that describes the best the page.

    Example 1: mainEntity = AggregateOffer for a product rental page with several durations.

    Example 2: mainEntity = itemListElement for a category page with several products

    Example 3: mainEntity = Service for the home page of a specialized service website (linking OfferCatalog on same page)

Feedback wanted

I would appreciate some feedback about the approach described above. Does it make sense? What are the possible shortcomings? What should be considered differently?


I also have the following questions:

  1. Although textual id are in theory possible, Schema.org's validator tends to convert them into URLs that often do not exist. Should I renounce to setting "id" whenever possible and link structures through the "url" attribute instead? In case "id" would be mandatory, should I preferably set is as an exisiting URL ?

  2. What to do when a page is not yet ready? Put the data structure at the best alternative location and update in later time ? (Example: Corporate website not yet ready, put the LocalBusiness information on each Service websites ?)

  3. OfferCatalog: better have all the catalog with one unique large itemListElement on the Service home page or possibly have several itemListElement, one per category page, and consolidating them as an array of itemListElement (if that is possible)?

  4. AggregateOffer(s): linked from the OfferCatalog: is this the way to go for rentals of the same object with different durations (leaseLength) or should better put all rental durations as separate offers in the OfferCatalog ? (Not sure if AggregateOffer is suitable for rental with several durations, or it was designed for price comparison tools with several sellers in mind.)

1 Answer 1


It generally looks good.

Why not mark up the rental as a Product and use that as the mainEntity?

Google considers AggregateOffer as a price range for a product offered by multiple merchants. I think your use it to show a price range for different versions of the same product from the one merchant. You might find minPrice and maxPrice in PriceSpecification more appropriate.


@id values should be URIs. If they are just some text then the system will convert them to a URI based on the URL for the page. A good practice is to start them with a #, e.g. #Product, which leaves the resulting URI valid.

When referencing entities on other pages you want to fully qualify the @id with the URL the primary version of the entity is on.


Maybe put them on a different page but set the @id to where it will end up.


I don't know any consumers of OfferCatalog, so I would do what feels right. If you do split them up between pages you could cross reference them with @ids. The official example puts OfferCatalogs in the list of other OfferCatalog to group them.


Maybe just list multiple Offers in the Product for each option.

The new OfferForLease may be of use to you.

The following example also shows a way to indicate how long the rental is:


  • Thanks Tony for your answer and useful suggestions. "Offer" appears to me as the base entity, as it supports businessFuntion, leaseLength, price, areaServed, availability, inventoryLevel attibutes (and others) and can be linked to Service. Basically, AggregateOffer seems suitable for grouping offers. If Google already uses it to compare offers from several merchants, future rich snippets could still use AggregateOffer to show rental prices of the same object, from the same merchant, with different durations. OfferForLease inherits all its attributes from Offer, making it an alias or subset.
    – OuzoPower
    Commented Feb 19 at 13:01
  • The concern is that using AggregateOffer will make the product ineligible for the Merchant Listings, which only accepts products from one merchant. "Product snippets accept an Offer or AggregateOffer but merchant listings require an Offer as the merchant has to be the seller of the product in order to be eligible for merchant listing experiences." "Do not use AggregateOffer to describe a set of product variants." "For merchant listing experiences, use Offer and not AggregateOffer" developers.google.com/search/docs/appearance/structured-data/… Commented Feb 28 at 22:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.