Name one system that decides price and one that decides stock. That’s usually your commerce admin and its inventory. Everything else is a copy: the product page, its structured data, your feeds and the catalog records built from them.
When copies disagree, write down each copy’s value and the time you captured it. A copy that’s older than the last change at the source is probably lag. A copy that’s newer and still wrong is a bug in how it’s made.
No platform we checked promises instant updates. So fix the source and the mapping, then check again.
Why the copies drift
On a typical Shopify store, one price edit has to reach several places, each on its own schedule:
| Copy | How it’s made | What its owner documents about updates |
|---|---|---|
| Product page | Rendered from your admin | Immediate on your site, unless a cache or app sits in between |
| Structured data | Emitted by your theme or an app, in the HTML or by JavaScript | Google warns that JavaScript-generated markup can make Shopping crawls “less frequent and less reliable” for fast-changing price and availability |
| Feed file (ChatGPT, Merchant Center) | Exported by an app or script, then uploaded | OpenAI: “Submit the current price; update the feed when a sale starts or ends.” Uploaded dates “do not schedule price or availability changes” |
| Shopify Catalog | Syndicated from your admin | Shopify says Catalog “continuously updates your product data”. No time is given |
| What an assistant shows | Built from these sources and third-party data | OpenAI: after a price change “there may be some delay before it is reflected”. No length is given |
The last row is the one founders worry about, and it’s the one you can see least. OpenAI says ChatGPT receives prices “from third-party providers”, and that the price in its first answer “typically reflects the price from the first merchant listed”. We can’t see what any assistant tells a particular shopper. The audit below works on the copies you can capture yourself.
Decide what’s authoritative first
Write it down once, per fact. Disagreements are easy to diagnose when everyone knows which copy wins.
| Fact | Authoritative source | Copies that must follow it | Watch for |
|---|---|---|---|
| Active price | Your admin’s variant price | Page, markup price, feed price or sale_price, Catalog price |
Apps that change the price at checkout. Prices that vary by location |
| Regular or “was” price | Your admin’s compare-at price, but only if it’s a genuine regular price | Markup StrikethroughPrice, feed price during a sale, Catalog list_price |
A compare-at price left on after the sale ends |
| Stock state | Inventory, plus your “sell when out of stock” setting | Page message and buy button, markup availability, feed availability |
A template that hard-codes “In stock” |
| Currency | The market’s currency setting | Every price copy | One URL showing several currencies |
Two rules from Google decide most cases. Its price rules say to submit “an amount and currency that match the price on your landing page and the checkout pages”, and not to change the landing-page price “based on a user’s location”. Its availability rules say availability must match across “the landing page, structured data (if available), checkout page, and your data source”, and that a mismatch “will trigger a product disapproval”.
Shopify lets you choose whether Catalog shows your compare-at prices to AI agents, or only the current price. Shopify’s developer docs say the catalog only returns a list_price when the merchant shares compare-at prices and the compare-at price is higher than the price. Decide what your compare-at price means before you map it anywhere. Our feed guide works through that decision.
The price-stock consistency audit template
Run it on one variant at a time. Capture every copy within a short window, note the time, and fill in one row per copy.
Header: product, variant, SKU, audit date, and the authoritative values right now, with the time you read them.
| Copy and where you captured it | Value seen and time | Matches source? | Diagnosis |
|---|---|---|---|
| Product page, rendered, for this variant | |||
| Structured data, from the page source | |||
| ChatGPT feed file, last uploaded version | |||
| Merchant Center, the product’s current data | |||
| Catalog response, if you or a partner can query it |
Diagnosis rule (proposed). For each row that doesn’t match:
- The copy is older than the last change at the source. Probably lag. Note it, and re-check after the copy’s next update. OpenAI documents a delay without giving its length, and Shopify gives no time for “continuously”, so don’t promise a fix time.
- The copy is newer than the source change and still wrong. It’s a bug in how the copy is made: wrong field mapped, a cached template, a hard-coded value or a currency conversion. Fix the mapping.
- You can’t tell how old the copy is. Record it as unknown and treat it as a bug until you can time it. Don’t log it as a match, and don’t log it as lag.
The mismatch log. Keep every mismatch in one running list: date found, copy, field, value seen against expected value, diagnosis, fix, and the date you re-checked. A recurring row in the log points to a pipeline problem. A one-off row is usually a sale that started or ended between updates.
Google’s Merchant Center can also correct some mismatches itself. Its product-data automations use structured data and other on-page signals to update price, availability and condition. Its example is an upload at $4 updated to the $3 shown on the page. It also says automations “may not work for products with frequent price or availability value changes (for example more than once per day)”, and that a product may be disapproved instead. Matching is relaxed: in_stock, preorder and backorder are treated as compatible with in_stock. That makes accurate markup on the page matter more, not less. We haven’t seen the account setting for this feature.
What a catalog response can and can’t tell you
If you can query Shopify’s catalog, or a partner does it for you, read the response carefully before you call anything a mismatch. Shopify’s documentation says each variant carries price, “the price the buyer pays”, and may carry list_price, the compare-at price, in minor currency units. So 6800 means $68.00.
Our October 26 snapshot of the top 10 Shopify Catalog results for 162 shopper-style searches (1,620 result occurrences, no buyer context) shows why the per-variant check matters:
- One variant per product. In every occurrence, the product’s price range equalled the price of the single variant returned. The example in Shopify’s docs shows a range spanning variants, but in our searches only one variant came back.
- Same product, different price. A watercolor kit appeared in two searches. One returned a variant at $68.00 with a $151.00 list price. The other returned a smaller set at $36.00 with an $80.00 list price. That’s variant selection, not an error. Compare a catalog price with the same variant’s price on your page, never with “the product”.
- List prices. 423 of the 1,620 first variants carried a list price, and every one was higher than the price, as the documentation says it must be.
- Stock and currency. 1,617 first variants were marked available and 3 unavailable. 1,568 were priced in USD and 52 in 11 other currencies, from a single unpersonalized context.
We didn’t compare these values with the merchants’ live pages. These figures describe the response’s shape, not a mismatch rate.
Worked case: a sale ends on Sunday night
Hypothetical store, not a real listing. The 4 L top-fill humidifier from our feed guide ran at $69.99, with a genuine regular price of $89.99, until Sunday 23:59. On Monday morning, the owner ends the sale in the admin and audits the white variant. The black variant sold out on Saturday.
| Copy | Value seen and time | Matches source? | Diagnosis |
|---|---|---|---|
| Admin, the source | $89.99, no compare-at. White in stock, black 0 and not sold when out. Read Mon 09:00 | Source | — |
| Product page, white | $89.99, “In stock”. Mon 09:02 | Yes | — |
| Structured data, black | price 89.99, availability InStock. Mon 09:03 |
No | Newer than the change and wrong: a bug. The template hard-codes InStock. Fix it to read inventory |
| ChatGPT feed file | price 89.99 USD, sale_price 69.99 USD, black in_stock. Uploaded Sat 06:00 |
No | Older than both changes: lag. Dates in the file don’t end the sale. Re-export and upload |
| Merchant Center | $69.99. Last updated Sun 20:00 | No | Older than the sale end: probably lag. Re-check after the next feed fetch |
Only one row is a real bug, and it’s the one that would have kept telling every reader of the page markup that a sold-out variant was in stock. The two lag rows have a known cause and an action: the upload. The audit doesn’t promise when any assistant will show $89.99. OpenAI documents a delay and doesn’t give its length. What the audit does is make sure every copy you control is right, and that each remaining gap has a date on it.
How often to re-run this, and when stale data expires, is covered in our freshness guide. Shipping costs and return terms need the same treatment and have their own markup, covered in our shipping and returns guide. For the markup rules themselves, see product schema markup.
How we checked
On October 27, 2026 we read OpenAI’s shopping help article, file-upload Products spec and merchants page. We read Google Merchant Center’s price, availability and product-data automations pages, and Google’s merchant listing structured data page. We read Shopify’s Catalog help page and the Global Catalog reference. Each documented statement in the tables is quoted or closely paraphrased from the page named.
The snapshot figures come from our saved Shopify Catalog results of October 26, 2026: 162 searches, top 10 each, one unpersonalized context, one variant returned per product. We counted availability flags, list-price presence and whether each list price was higher than its price, currencies, and whether each product’s price range differed from its returned variant’s price. Merchant and brand names are withheld. The worked case is a hypothetical store.
Your next step
Pick your best-selling variant. Within ten minutes, capture its price and stock from your admin, the rendered page, the page’s structured data, and your latest feed file, with the time of each capture. Fill in the audit template. Any copy that’s newer than your last change and still wrong is a mapping bug, so fix that first.