JSON-LD emitted by a serializer that reads the same props your template renders, so price and availability cannot drift out of sync with the page.
About this service
The problem we are usually hired to fix is drift. The markup says 21,499 and the page renders 18,999, because the JSON-LD lives in a CMS field somebody last touched in March while the price comes from an inventory service at request time. Merchant Center reads that as a price mismatch, suppresses the offer, and the rich result quietly disappears while every validator on the market still reports the markup as correct. The fix is not better markup. It is removing the second source of truth.
How we build it:
We write structured data as a serializer that receives the same props the template already renders: price, availability, currency, SKU, delivery estimate. If a component changes the displayed price, the emitted price changes in the same commit, or a snapshot test fails in CI and the build does not ship. On a catalog of any size this is the only version of the work that survives a redesign.
Entity graph:
We consolidate Organization, Brand, WebSite and the per-page node into one graph with stable @id references, instead of four disconnected blocks contradicting each other. Breadcrumbs come from the router's own path segments rather than a hand-maintained array that goes stale the first time a category moves. Category and search templates get ItemList with position and url, pointers rather than inlined Product objects, which bloat the response and repeat entity definitions Google resolves anyway.
Retail specifics:
OfferShippingDetails and MerchantReturnPolicy are the two that pay for themselves on home and furniture catalogs, where the delivery window and the return window are the entire objection. We map the policy you actually operate, including pin-code dependent delivery estimates that Indian retailers really run, rather than a flat national promise the markup cannot honour. For app publishers we mark the web property rather than the store listing: SoftwareApplication with operatingSystem, applicationCategory and the offer, tied to the same Organization node as everything else.
What we will not mark up:
AggregateRating you did not collect. If the reviews sit in a third-party widget and you cannot export the underlying data, that markup is a manual action waiting to be filed and we will put our refusal in writing rather than ship it. FAQPage on commercial pages, which Google restricted to health and government sources in August 2023 and which now costs you response weight for nothing. HowTo, retired. Speculative Event or Course markup on pages that are not those things. Where a previous agency has already shipped these, removing them is part of the first week.
Validation:
We sample across templates rather than testing the one URL that looks good, usually forty to sixty URLs spanning every template and every edge case: out of stock, price on request, single variant, bundle, pre-order. Rich Results Test and the Schema.org validator for syntax. Search Console enhancement reports read over a full 28-day cycle for what Google actually accepted, which is the only opinion that counts. Merchant Center diagnostics wherever a feed exists, because feed and markup disagreeing is its own class of failure and nobody owns it.
Who this is not for:
Anyone expecting rankings from this. Structured data changes eligibility and presentation, not position, and we will not sell it as anything else. Also not for teams who cannot take a pull request. If markup has to be pasted into a tag manager by someone outside engineering, it will drift again within two quarters and you should hire someone cheaper to paste it.
Handover:
A merged branch, the snapshot tests that keep it honest, one page per template on what is emitted and why, and the list of things we deliberately left unmarked.