Ship It Right: Building a Valid Product Schema Block From Scratch
Objective: Given one real product's facts, build a complete, valid JSON-LD Product schema block from zero, matched exactly to what the live page visibly shows, and get it to pass Google's Rich Results Test with zero errors.
FirstCry is launching Rich Results on its baby-gear PDPs for the first time. You're handed one real product, a baby stroller priced at ₹8,999, in stock, with a 4.4-star average from 87 verified reviews already visible on the page, and asked to build and sign off the first JSON-LD block from scratch.
Four passes: pick the right schema type and top-level fields, fill the offers object correctly, cross-check every value against what the page actually shows, then validate the finished block.
What does a complete, valid Product JSON-LD block look like when built from a real page's exact visible facts, and how do you confirm it passes Google's own validator?
Before you start
What you'll need
- —Basic familiarity with JSON syntax
- —Access to a text editor and the target product page
- Offer object
- the nested schema.org block declaring price, priceCurrency, and availability, all three required for Google to render a price badge.
- Availability
- a schema.org URL value (e.g. https://schema.org/InStock) telling Google the current stock state of the product.
Free path (everything below is enough to finish)
Free, and the same validation engine Google's indexing pipeline itself uses to determine rich-result eligibility.
Paid upgrades (optional, faster/deeper)
Building and validating one Product schema block needs zero paid tools; a text editor and GSC's free validator are the whole free path.
The free path (manual JSON-LD build plus GSC validation) is complete for building and shipping this one block; SEMrush is worth it once auditing schema across thousands of live product pages.
The process
4 steps
Step 01 of 04
The lesson's table lists Product schema as unlocking prices, ratings, and availability, and names it the best fit for ecommerce and SaaS pages.
Given a baby-stroller PDP with a real name, brand, price, stock status, and an on-page rating, which @type and top-level properties does the lesson's table point you toward?
Procedure
- Confirm the page is a single, purchasable product, not a category or article, and set @type to Product
- Add the baseline identity fields: name, image, description, brand
- Stub out placeholders for offers and aggregateRating, to be filled in the next steps
{
"@context": "https://schema.org",
"@type": "Product",
"name": "FirstCry Comfort Ride 3-Wheel Baby Stroller",
"image": "https://www.firstcry.com/img/comfort-ride-stroller.jpg",
"description": "Lightweight 3-wheel stroller with reclining seat and all-terrain wheels.",
"brand": {
"@type": "Brand",
"name": "FirstCry Original"
},
"offers": { },
"aggregateRating": { }
}Healthy
@type is Product, and every baseline identity field (name, image, description, brand) is filled with the real page values, not placeholder text.
Unhealthy
Leaving @type as a generic "Thing" or copying an @type from a different product family, which prevents any of the intended rich-result types from being considered at all.
What this means
Getting @type right is the single highest-leverage decision in the whole block. Every property added after this point is wasted if the type doesn't match what the lesson's table says triggers Product rich results.
So what do I do about it?
| Symptom | Action | Effort |
|---|---|---|
| The draft schema was copy-pasted from a different product template | Rebuild the identity fields from this exact product's real page content before adding offers or ratings | 30 min |
Step 02 of 04
The lesson's minimal JSON-LD example shows the offers object needs price and priceCurrency at minimum, with availability as the field that tells Google whether the item can currently be purchased.
The stroller is in stock at ₹8,999. What exact values go in offers.price, offers.priceCurrency, and offers.availability?
Procedure
- Set price to the exact numeric value shown on the page, 8999, with no currency symbol inside the field
- Set priceCurrency to the ISO code, INR
- Set availability to the schema.org URL for the current stock state, https://schema.org/InStock
"offers": {
"@type": "Offer",
"price": "8999",
"priceCurrency": "INR",
"availability": "https://schema.org/InStock"
}Healthy
price, priceCurrency, and availability are all present and match the live page exactly, using the full schema.org URL form for availability.
Unhealthy
Leaving priceCurrency out because "the price is obviously in rupees," which Google's parser does not infer on its own.
What this means
All three offer fields are required for Google to render a price badge at all, not just recommended; a partial offers block is treated the same as a missing one for rich-result eligibility.
So what do I do about it?
| Symptom | Action | Effort |
|---|---|---|
| The offers block is missing priceCurrency | Add the ISO currency code explicitly before submitting for validation | 5 min |
Step 03 of 04
The lesson warns against marking up content that isn't visible on the page and against mismatching schema type to content; both are guideline violations that can trigger a manual removal of rich-result eligibility.
The page shows 87 reviews and a 4.4 average directly on screen. Does the draft's aggregateRating block honestly match what's visible, and is anything else on the page still unrepresented in the schema?
Procedure
- Open the live product page next to the JSON-LD draft
- Confirm ratingValue and reviewCount in the draft match exactly what's rendered on the page (4.4 and 87)
- Scan the page for any other visible fact (SKU, GTIN if shown) not yet represented in the schema
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "4.4",
"reviewCount": "87"
}
Cross-check against live page: ratingValue and reviewCount match exactly. No SKU
or GTIN is currently displayed on the page, so neither is added to the schema yet.Healthy
Every value in aggregateRating matches the visible page exactly, and no field is added for information the page doesn't actually show.
Unhealthy
Inflating reviewCount to a rounder number, or adding a GTIN value that isn't displayed anywhere a shopper can see it, both guideline violations even if technically valid JSON.
What this means
The draft passing this cross-check is what separates "technically valid schema" from "schema Google will actually trust," since the guideline violation the lesson warns about is invisible to a syntax validator.
So what do I do about it?
| Symptom | Action | Effort |
|---|---|---|
| No one currently cross-checks schema values against the rendered page before publishing | Add a page-vs-schema side-by-side check as a required step before any PDP schema ships | 5 min |
Step 04 of 04
The lesson instructs validating every schema block with Google's Rich Results Test before launch, since it shows exactly what rich-result types Google can detect and flags any errors.
Paste the finished block into Google's Rich Results Test (accessible via GSC's URL Inspection on a live URL). What does a clean pass look like, and what would still block launch?
Procedure
- Paste the finished JSON-LD block or the live URL into the Rich Results Test
- Confirm the detected type reads "Product" with zero errors
- Review any warnings (non-blocking) separately from errors (blocking) and resolve errors before considering the block launch-ready
Rich Results Test: PASS Detected type: Product Eligible for: Merchant listing, Product snippet Errors: 0 Warnings: 0
Healthy
Zero errors, the detected type matches the intended one, and the eligible rich-result types include the ones the launch actually needs.
Unhealthy
Shipping a block with an unresolved warning about a recommended-but-missing field like gtin, which won't block eligibility today but limits which AI shopping tools can confidently cite the page.
What this means
A clean validator pass is the prerequisite the lesson describes, not a guarantee: Google still decides whether to actually show the rich result, but a failing validation guarantees it never will.
So what do I do about it?
| Symptom | Action | Effort |
|---|---|---|
| The block hasn't been run through validation before the launch date | Block the CMS deploy on a passing Rich Results Test result, not just a code review | 5 min |
Analyze your findings
What to look for
- Type correctness
- Is @type Product, matching what the lesson's table says triggers price, rating, and availability rich results?
- Field completeness
- Are all three offer fields, price, priceCurrency, and availability, present, not just price alone?
- Page match
- Does every declared value match exactly what's visibly rendered on the page, no rounding, no invented fields?
- Clean validation
- Does the finished block pass Google's Rich Results Test with zero errors, not just zero syntax errors?
Make the call
The draft's offers object has price and availability filled in but priceCurrency is missing. What's the consequence of shipping it as-is?
Recommendation · Priority: Medium
“The FirstCry stroller PDP's Product schema block is ready to ship: @type is correctly set to Product, all four identity fields are filled from the real page, the offers object includes price (8999), priceCurrency (INR), and availability (InStock), and the aggregateRating values (4.4, 87 reviews) match exactly what's visibly rendered. The block passed Google's Rich Results Test with zero errors and zero warnings, making it a safe template to replicate across the rest of the baby-gear catalog.”
Common mistakes
What trips people up
Leaving priceCurrency out because the price seems obviously local — Google's parser doesn't infer currency, an incomplete offers object can be rejected entirely.
Copying identity fields from a different product's template — name, image, description, and brand all need to be rebuilt from this exact product's real page content, not reused from a similar listing.
Skipping the page-vs-schema cross-check — technically valid JSON can still violate content guidelines if a value like reviewCount doesn't match what's actually displayed.
Treating a clean validator pass as a guarantee of the rich result appearing — passing validation is a prerequisite, not a promise, Google still independently decides whether to show the rich result.
Final deliverable
A complete, validated JSON-LD Product schema block for one real PDP, passing Google's Rich Results Test with zero errors, matched exactly to what the page visibly shows.
See a reference example
The same 4-step build applied to a ThredUp resale-item PDP (illustrative): @type Product, offers.price "34.00", offers.priceCurrency "USD", offers.availability set to https://schema.org/InStock for a single available unit. The cross-check step caught that the draft's condition field said "New" while the visible listing clearly stated "Gently Used," a values mismatch fixed before submission.
Success criteria
You're done when you can:
- @type is set to Product with all four baseline identity fields (name, image, description, brand) filled from the real page
- offers includes price, priceCurrency, and a correctly formatted availability URL matching the live stock state
- aggregateRating values match the visibly displayed rating and review count exactly, with no invented fields
- The finished block passes Google's Rich Results Test with zero errors
Key takeaway
Building schema from scratch is less about JSON syntax and more about discipline: choosing the right type, filling every required field completely, and verifying each value against what the page actually shows before ever running the validator. A clean Rich Results Test pass is the finish line, not the starting point.