Merchant Center price mismatch on Shopify: disapproved, or silently corrected

Short answer. Merchant Center does not only read your feed. Googlebot crawls the product page and compares the price it finds there with the price you submitted. If the two disagree you get one of two outcomes: the product is disapproved for a mismatched price, or, because automations are on by default, Google keeps the listing and shows its own crawled price instead. The second outcome produces no error to notice.

Want to know if your store has this right now? Check my store, free 2 min · read-only

You open Merchant Center expecting a quiet dashboard and instead find a wall of red: dozens of products disapproved, reason given as a mismatched or inconsistent price. Nothing changed on your end, no new promo, no manual edit. And yet Google insists the price on your page doesn't match the price you sent.

The price in Shopify admin usually is correct. What Google read off the page was something else, and on a Shopify store there are several ways for those two numbers to drift apart without anyone touching a price.

Why this happens: Google reads your page, not just your feed

Google's help page for this issue, How to fix: Mismatched product price, states the mechanism plainly: Googlebot "compares the [price] attribute in your data source with the prices on your landing page", using your structured data markup where you have it. The same page adds the sentence that decides most Shopify cases: "If data on your website is passed dynamically with JavaScript after the page is loaded, this will trigger an error."

So a Shopify store can hold a perfectly correct price in admin, submit that price, and still be told the page says otherwise, because what Googlebot read was the markup in the served HTML, not the number a shopper sees after the page finishes loading.

The outcome most merchants never look for: Google corrects you

A disapproval is the visible half of this failure. The other half leaves no error at all, and Google's item-level notifications are pull, not push, so there is nothing to receive either.

Merchant Center's automations page, Allow Merchant Center to update product information automatically, says the feature "keeps your product data accurate by automatically using your landing page data to update your product data", and that "Automations are enabled by default." It then gives its own example: "If your most recent product upload contains a product that costs $4 USD, but your product landing page lists it as $3 USD, we'll update the product to $3 USD in your ads or product listings."

Read that from the merchant's side. When the crawled price is lower than the one you submitted, the product is not disapproved: Google advertises the cheaper number it found on your page, and tells you afterwards rather than asking first. The corresponding entry, Automatic updates: Mismatched price, is written as a fait accompli, "Google updated the price of these products because you enabled automatic item updates for incorrect prices", and its remedy is to make the two agree so the correction stops being needed.

That is why a price mismatch on Shopify is worth checking even when Merchant Center looks clean, and it is a different failure from a stale priceValidUntil date on the search side, which makes Google distrust a price it can otherwise read perfectly well. A stale sale price in your markup does not necessarily cost you a listing. It can cost you the margin on every unit sold through that listing, quietly, for as long as the markup stays stale, and the only place the discrepancy is visible is a page nobody re-reads.

Where the divergence actually comes from

A half-applied sale. A discount app updates the displayed price on the page but never touches the JSON-LD block, which keeps rendering the pre-sale figure. Shoppers see the sale price; Google's structured-data reader sees the old one.

A pricing or currency-conversion app. This is the cause that fits Google's JavaScript sentence most exactly. Multi-currency and localization apps rewrite the price a visitor sees based on geolocation or a currency switcher, but they frequently don't touch the underlying cart.currency.iso_code or the structured-data price and currency fields: those keep reporting the shop's base currency and price. Google is then reading a different amount, or a different currency, from the one the shopper was shown.

A hardcoded price left over from a theme edit. Someone customized a promotional banner or a custom section and typed a price directly into the Liquid template instead of pulling it from the product object. It looks fine at launch and quietly goes stale the next time you change the price in Shopify admin, nothing on the page updates to match, because it was never wired to live data in the first place.

Duplicate schema from an app. If a review app or a second SEO app injects its own Product block alongside your theme's native one, you can end up with two different prices declared on the same page. Google is left choosing between them, and if it reads the stale one, that is what gets compared against the price you submitted.

A note on Open Graph tags, because the advice circulating on this problem is split. Google's Merchant Center documentation for automatic item updates names schema.org properties and does not mention Open Graph anywhere, so we do not claim og:price:amount is what Merchant Center reads. It is still worth checking: when a page's og:price:amount and its structured-data price disagree, that is direct evidence the page has more than one source of truth for its price, which is the underlying condition behind every cause above. Our own scan treats that disagreement as a critical finding for that reason, not because Google published a rule about it.

The requirement that decides all of them: the markup has to be in the served HTML

Google's Supported structured data attributes and values page is short and unusually direct about this: "Structured data markup must be present in the HTML returned from the web server. The structured data markup can't be generated with JavaScript after the page has loaded." The same page names what the markup has to carry for automatic item updates (the schema.org properties price, priceCurrency, availability and condition) and how they must be written: the price as a "Number. Submitted without currency symbols, thousand separators, or spaces (for example, '1498.99')", and priceCurrency as "Text. Submitted in a three-letter ISO 4217 format". A badly formatted value is not a cosmetic problem here: it is the difference between a warning you can ignore and an item Google treats as invalid.

That single sentence about JavaScript is what separates the two kinds of Shopify price app. A theme's own markup is rendered by Liquid on Shopify's servers, so it arrives in the HTML Googlebot receives. A currency switcher or a discount widget that rewrites the price in the visitor's browser does not touch that HTML at all: the served page keeps the old number, the shopper sees the new one, and both parties are looking at something real. There is no screen in Shopify admin that shows you the first one.

It is also worth checking that any product markup is there at all, and that what is there actually parses, because a block Google cannot read is a block Google cannot take a price from. On 2026-09-14 we fetched six live Shopify product pages as Googlebot and parsed every application/ld+json block in the response: none of the six carried a Product or ProductGroup block with an offer price in the served HTML. What we found instead was breadcrumbs, an organisation block, a collection page, and, on one store, a fragment of JavaScript that builds a Product block with setAttribute('type','application/ld+json') once the page has loaded, which is precisely the case Google's documentation excludes. Six stores is a sample, not a rate, and it is not a claim about Shopify's own filter, which does emit its markup server-side. It is a reason to look at your own page before assuming the price Google reads is the one you think it is.

When Google gives up on your markup entirely

There is a third state, and it is account-wide rather than per-product. The issue Not performing automatic item updates for price reports that "Automatic item updates have stopped for all of your products", caused by incorrect microdata on the website or a large-scale mismatch between the markup and the Merchant Center data. Google's own remedy is to review the markup on the product landing pages against the price value in Merchant Center and correct it.

The consequence is easy to miss because it is a loss of a safety net rather than an error. Until it is fixed, the mechanism that was quietly keeping your listings aligned with your site, for better or worse, is switched off for the whole account, and prices drift with nothing catching them.

The fix: one single source of truth, in Liquid

The rule that fixes essentially all of these cases: the visible price and the price inside the structured data must be pulled from the same Liquid variable, live, at render time, on the server. In practice:

"offers": {
  "@type": "Offer",
  "price": "{{ product.selected_or_first_available_variant.price | divided_by: 100.0 }}",
  "priceCurrency": "{{ cart.currency.iso_code }}",
  "availability": "{% if product.available %}https://schema.org/InStock{% else %}https://schema.org/OutOfStock{% endif %}"
}

No hardcoded numbers, no values duplicated from a separate app config, no currency assumed from the shop's default market. If you run Shopify Markets or a currency-conversion app, cart.currency.iso_code needs to reflect the converted currency the shopper is actually seeing: check that the app writes to that value rather than only restyling the displayed number with CSS or a client-side script.

Availability deserves the same discipline: if the schema says InStock while the real variant stock is empty (or vice versa), that is its own disapproval, with its own error, and it should also be driven from product.available rather than hardcoded or set once and forgotten. The same goes for the opposite extreme: a variable that resolves to nothing puts a price of zero in front of Google, which is rejected outright rather than compared.

How to audit this yourself, in five minutes

  1. Read the served HTML, not the rendered page. Use view-source: on the product URL, or fetch it with curl, and find the <script type="application/ld+json"> block. Dev tools show you the page after scripts have run, which is the one view that cannot tell you what Googlebot received. If no product markup is in that HTML, Google is falling back on what it can extract from the page itself.
  2. Check the format, not only the number. price must be a bare number (no symbol, no thousand separator, no space) and priceCurrency a three-letter ISO 4217 code matching the country you target.
  3. Put three numbers side by side: the price in the served markup, the price a shopper sees, and the price in your Merchant Center data source. Any two of them disagreeing is the mismatch, and which two tells you where to look.
  4. Repeat under a second currency. If you run Shopify Markets or a currency app, fetch the same page again in another country context and compare the served HTML, not the browser view. If the HTML is identical and only the visible number changed, the conversion is happening in the browser and Google never sees it.
  5. Read both issue names in Merchant Center. Under Products, the Needs attention tab carries the per-product disapproval; the account-wide "Not performing automatic item updates for price" means Google has stopped reading your markup for every product, which is a different and larger problem than one wrong price.

Then trace whichever app or template edit owns the wrong value, and route it through the single Liquid variable above.

This is exactly the kind of failure that produces zero visible symptoms for weeks: the storefront looks completely normal, sales keep coming in, and the signal is either a slow bleed of disapproved SKUs in a tab most merchants don't check daily, or no signal at all because Google decided to use the price it found. Across our full-catalog scans of more than 90,000 Shopify product pages, roughly 46% of stores had at least one critical Google visibility issue, without knowing it.

A price is not a thing you set once. It is set again by every sale, every currency, every app that touches the product, and each of those events can move the number on the page without moving the number in the markup. That is what makes this failure mode recur rather than resolve: fixing today's mismatch does not tell you about the next one, and neither does Merchant Center when its automations quietly absorb it. It is one entry in a longer list of Shopify failures that leave the admin looking perfectly correct.

StoreCanary checks this on your store

Run your own check. We compare your displayed price, JSON-LD, and Open Graph tags across your catalog automatically: read-only, no admin access, about 2 minutes.

Scan my store for free

Is this happening on your store right now?

Merchant Center will not tell you until it disapproves a listing. The product is live in your store, and Google Shopping is showing a different price, the wrong availability, or nothing at all. The only way to know is to check the feed the way Google reads it.

  • 90+ checks, the way Google reads your product feed. Price and availability mismatches, missing identifiers, redirect chains.
  • Read-only. No app to install, no admin access, nothing changes on your store.
  • The exact fix for each page, in plain English, so you or your developer can act the same day.

Across full-catalog scans of more than 90,000 Shopify product pages, roughly 46% of stores had at least one critical Google visibility issue.

Scan my store for free

Free scan: issue types, counts and first fixes. Full report on every product page, not a sample: $49 once, no subscription.

Next in this cluster