GuideUpdated 2026-07-06

Shopify sale pricing in the feed

How Shopify's compare-at price maps to Google's price and sale_price, how scheduled sales flow into the feed, and how to keep the feed matching the landing page so sales don't get disapproved.

Plain-English summary

Shopify has two price fields per variant: the price and the compare-at price. When compare-at is higher, Shopify is showing a sale. Google Shopping expects that expressed as two attributes, price (the original) and sale_price (the discounted one), so it can show a strikethrough and a sale badge. The commonest mistake is running sales by editing the price field down instead of using compare-at, which means Google never sees a sale at all, and the second commonest is a feed sale price that no longer matches the live landing page.

Shopify's two price fields

Every Shopify variant has two price fields, and the relationship between them is the sale:

  • Price: what you're charging right now.
  • Compare-at price: the "was" price, shown struck through on your storefront when it's higher than the price.

When compare-at is empty or lower than price, there's no sale: the product is simply at its price. When compare-at is higher than price, Shopify renders a strikethrough on your site: "was £80, now £60". That gap is how Shopify expresses a discount.

How that maps to Google

Google Shopping expresses a sale with two attributes, and the mapping inverts which Shopify field goes where:

Situation Shopify Google price Google sale_price
No sale price = £60, compare-at empty £60 (unset)
On sale price = £60, compare-at = £80 £80 (the compare-at) £60 (the price)

The key move: when compare-at is higher, the compare-at becomes Google's price and the Shopify price becomes Google's sale_price. That's what lets Google draw the strikethrough and attach a sale annotation: showing shoppers the old price crossed out and the new one highlighted, sometimes with a price-drop badge.

Get this wrong and you lose the merchandising you already paid for with the discount.

The mistake that hides every sale

The single most common sale-pricing error on Shopify is running a sale by editing the price field down and leaving compare-at empty. From your storefront it might even look fine if you've set things up a certain way: but in the feed, Google sees one price with no compare-at, so it concludes there is no sale:

  • No strikethrough.
  • No sale annotation.
  • No price-drop badge.

You discounted the product and got none of the sale merchandising in your ads. The fix is simple and worth repeating: compare-at holds the original, price holds the discounted amount. Never discount by editing price alone. This is item 5 on the common feed mistakes list for a reason.

Scheduled sales and the timing gap

Many Shopify sales are scheduled: an app or manual edit drops prices at a start time and restores them at an end time. Google supports this too, via optional sale start/end dates, but the real-world risk is timing:

  • Feeds refresh on a schedule, not instantly. If a sale starts at midnight and the feed syncs at 6am, there's a window where the live site is on sale but the feed still shows full price: or the reverse when the sale ends.
  • During that window, Google's sale_price and your landing-page price disagree: which is exactly the mismatch Google's crawler flags.

The tighter the feed's sync to your actual price changes, the smaller that window and the lower the disapproval risk.

Matching the landing page

Google actively compares the price and sale price in your feed against what a shopper sees on the landing page. A disagreement (for any reason) is a price mismatch, one of the most common and most damaging disapproval types, because it can suppress the item and dent account trust. Causes are almost always sync or process gaps:

  • The feed hasn't caught up with a price edit.
  • A scheduled sale started or ended between syncs.
  • A currency or rounding difference between the feed and the storefront.
  • A variant-level price the feed sent at product level (so one variant's price is right and the others are wrong).
Deep dive What makes a sale price legitimate to Google

Google doesn't just check that your sale price matches the landing page: it also has a view on whether the sale is genuine, and stores get caught out by both.

The rules that matter:

  • The reference price must be real. Google's sale_price is a discount from a price that was actually charged for a meaningful period. A compare-at price you set to £120 on a product that has only ever sold at £60 isn't a sale: it's an inflated "was" price, and permanent fake-discount pricing can be treated as misrepresentation. The strikethrough is only earned if the original price was genuinely in effect.
  • The sale needs to be an actual reduction. A sale_price equal to or above price is nonsensical and rejected. It should be a clear, real drop.
  • Duration expectations. Sales are meant to be temporary. A "sale" that never ends drifts back toward the fake-discount problem; if the reduced price is now your standing price, it should simply be the price, with no compare-at.
  • Consistency across the funnel. The sale must hold from ad to landing page to checkout. Advertising £60, showing £60 on the PDP, then adding the discount only at the basket is a mismatch: Google reads the landing page, and the landing page said one thing while checkout did another.

The scenario that catches careful merchants: a Black Friday sale scheduled through a discount app that applies the reduction at checkout rather than changing the variant price. The product page still shows £80, so the feed maps price = £80 with no sale_price, the ad shows full price, and the actual discount is invisible to Google right through to the moment of payment. You ran a sale nobody searching Google could see, and (because the basket price undercut the advertised price) you may also have tripped a mismatch. The lesson: for Google to show and honour a sale, the discount has to live in the variant's compare-at/price fields, not in a checkout-time promotion, and the feed has to stay tightly synced to those fields as they change.

Because /tools reads your Shopify price and compare-at fields directly and maps them to price/sale_price (rather than you hand-maintaining the split) it keeps the sale expressed the way Google expects, and flags where a feed price has drifted from the live landing page. The mapping and any flags live in /tools' supplemental layer; your Shopify price fields are read, never overwritten, so how you run and schedule the sale stays entirely in your hands.

How /tools handles this

/tools reads each variant's price and compare-at price and maps them correctly: when compare-at is higher, compare-at becomes Google's price and the price field becomes sale_price, so your sales actually show as sales. It watches for the drift that causes price-mismatch disapprovals (feed price out of step with the landing page, a scheduled sale that's started or ended between syncs) and surfaces it before Google does. All of this is written to /tools' supplemental feed layer; your Shopify pricing is read, never changed, so you keep full control of how and when you discount. Price consistency is a monitored input to your Feed Health Score, and the closely related availability promise is covered in inventory and availability sync.

Frequently asked questions

How do I run a sale so Google shows it?

Put the original price in Shopify's compare-at price and the discounted price in the price field. The feed then maps compare-at to Google's price and the price field to sale_price, so Google shows a strikethrough and a sale annotation. Editing the price field down with no compare-at means Google sees no sale.

Why is my feed sale price different from my website?

The feed hasn't caught up with a price change, or a scheduled sale started or ended between syncs. Google compares the sale_price it has against your live landing page, if they disagree, the item risks a price-mismatch disapproval.

Does Google have rules about how long or how deep a sale can be?

Yes. The sale price must be a genuine reduction from a price that was actually charged, and permanent "sales" where the compare-at was never a real selling price can be treated as misrepresentation.

Put this into practice. /tools rebuilds messy product data into Merchant Center-ready feeds. Connect a store and see your Feed Health Score in minutes.
Try /tools →