Structured Data

Schema.org and JSON-LD: complete guide to Organization, Product, ratings and photos

Article cover: Schema.org and JSON-LD — complete structured data guide

Complete Schema.org structured data guide: JSON-LD vs Microdata formats, schema priority for [rich results](/glossary/rich-results), Organization and all subtypes, Product with Offer and AggregateOffer, ratings, reviews and photos — with working code.

Schema.org structured data is a vocabulary that search engines use to understand page content. Implemented correctly, it turns an ordinary snippet into a rich result — with star ratings, product prices, business hours or photos displayed directly in Google's search results.

This article covers the complete system: from choosing the right format and prioritising schemas to concrete JSON-LD code for Organization, Product, AggregateRating, Review and ImageObject — with special attention to the subtypes most often overlooked.

The JSON-LD path: markup inside <script> → Google parses the structure → rich snippet with rating stars and price right in the SERP.
Google officially recommends JSON-LD as the preferred format. Unlike Microdata and RDFa, JSON-LD sits inside a <script> tag and does not embed into HTML markup — making it easier to maintain and update independently from the layout.

What is Schema.org and why use structured data

Schema.org is a joint project of Google, Bing, Yahoo and Yandex launched in 2011. The vocabulary contains over 900 data types and thousands of properties. All types inherit from the base Thing and branch into Person, Organization, Product, Event, CreativeWork, Action and others.

+36%

CTR lift from rich results

Snippets with star ratings and other rich elements consistently show higher CTR compared to plain snippets

900+

Types in Schema.org

The vocabulary covers everything from recipes and products to scientific papers and software

JSON-LD

Google's recommendation

Google officially recommends JSON-LD for all new structured data implementations

0

Direct ranking impact

Markup does not directly boost rankings, but rich results raise CTR — which indirectly improves ranking signals

Schema.org markup solves three problems at once. For search engines — it unambiguously identifies entities: this is an organisation, not just text with a company name; this is a product price, not just a number. For users — rich results in the SERP make snippets more informative before the click. For the business — higher CTR and presence in the Knowledge Graph.

Formats: JSON-LD, Microdata, RDFa

Schema.org structured data can be added in three ways. For new projects the choice is clear — JSON-LD. The others are supported but harder to maintain.

FormatWhere it livesHTML couplingGoogle recommendation
JSON-LD<script type="application/ld+json"> tagSeparate from HTMLPreferred
Microdataitemscope, itemtype, itemprop attributesEmbedded in HTML elementsSupported
RDFavocab, typeof, property attributesEmbedded in HTML elementsSupported
JSON-LD is the clear choice for new projects: the code is isolated from the layout, easy to generate dynamically, and straightforward to update without touching HTML templates. Microdata and RDFa are supported by Google, but any layout change can silently break the markup.

The base JSON-LD template is the same for any page: a <script> tag with type application/ld+json, containing a JSON object with the mandatory @context and @type fields. The tag can go in <head> or at the end of <body>. Multiple schemas — multiple separate <script> blocks.

HTML
<!-- Base JSON-LD template -->
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "WebSite",
  "name": "Site Name",
  "url": "https://example.com"
}
</script>

<!-- Multiple schemas — multiple <script> blocks -->
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Organization",
  "name": "Company",
  "url": "https://example.com"
}
</script>

Schema priority: what gives rich results in Google

Not every Schema.org type produces a rich result. Google supports a limited set of types that give visual enhancements in the SERP. Other schemas help Google understand content but do not change the appearance of the snippet.

Business and organisations

LocalBusiness, Restaurant, Store — business hours, address, phone, ratings in the Knowledge Panel. Top priority for local businesses.

Products and prices

Product + Offer — price, availability, ratings directly in the snippet. Google Shopping and rich snippets. Essential for e-commerce.

Content and FAQ

Article, FAQPage, HowTo — FAQ expands directly in search results, giving much more SERP real estate for your page.

Ratings and reviews

AggregateRating + Review — star ratings in the snippet. The most visually prominent element. Works for Product, LocalBusiness, Recipe, Course.

Schema typeRich result in GooglePriority
Product + OfferPrice, availability, ratings in snippet; Shopping cardsCritical for e-commerce
LocalBusiness + AggregateRatingKnowledge Panel, Map Pack, starsCritical for local business
FAQPageExpandable questions beneath the snippetHigh for content pages
Article / NewsArticleTop Stories carousel, publication dateHigh for media and blogs
BreadcrumbListBreadcrumbs instead of URL in snippetHigh for any site
HowToStep-by-step instructions with photos in SERPMedium for tutorial pages
EventEvent card with date and locationMedium for event pages
RecipeRecipe card with photo, time, caloriesMedium for cooking sites
Organization (non-Local)Knowledge Graph, logo in resultsBaseline for all sites

Organization — base schema and all subtypes

Organization is the base type for any company, organisation or brand. The subtype hierarchy is broad — each subtype adds specific fields. Choosing the right subtype matters: Google uses it to determine the business context and what to display in the Knowledge Panel.

TypeFor whomKey additional fields
OrganizationAny company, NGO, brand without a physical addressname, url, logo, sameAs
LocalBusinessAny business with a physical addressaddress, telephone, openingHours, geo
StoreRetail storecurrenciesAccepted, paymentAccepted
FoodEstablishmentCafe, restaurant (base type)servesCuisine, menu, hasMap
RestaurantRestaurantservesCuisine, menu, acceptsReservations
CafeOrCoffeeShopCoffee shopservesCuisine
FastFoodRestaurantFast foodservesCuisine
MedicalOrganizationMedical institution (base)medicalSpecialty
HospitalHospitalavailableService, medicalSpecialty
MedicalClinicClinicmedicalSpecialty, availableService
DentistDental practiceavailableService
LegalServiceLaw firm
LawyerAttorney / lawyer
AccountingServiceAccounting / audit firm
FinancialServiceBank, insurer, investment firm
RealEstateAgentReal estate agency
AutomotiveBusinessCar dealer, auto repair (base)
AutoDealerCar dealership
AutoRepairAuto repair shop
HotelHotelamenityFeature, checkinTime, checkoutTime
SportsActivityLocationSports venue (base)availableService
GymOrHealthClubGym / fitness club
GovernmentOrganizationGovernment organisation
EducationalOrganizationEducational institution (base)
CollegeOrUniversityUniversity / college
SchoolSchool
CorporationLarge corporationtickerSymbol, legalName

Base Organization

The base Organization schema is added to the homepage of any site. It helps Google identify the brand and link it to external profiles via sameAs. The @id field with a unique identifier lets other schemas reference this organisation without duplicating data.

JSON
{
  "@context": "https://schema.org",
  "@type": "Organization",
  "@id": "https://example.com/#organization",
  "name": "Company Name",
  "legalName": "Example LLC",
  "url": "https://example.com",
  "logo": {
    "@type": "ImageObject",
    "url": "https://example.com/logo.png",
    "width": 200,
    "height": 60
  },
  "description": "Brief company description for Knowledge Graph.",
  "foundingDate": "2015",
  "contactPoint": {
    "@type": "ContactPoint",
    "telephone": "+1-800-123-4567",
    "contactType": "customer service",
    "availableLanguage": "English"
  },
  "sameAs": [
    "https://www.linkedin.com/company/yourcompany",
    "https://twitter.com/yourcompany",
    "https://www.facebook.com/yourcompany"
  ]
}

LocalBusiness — physical business with an address

LocalBusiness is the most important subtype for businesses with a physical location. A correctly filled schema improves chances of appearing in the Google Map Pack (the map block at the top of the SERP) and a Knowledge Panel with business hours. Key fields: address, geo, openingHoursSpecification, telephone, priceRange.

JSON
{
  "@context": "https://schema.org",
  "@type": "Restaurant",
  "@id": "https://example.com/#business",
  "name": "The Birch Restaurant",
  "url": "https://example.com",
  "telephone": "+1-212-555-0100",
  "email": "info@example.com",
  "servesCuisine": ["American", "European"],
  "priceRange": "$$",
  "address": {
    "@type": "PostalAddress",
    "streetAddress": "123 Main Street",
    "addressLocality": "New York",
    "addressRegion": "NY",
    "postalCode": "10001",
    "addressCountry": "US"
  },
  "geo": {
    "@type": "GeoCoordinates",
    "latitude": 40.7128,
    "longitude": -74.0060
  },
  "openingHoursSpecification": [
    {
      "@type": "OpeningHoursSpecification",
      "dayOfWeek": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
      "opens": "12:00",
      "closes": "23:00"
    },
    {
      "@type": "OpeningHoursSpecification",
      "dayOfWeek": ["Saturday", "Sunday"],
      "opens": "12:00",
      "closes": "00:00"
    }
  ],
  "image": "https://example.com/photos/restaurant.jpg",
  "hasMap": "https://maps.google.com/?cid=XXXXXX"
}
Days of the week in openingHoursSpecification must be spelled out in English with a capital letter: Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday. Times use 24-hour format HH:MM. For round-the-clock hours: opens "00:00", closes "23:59".

sameAs — linking to external profiles

The sameAs field tells Google that your organisation is identical to profiles on external platforms. This is the key field for building the Knowledge Graph — Google's entity database. The more authoritative sources confirm an organisation's existence, the higher the probability of appearing in a Knowledge Panel.

  • Wikidata (most authoritative): https://www.wikidata.org/wiki/Q...
  • Wikipedia: https://en.wikipedia.org/wiki/...
  • LinkedIn, Twitter/X, Facebook, Instagram
  • The organisation's YouTube channel
  • Google Business Profile (for local businesses)
  • Industry directories — medical registries, bar associations, etc.

Product — product schema and all subtypes

Product is the base schema for any product or item. It works together with Offer (price and availability) and AggregateRating (rating). A correctly implemented Product schema gives a rich result in Google: price, availability status and star ratings right in the snippet.

TypeUse caseNotes
ProductBase schema for any productname, image, description required
OfferSpecific listing: price + availabilityprice, priceCurrency, availability required
AggregateOfferPrice range (multiple offers)lowPrice, highPrice, offerCount
ProductGroupGroup of products with variantsvariesBy, hasVariant
IndividualProductSpecific variant within a ProductGroupisVariantOf → ProductGroup
BrandProduct brand (nested in Product)name required

Base Product with Offer

Google requires a minimum set of fields for a rich result: name, image, description, and a nested Offer with priceCurrency, price and availability. Without at least one of these, the product snippet will not be enriched.

JSON
{
  "@context": "https://schema.org",
  "@type": "Product",
  "name": "Air X Pro Running Shoes",
  "description": "Lightweight running shoes with foam sole for road running. Weight 230g, drop 10mm.",
  "sku": "AIRX-001-BLK",
  "mpn": "AIRX001",
  "gtin13": "0123456789012",
  "brand": {
    "@type": "Brand",
    "name": "RunMax"
  },
  "image": [
    "https://example.com/photos/airx-front.jpg",
    "https://example.com/photos/airx-side.jpg",
    "https://example.com/photos/airx-back.jpg"
  ],
  "offers": {
    "@type": "Offer",
    "url": "https://example.com/products/air-x-pro",
    "priceCurrency": "USD",
    "price": "89.99",
    "priceValidUntil": "2026-12-31",
    "itemCondition": "https://schema.org/NewCondition",
    "availability": "https://schema.org/InStock",
    "seller": {
      "@type": "Organization",
      "name": "RunMax Store"
    }
  },
  "aggregateRating": {
    "@type": "AggregateRating",
    "ratingValue": "4.7",
    "reviewCount": "128"
  }
}
availability values: InStock, OutOfStock, PreOrder, Discontinued. Always use the full prefix https://schema.org/ — abbreviated forms (just "InStock") are not guaranteed to be recognised correctly.

AggregateOffer — price range

If a product is offered by multiple sellers or comes in variants with different prices, use AggregateOffer. It shows a price range (lowPricehighPrice) instead of a single price.

JSON
{
  "@context": "https://schema.org",
  "@type": "Product",
  "name": "Galaxy S25 Smartphone",
  "brand": { "@type": "Brand", "name": "Samsung" },
  "image": "https://example.com/galaxy-s25.jpg",
  "offers": {
    "@type": "AggregateOffer",
    "priceCurrency": "USD",
    "lowPrice": "799",
    "highPrice": "1099",
    "offerCount": "3",
    "offers": [
      {
        "@type": "Offer",
        "name": "128 GB / Black",
        "price": "799",
        "priceCurrency": "USD",
        "availability": "https://schema.org/InStock"
      },
      {
        "@type": "Offer",
        "name": "256 GB / White",
        "price": "899",
        "priceCurrency": "USD",
        "availability": "https://schema.org/InStock"
      },
      {
        "@type": "Offer",
        "name": "512 GB / Violet",
        "price": "1099",
        "priceCurrency": "USD",
        "availability": "https://schema.org/PreOrder"
      }
    ]
  }
}

ProductGroup — products with variants

ProductGroup is a group of related products that differ by parameters (size, colour, material). Used on product pages with variants. Each specific variant is a nested Product with isVariantOf pointing to the parent ProductGroup.

JSON
{
  "@context": "https://schema.org",
  "@type": "ProductGroup",
  "name": "Air X Pro Running Shoes",
  "description": "Road running shoes available in multiple colours and sizes.",
  "url": "https://example.com/products/air-x-pro",
  "brand": { "@type": "Brand", "name": "RunMax" },
  "productGroupID": "AIRX-PRO",
  "variesBy": ["https://schema.org/color", "https://schema.org/size"],
  "hasVariant": [
    {
      "@type": "Product",
      "name": "Air X Pro — Black, US 9",
      "sku": "AIRX-BLK-9",
      "color": "Black",
      "size": "US 9",
      "offers": {
        "@type": "Offer",
        "price": "89.99",
        "priceCurrency": "USD",
        "availability": "https://schema.org/InStock"
      }
    },
    {
      "@type": "Product",
      "name": "Air X Pro — Blue, US 11",
      "sku": "AIRX-BLU-11",
      "color": "Blue",
      "size": "US 11",
      "offers": {
        "@type": "Offer",
        "price": "89.99",
        "priceCurrency": "USD",
        "availability": "https://schema.org/OutOfStock"
      }
    }
  ]
}
For a Product rich result Google requires: name, image and a nested Offer. Without Offer (price and availability) the enriched snippet will not be displayed. Additionally: brand, sku or gtin13 improve chances of appearing in Google Shopping.

AggregateRating and Review — ratings and reviews

Star ratings in search results are the most visually prominent snippet element. They are implemented via two related types: AggregateRating (the combined score from all ratings) and Review (an individual text review). Both types always nest inside a parent schema: Product, LocalBusiness, Recipe, Course, Book, etc.

AggregateRating — combined score

AggregateRating reflects the aggregate score: average value and number of votes. Google renders it as stars in the snippet. Required fields: ratingValue (average) and reviewCount or ratingCount.

JSON
{
  "@context": "https://schema.org",
  "@type": "Product",
  "name": "Air X Pro Running Shoes",
  "image": "https://example.com/airx.jpg",
  "offers": {
    "@type": "Offer",
    "price": "89.99",
    "priceCurrency": "USD",
    "availability": "https://schema.org/InStock"
  },
  "aggregateRating": {
    "@type": "AggregateRating",
    "ratingValue": "4.7",
    "bestRating": "5",
    "worstRating": "1",
    "reviewCount": "128",
    "ratingCount": "215"
  }
}
ratingCount — total number of ratings (including those with no text). reviewCount — number of text reviews. If you only have star ratings with no text — use ratingCount. If you only have text reviews — use reviewCount. Both fields can be provided simultaneously.

Review — individual reviews

Review is the schema for an individual text review. It nests inside the parent object via the review property. Each Review contains an author, rating and body text. Google may display a snippet of the review text in the rich result.

JSON
{
  "@context": "https://schema.org",
  "@type": "Product",
  "name": "Air X Pro Running Shoes",
  "image": "https://example.com/airx.jpg",
  "offers": {
    "@type": "Offer",
    "price": "89.99",
    "priceCurrency": "USD",
    "availability": "https://schema.org/InStock"
  },
  "aggregateRating": {
    "@type": "AggregateRating",
    "ratingValue": "4.7",
    "reviewCount": "128"
  },
  "review": [
    {
      "@type": "Review",
      "reviewRating": {
        "@type": "Rating",
        "ratingValue": "5",
        "bestRating": "5"
      },
      "name": "Great shoes for daily runs",
      "reviewBody": "Been running in them for 3 months — sole holds up well, feet don't tire. Recommend for road running.",
      "datePublished": "2026-03-15",
      "author": {
        "@type": "Person",
        "name": "Alex K."
      }
    },
    {
      "@type": "Review",
      "reviewRating": {
        "@type": "Rating",
        "ratingValue": "4",
        "bestRating": "5"
      },
      "reviewBody": "Good cushioning, but runs a little small. Order half a size up.",
      "datePublished": "2026-04-01",
      "author": {
        "@type": "Person",
        "name": "Maria S."
      }
    }
  ]
}
Critical Google requirement: reviews in markup must be genuine reviews from real users on your site. Prohibited: showing only 5-star reviews, hiding negative ones, publishing reviews written by the editorial team. Violations result in a manual action and loss of rich results.

ImageObject — photos in structured data

Images in Schema.org can be specified in two ways: as a URL string ("image": "https://example.com/photo.jpg") or as an ImageObject with metadata. A string is sufficient for most schemas, but for Article, Recipe and HowTo Google recommends ImageObject — it lets you specify dimensions, caption and format.

Schema typeImage requirementsMin. width
ProductAt least one product image; white background preferred800 px recommended
Article / NewsArticleAt least 3 images: 16:9, 4:3, 1:11200 px
RecipeImage of finished dish is requiredAny; 1200×628 optimal
LocalBusinessPhotos of entrance, interior, facadeAny
Organization (logo)Rectangular, white/transparent background112×112 min, up to 1×10 ratio
Example Schema.org image markup via JSON-LD:
JSON
{
  "@context": "https://schema.org",
  "@type": "Product",
  "name": "Air X Pro Running Shoes",
  "image": [
    {
      "@type": "ImageObject",
      "url": "https://example.com/airx-front.jpg",
      "width": 1200,
      "height": 1200,
      "caption": "Air X Pro — front view"
    },
    {
      "@type": "ImageObject",
      "url": "https://example.com/airx-side.jpg",
      "width": 1200,
      "height": 1200,
      "caption": "Air X Pro — side view"
    }
  ],
  "offers": {
    "@type": "Offer",
    "price": "89.99",
    "priceCurrency": "USD",
    "availability": "https://schema.org/InStock"
  }
}
For Article, Google requires at least one image 1200 px wide with a 16:9, 4:3 or 1:1 aspect ratio. Provide all three variants — Google selects the right one for each placement. Images must be crawlable (not blocked in robots.txt) and have a stable URL.

Combining schemas on one page

Multiple schemas on a single page are added as separate <script> blocks. Here is a complete product page markup: the selling organisation, breadcrumbs, product with price, rating and review.

HTML
<!-- Schema 1: Selling organisation (on all site pages) -->
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Organization",
  "@id": "https://example.com/#organization",
  "name": "RunMax Store",
  "url": "https://example.com",
  "logo": "https://example.com/logo.png"
}
</script>

<!-- Schema 2: Breadcrumbs -->
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "BreadcrumbList",
  "itemListElement": [
    { "@type": "ListItem", "position": 1, "name": "Home", "item": "https://example.com" },
    { "@type": "ListItem", "position": 2, "name": "Footwear", "item": "https://example.com/shoes" },
    { "@type": "ListItem", "position": 3, "name": "Air X Pro" }
  ]
}
</script>

<!-- Schema 3: Product with price, rating and review (on product page) -->
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Product",
  "name": "Air X Pro Running Shoes",
  "description": "Road running shoes.",
  "sku": "AIRX-001",
  "brand": { "@type": "Brand", "name": "RunMax" },
  "image": "https://example.com/airx.jpg",
  "offers": {
    "@type": "Offer",
    "price": "89.99",
    "priceCurrency": "USD",
    "availability": "https://schema.org/InStock",
    "priceValidUntil": "2026-12-31",
    "seller": { "@id": "https://example.com/#organization" }
  },
  "aggregateRating": {
    "@type": "AggregateRating",
    "ratingValue": "4.7",
    "reviewCount": "128",
    "bestRating": "5"
  },
  "review": {
    "@type": "Review",
    "reviewRating": { "@type": "Rating", "ratingValue": "5" },
    "author": { "@type": "Person", "name": "Alex K." },
    "reviewBody": "Excellent shoes, run in them every day.",
    "datePublished": "2026-04-10"
  }
}
</script>
Note seller: { "@id": "https://example.com/#organization" } — a reference to the previously defined organisation by its @id. This is called graph linking: Google connects entities into a unified knowledge graph without duplicating data.

Validating structured data

After implementation, always validate the markup. Google provides two official tools, and Search Console gives the real picture of what the crawler sees on indexed pages.

Step 1Rich Results Test

Open search.google.com/test/rich-results, enter a URL or HTML. The tool shows which rich results are available for the schema, which fields are required, and what is filled in incorrectly. This is the only place to confirm whether the markup will actually produce visual enhancements in the SERP.

Step 2Schema Markup Validator

Open validator.schema.org for full validation against the Schema.org standard (not just types Google supports). It checks required properties for each type and warns about incompatible combinations — useful for complex nested schemas.

Step 3Google Search Console

Search Console → Enhancements → select the schema type. This section shows real errors on indexed pages. Errors here are what is actually blocking rich results right now, on live URLs — the most actionable signal.

Quick check: make sure JSON-LD is present in the server HTML response, not only added via JavaScript. Open DevTools → Network → find the first request → Preview → search for script type="application/ld+json". If the block is missing, Googlebot may not see the markup.

Structured data checklist

JSON-LD base implementation

  • <script type="application/ld+json"> tag added in <head> or end of <body>
  • Every block contains @context: "https://schema.org" and @type
  • All main entities have a unique @id in URL-with-fragment format (#name)
  • JSON is valid — no trailing commas, all quotes closed
  • Markup is present in the server HTML response (not only via JavaScript)

Organization / LocalBusiness

  • Correct subtype chosen (Restaurant, Dentist, AutoDealer, etc.) instead of plain Organization
  • Filled: name, url, telephone, address (for LocalBusiness)
  • geo added with precise coordinates (latitude, longitude)
  • openingHoursSpecification filled with English day names and HH:MM times
  • sameAs links to at least social profiles + Wikidata (if available)

Product

  • Filled: name, description, image (at least one image)
  • Nested Offer with: price, priceCurrency, availability, priceValidUntil
  • availability uses full URL: https://schema.org/InStock
  • sku and/or gtin13 provided for Google Shopping
  • AggregateRating nested if the page has user ratings

Ratings and reviews

  • AggregateRating contains ratingValue, reviewCount, bestRating, worstRating
  • AggregateRating values match real data from the site
  • Review is nested inside the parent object (Product / LocalBusiness), not standalone
  • Each Review has author (Person), reviewRating, datePublished, reviewBody
  • Reviews in markup are real reviews from real users

FAQ

No. Google officially states that structured data is not a ranking signal. Markup helps Google understand content but does not mechanically boost positions. The indirect effect is real: rich results raise CTR, and higher CTR is a quality signal. So correct markup works — just not as a direct ranking factor.
Technically yes — GTM can inject a <script> tag with JSON-LD. But Google prefers markup in the server HTML response, not added via JavaScript. GTM-injected markup may not be recognised correctly during crawling if Googlebot does not execute JS for that page. The optimal approach is to generate JSON-LD server-side.
Google does not require a fixed number. In practice: if fewer than 10 reviews, include all. If hundreds — 3–5 recent ones are sufficient. Quality matters more: each Review needs body text, an author and a date. Too few reviews may prevent Google from showing stars in the snippet.
Several reasons: (1) Google has not re-crawled the page yet — wait 1–4 weeks or request re-indexing; (2) reviewCount is too low; (3) The page violates Google's policies (e.g. reviews look manipulated); (4) Rich results for this schema type compete with other SERP features. Check Search Console → Enhancements.
Use a separate Offer per currency, or multiple Offers inside AggregateOffer. The priceCurrency field uses ISO 4217 codes: USD, EUR, GBP, JPY. Google displays the price of the country/language for which the page is intended. Make sure hreflang is configured correctly so each locale sees markup in its own currency.