MMurmTools

Home › Product schema

Every page makes two claims. Only one is read by machines.

There is the page a shopper sees, and there is the structured data beneath it — a second, machine-readable copy of the same claims. Search engines read the second one. The two drift apart constantly, and nothing on the visible page tells you when it happens.

2 September 2026 · from the reader inside MurmSchema

The visible price and the declared price are two different claims. A shopper reads the first. Google reads the second. When they disagree — and they disagree often — the one you never look at is the one that ranks you.

The minimum that actually earns rich results

Less than most guides imply. A Product node needs:

  • name — the product, as a string.
  • image — a resolvable URL, not a data URI or a lazy-load placeholder.
  • An Offer with price, priceCurrency and availability.

aggregateRating adds stars, and should exist only where the reviews are real. The rest is optional and mostly ignored.

The failures that matter are not missing fields

A missing field is easy: a validator flags it and someone fixes it. The expensive failures are the ones that validate cleanly:

  • Two Product nodes on one page. A theme declares one, an app injects another, and they disagree. A search engine picks one and does not tell you which.
  • A price the page no longer shows. The visible price came from a live variant selector; the JSON-LD was rendered server-side and is a week stale. Both are valid JSON. Only one is true.
  • availability that never changes. Hardcoded InStock is extremely common and quietly wrong every time something sells out.
  • Currency without locale. A multi-currency store declaring one currency in the markup and showing another in the interface.
  • Ratings with no reviews behind them. The one that is a policy problem rather than a bug.

Why nobody notices

Because the machine layer is invisible while browsing. You can look at a product page every day for a month and never see the block that decides how it appears in search. Rich results disappear, and the page looks fine, and the change that caused it was three deploys ago.

This is also why it is worth reading a competitor’s structured data. It is the most literal statement a store makes about itself — prices, availability, brand, category — in a format designed to be parsed. It does not know it is being read, and it does not perform.

What structured data is good evidence for

  • Real prices, including variants. An AggregateOffer declares a low and a high, which is the price architecture stated outright.
  • Genuine category. The BreadcrumbList is the merchant’s own taxonomy, not a guess from the URL.
  • Review counts. Declared rather than scraped from a widget that might be rendering someone else’s numbers.

Reading the machine layer rather than the rendered text is a general principle worth adopting: it is the same in every language, it is stable across redesigns, and a page that shouts “€4,99 today only!” in a banner while declaring €9.99 in its markup has told you something interesting about itself.

MurmSchema reports every type a page declares, how many blocks it found, and where they conflict — including two nodes of the same type disagreeing with each other, which most validators pass without comment.

How MurmSchema works

Questions

What does a product page need for rich results?

A Product node with name, image, and an Offer carrying price, currency and availability.

Why did my rich results disappear?

Usually a second Product node from a theme or app, conflicting with the first.

Must the markup match the visible page?

Yes, and a disagreeing price is a policy problem as well as a technical one.

How do I check?

Read the JSON-LD, or use something that parses it and reports conflicts.

See also: MurmSchema · MurmSpy · How accurate are sales estimates? · Writing