Use Case
Product rich results — with pricing, availability, and star ratings — require complete Schema.org markup. A single missing property costs you the feature snippet. SchemaCheck validates every required field across your entire catalog.
Google's Product rich results (pricing, availability, ratings in search) require specific fields. SchemaCheck validates all of them with per-property error messages and fix suggestions.
| Property | Required? | What SchemaCheck checks |
|---|---|---|
| name | Required | Non-empty string |
| image | Required | Valid URL or ImageObject |
| offers | Required for pricing | Has price, priceCurrency, availability |
| offers.price | Required | Numeric string or number |
| offers.priceCurrency | Required | ISO 4217 currency code (USD, EUR, GBP…) |
| offers.availability | Required | Schema.org InStock / OutOfStock URL |
| aggregateRating | Recommended | Has ratingValue and reviewCount/ratingCount |
| brand | Recommended | Organization or Brand with name |
| description | Recommended | Non-empty string |
| sku | Recommended | String identifier |
Here's a typical incomplete Product schema that fails rich results, and what the corrected version looks like.
// Before: incomplete Product schema (missing required properties for Google Shopping)
{
"@context": "https://schema.org",
"@type": "Product",
"name": "Wireless Noise-Cancelling Headphones",
"description": "Premium audio with 30-hour battery life."
}
// SchemaCheck response:
// errors: ["name ✓", "offers ✗ — required for rich results", "image ✗ — required for rich results"]
// rich_result_eligible: false
// score: 20/100
// After: complete Product schema
{
"@context": "https://schema.org",
"@type": "Product",
"name": "Wireless Noise-Cancelling Headphones",
"description": "Premium audio with 30-hour battery life.",
"image": "https://example.com/headphones.jpg",
"brand": { "@type": "Brand", "name": "SoundCo" },
"offers": {
"@type": "Offer",
"price": "299.99",
"priceCurrency": "USD",
"availability": "https://schema.org/InStock",
"url": "https://example.com/products/headphones"
},
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "4.7",
"reviewCount": "1284"
}
}
// SchemaCheck response:
// errors: []
// rich_result_eligible: true
// score: 100/100Pull every product URL from the Shopify Admin API and validate each one. Run weekly as a cron job and alert on new errors.
// Validate every product in your Shopify store via the Admin API
import Shopify from "@shopify/shopify-api";
const shop = "your-store.myshopify.com";
const accessToken = process.env.SHOPIFY_ACCESS_TOKEN;
const schemaKey = process.env.SCHEMACHECK_API_KEY;
// Fetch all product URLs from Shopify
async function getAllProductUrls(): Promise<string[]> {
const response = await fetch(
`https://${shop}/admin/api/2024-01/products.json?fields=handle&limit=250`,
{ headers: { "X-Shopify-Access-Token": accessToken! } }
);
const { products } = await response.json();
return products.map((p: { handle: string }) => `https://${shop}/products/${p.handle}`);
}
// Validate a single product URL
async function validateProduct(url: string) {
const res = await fetch(
`https://schemacheck.dev/api/v1/validate?url=${encodeURIComponent(url)}&access_key=${schemaKey}`
);
return res.json();
}
// Run audit
const urls = await getAllProductUrls();
console.log(`Validating ${urls.length} products...`);
const issues: Array<{ url: string; errors: number; missingProps: string[] }> = [];
for (const url of urls) {
const result = await validateProduct(url);
if (!result.success) continue;
const productSchema = result.schemas.find((s: { type: string }) => s.type === "Product");
if (productSchema && productSchema.errors.length > 0) {
issues.push({
url,
errors: productSchema.errors.length,
missingProps: productSchema.properties_missing_required,
});
}
}
console.log(`\n${issues.length} products with schema errors:`);
issues.forEach((i) => console.log(` ${i.url} — missing: ${i.missingProps.join(", ")}`));Use a publish webhook to validate product schema the moment it goes live. Send a Slack alert if required properties are missing.
// WordPress / WooCommerce publish webhook
// Validate schema every time a product is published or updated
addEventListener("fetch", (event) => {
event.respondWith(handlePublishHook(event.request));
});
async function handlePublishHook(request: Request): Promise<Response> {
const { post_id, post_type, permalink } = await request.json();
// Only validate product and post types that have schema
if (!["product", "post", "page"].includes(post_type)) {
return new Response("skipped", { status: 200 });
}
// Give WordPress a moment to publish before fetching
await new Promise((r) => setTimeout(r, 3000));
const result = await fetch(
`https://schemacheck.dev/api/v1/validate?url=${encodeURIComponent(permalink)}&access_key=${SCHEMACHECK_API_KEY}`
).then((r) => r.json());
if (!result.success) return new Response("validation skipped", { status: 200 });
const errors = result.schemas.flatMap((s) => s.errors);
if (errors.length > 0) {
// Send Slack alert
await fetch(SLACK_WEBHOOK_URL, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
text: `⚠️ Schema errors on newly published ${post_type}: ${permalink}`,
attachments: errors.map((e) => ({
color: "danger",
text: `${e.property}: ${e.message}\nFix: ${e.fix}`,
})),
}),
});
}
return new Response(JSON.stringify({ errors: errors.length, score: result.summary.score }));
}“Our product team kept removing the offers block from product descriptions to 'clean up' the page. SchemaCheck in CI blocked those PRs automatically — we recovered 18% of our Google Shopping impressions within two weeks.”
Free plan — 100 validations/month. Enough to validate your top products daily.