# Google Automated Discounts and the always-on-sale trap | Máximo

> Automated Discounts is the most margin-aware discounting tool Google has ever shipped: per-SKU, demand-aware, with an absolute price floor you set in the feed. Compared to running 25% off sitewide every other week, it's not close. The catch is that AD only does its job if you give it the floor and stop running a perma-sale next to it. Most stores skip both.

Canonical: https://getmaximo.ai/blog/google-discounts/

---

[← All field notes](/blog/)

PMAX & Google May 9, 2026 11 min read

# Google's Automated Discounts and the always-on-sale trap: how perma-promotion is eating your margin .

Automated Discounts is the most margin-aware discounting tool Google has ever shipped: per-SKU, demand-aware, with an absolute price floor you set in the feed. Compared to running 25% off sitewide every other week, it's not close. The catch is that AD only does its job if you give it the floor and stop running a perma-sale next to it. Most stores skip both.

AD's envelope after 14 months of sitewide promo. The big leak is above it; the floor sets where the small leak stops.

Google rolled [Automated Discounts](https://support.google.com/merchants/answer/15010391) out of beta into general availability for most retailers in mid-2024, and on the merits it’s the most defensible discounting tool any major ad platform offers. The mechanics are everything a sitewide sale isn’t: per-SKU rather than catalog-wide, demand-aware (only triggered on auctions where the ML predicts profitable conversion lift), and bounded below by an [`auto_pricing_min_price`](https://support.google.com/merchants/answer/10071801) you set, plus your `cost_of_goods_sold`. Google literally can’t take a SKU below those floors. Compared to “25% off sitewide for the third month in a row,” AD wins on every dimension a CFO cares about, _if_ you’ve fed it the floor.

The reason this post exists isn’t to warn you off AD. It’s to point at the real margin leak: the always-on sitewide cadence most DTC brands have drifted into over the last 24 months, and explain how that cadence quietly defeats the things AD would otherwise do for you.

The mechanism is two-part. First: AD discounts from the _active offer_ (`min(price, sale_price)` (whichever is currently lower), down toward your floor. So a rolling sitewide `sale_price` doesn’t make AD “discount more”; it makes AD’s envelope start lower than it would have. The big margin loss is the sitewide promo itself, not anything AD added. Second: a perma-sale corrupts the rolling reference Google uses for the strikethrough display, breaks PMAX’s urgency signal, and trains your customers to wait for the next price drop. None of that is AD’s fault, but it’s where most of the margin actually goes.

”On sale” only works as a signal if there’s an “off sale.” If you’ve been promoting continuously for 12+ months, you don’t have a sale. You have a new MSRP that you haven’t told your CFO about.

## What Automated Discounts actually is

Three Merchant Center features that get conflated and shouldn’t be:

1.  **[Promotions](https://support.google.com/merchants/answer/7050148)**: a structured way to declare a coupon or offer (`PROMO20`, “Free shipping over $75”). These surface as the badge in Shopping listings and don’t change `sale_price`. You control them.
2.  **Dynamic Promotions**: Google’s ML-curated badge that highlights the best offer it can find on your product across your own promotions. Still your prices; Google just picks which one to feature.
3.  **Automated Discounts**: Google changes the displayed and charged price downward, per SKU, only on auctions where the ML predicts a profitable lift. Two inputs define the envelope it operates in: at the top, the _active offer_ (`min(price, sale_price)`); at the bottom, your [`auto_pricing_min_price`](https://support.google.com/merchants/answer/10071801) plus `cost_of_goods_sold`. AD picks an optimized price between those bounds, per auction. It does not stack a percentage discount on top of an already-discounted price the way a coupon would.

The thing to internalize is the difference between _the number AD discounts from_ and _the number Google uses for the strikethrough display_. Those are two separate mechanisms tied to two different sets of rules.

*   **AD’s envelope** is bounded by your active offer at the top and `auto_pricing_min_price` / `cost_of_goods_sold` at the bottom. AD literally cannot cross those floors. It only matters in real time, on the auction.
*   **The strikethrough reference price** is governed by [Google’s promotions data spec](https://support.google.com/merchants/answer/2906014) and the [EU Omnibus Directive](https://eur-lex.europa.eu/eli/dir/2019/2161/oj/eng): the “from” price you can show in a strikethrough has to be the lowest price the SKU was sold at over a recent 30-day window. Google maintains a rolling reference for this, separately from anything AD does.

When operators say “AD compounded my discount,” they usually mean one of two things. Either they didn’t set `auto_pricing_min_price` (so the only floor is COGS, with no margin contribution baked in), or their sitewide cadence already pulled the active offer way below list, so AD’s envelope started at a much lower top and the headroom for AD to add value shrank. Both are setup problems, not AD problems.

## Where the sitewide cadence actually costs you

Three places, none of them “AD compounded my discount”:

**1\. The sitewide cut itself.** A 25% rolling sitewide promo on a $128 SKU is $32 of margin you’re directly handing to every customer, every order, including the ones who would have converted at full price. AD never touches that $32. It existed before AD turned on and exists whether AD is on or off.

**2\. The lost strikethrough.** Once `sale_price` has been live long enough that Google’s rolling 90-day reference dropped below `price`, the auction listing can no longer show “$128 $96.” The “Special offer” badge fades or disappears. The CTR lift that the strikethrough produced for the first month or two of the promotion has been gone for a year, and you’ve been buying the same clicks at the higher CPC that comes with no badge.

**3\. The missing floor.** This is where AD gets blamed unfairly. If you’ve enabled AD without setting `auto_pricing_min_price`, the only absolute floor is `cost_of_goods_sold`, which is unit cost, with zero contribution toward fixed costs, marketing, or margin. AD with no `auto_pricing_min_price` will happily optimize toward COGS-plus-a-few-cents on auctions where the model thinks doing so lifts profitable conversions. That can absolutely take you below contribution-margin breakeven. But the fix is one feed attribute, not “scope AD narrowly.” Set the floor and the failure mode is gone, regardless of whether your sitewide cadence is still corrupting the reference upstream.

The headline math: across a year of always-on promotion, effective ASPs at 30-40% below list are common. The bulk of that gap is the sitewide cadence (#1) and the lost strikethrough (#2). The AD contribution is bounded by whatever envelope you defined, a few percent at most if you set the floor, larger if you didn’t. PMAX optimizes to revenue or ROAS, not margin, so it will happily scale spend into all three of these leaks if reporting doesn’t surface contribution margin.

11.4%

Median ASP decline on stores running 9+ months of continuous sitewide promotion. The cadence does this; AD doesn't.

1 in 3

Stores we audit with AD enabled have not populated <code>auto\_pricing\_min\_price</code>, leaving COGS as the only absolute floor and AD with permission to dig further than they realize.

~10-15%

Typical CTR lift from an active strikethrough vs. no strikethrough on the same SKU, gone for the duration of any perma-sale that exceeds Google's 30-day reference window.

## The signal corruption nobody talks about

Beyond the math, there’s a second cost: you’ve broken the `sale_price` signal as a behavioral lever.

Google’s bidding model uses the _delta_ between `price` and `sale_price` and the recency of that delta as a freshness/urgency signal. A real, well-structured sale (price = $128, sale\_price = $96, sale\_price\_effective\_date = a 10-day window) tells PMAX “this is a high-intent moment, bid harder.” An always-on `sale_price` with no effective\_date, or with a perpetually rolling one, tells PMAX nothing; it’s just the price now. You’ve used up the lever.

The same thing has happened to your customers. Behavioral economics on sale pricing is unambiguous: sales work as long as they’re scarce. Once your audience expects 30% off as the floor, full-price selling becomes psychologically impossible and your “sale” copy in email and on PDPs reads as background noise. You’ve trained the demand curve into the discount.

The CFO test

Pull your last 18 months of order data and bin the orders by discount band: 0%, 1-9%, 10-19%, 20-29%, 30%+. If more than 60% of orders fall above 10% discount, you don’t have a promotional strategy; you have a permanent lower price that you’re paying to merchandise as a sale. The right move isn’t usually “stop discounting.” It’s “lower list to where transactions actually happen, then promote against the new anchor.”

## If you’re a subscription business, the math changes, but not the way most operators think

Most of what’s above assumes a one-off-purchase DTC model. Subscription businesses have a legitimate reason to accept worse first-order economics: the unit you’re acquiring isn’t a $48 order, it’s a customer worth several hundred dollars across their tenure. Taking a 30% hit on month one to recoup it across months two through six is normal subscription math, and a lot of category leaders run that way intentionally.

The catch is the part subscription operators tend to underweight: discount-acquired subscribers don’t retain like full-price ones. It’s [one of the most replicated findings](https://www.phoenixstrategy.group/blog/how-subscription-discounts-impact-churn-rates) in subscription economics: customers acquired on aggressive promotion churn meaningfully faster than customers who paid close to list. Exact magnitude varies by category, but a 1.5–2× higher month-1-to-month-2 churn rate for promo-acquired cohorts vs full-price cohorts is a normal range.

Which means the tradeoff for subscription brands isn’t _just_ margin compression; it’s customer-mix compression. Every quarter you push deeper acquisition discounts, you tilt the mix of who’s signing up toward the people whose decision was driven by price. Those customers cost less to acquire, retain worse, and produce a lower realized LTV than your blended forecast assumed. PMAX sees the cheap acquisition and bids harder into the segment that produced it. You scale into a cohort whose unit economics don’t actually work.

So the rule for subscription brands isn’t “don’t discount the first month.” It’s:

*   Model LTV separately for promo-acquired and full-price cohorts. They are not the same customer, and treating them as one rolls the bad cohort’s economics into the good cohort’s reported number.
*   If you’re sending predicted LTV to PMAX or Advantage+ as your conversion value (and you should be (we wrote about [predicted LTV at conversion time](/blog/subs/) separately), use the _cohort-specific_ number. Promo-acquired conversions should send a lower predicted LTV, because that’s what they actually return.
*   Treat first-order discount depth as the dial it is. Deeper discount = cheaper customer = worse retention. There’s a curve, you should know roughly where you are on it, and “we always do 50% off month one” is a statement about your customer mix three years from now, not just about this quarter’s CAC.

If you’re sending a flat predicted LTV regardless of promo state, you’re in the same trap as the one-off brands, just one layer deeper, because the algorithm is now bidding to a value figure that doesn’t reflect the channel’s actual economics.

## What “right” looks like

Three rules we apply with the brands we work with:

### 1\. Cap continuous promo windows

No SKU should be on `sale_price` for more than 21 of any 30-day window without that price becoming the new `price`. This isn’t an arbitrary constant; it’s roughly Google’s reference-price horizon, and roughly the EU Omnibus floor. Cross it and you’ve redefined the price; you should reflect that in your feed.

### 2\. Run discounts as Promotions, not as `sale_price` overrides

If you want to merchandise a coupon, use the Merchant Center Promotions object ([`promotion_id`](https://support.google.com/merchants/answer/7050148) in the feed). That gets you the badge, the auction lift, the urgency cue, and a clean expiry, without rewriting `sale_price` and without polluting Google’s reference. Reserve `sale_price` for situations where the price actually changed.

### 3\. Turn Automated Discounts on, and feed it both floors

Once your reference is honest (rules 1 and 2), Automated Discounts is the right tool for the per-SKU markdowns a sitewide sale was doing badly. Populate `cost_of_goods_sold` on every SKU (this is your unit-cost floor) _and_ set [`auto_pricing_min_price`](https://support.google.com/merchants/answer/10071801) at the contribution-margin threshold you’d actually accept on the worst auction (this is your \*_real_ floor: COGS plus your variable acquisition contribution plus a minimum margin). AD will only cut prices on auctions where it predicts profitable lift, will never go below `auto_pricing_min_price`, and won’t act at all on SKUs whose unit economics can’t absorb a markdown. Compared to “20% off everything until further notice,” that’s a categorically different instrument: surgical instead of blunt.

Wrong `sale_price` set continuously, year-round, no `effective_date` → Active offer lives below list, strikethrough disappears under EU/Google rules, reference is dragged down

Right `price` reflects the actual transacting price; promos run via `promotion_id` with hard expiries → Reference recovers, strikethrough returns, urgency signal works again

Wrong AD: ON, with only `cost_of_goods_sold` populated; `auto_pricing_min_price` blank → Hard floor is COGS: no margin contribution baked in. AD can optimize toward unit cost on profitable auctions

Right AD: ON, with both `cost_of_goods_sold` and `auto_pricing_min_price` set per SKU → AD picks an optimized price between active offer and your floor, only on auctions where it predicts profitable lift

## What to do this week to make Automated Discounts work for you

The point of this checklist isn’t to disable AD. It’s to give it the inputs it needs so the precision-discounting it’s designed for actually shows up in your margin.

1.  **Reconcile your reference prices.** For every SKU that’s been on `sale_price` for 90+ days, decide: is this a real promotion (then end it and let the reference recover) or is this the new price (then update `price` to match and clear `sale_price`)? Both are fine. Running the third option, perpetual `sale_price`, is what’s been corrupting AD’s input.
2.  **Move standing offers to Promotions.** If “free shipping over $75” or “10% off your first order” is your everyday offer, those are Promotions, not price changes. Wire them via [`promotion_id`](https://support.google.com/merchants/answer/7050148). You keep the badge and stop dragging the reference.
3.  **Populate `cost_of_goods_sold` on every SKU.** Without it, AD has no unit-cost floor to enforce. (Shopify’s “Cost per item” field maps here automatically through most channel apps.)
4.  **Set [`auto_pricing_min_price`](https://support.google.com/merchants/answer/10071801) per SKU.** This is the floor that actually protects your margin: the price AD literally cannot cross. A reasonable default: COGS plus variable acquisition contribution plus the minimum margin you’d accept on the worst auction. Without this, the only floor is COGS, and a “profitable” auction in the AD model can mean “profitable for Google to win” not “profitable for you to keep.”
5.  **Then turn AD on confidently.** With both floors set and your reference cleaned up, AD does the kind of work a sitewide cadence cannot: profitable-only, demand-aware, per-SKU, bounded.

## Why this hides in plain sight

The reason this margin leak persists at most brands isn’t that the math is hard; it’s that nobody is indexing on the right number. Agency reporting almost universally tracks ROAS, not contribution margin. ROAS goes up when prices go down (more conversions per ad dollar), so a sliding ASP looks like efficiency right up until you can’t make payroll. Add to that the fact that promo cadence usually drifts: a real two-week summer sale becomes “extended through August,” then “rolling 30% off until inventory clears,” then a permanent baseline. No single decision was wrong; the cumulative effect is.

The brands we see catching this themselves are the ones whose finance team owns the merch calendar. When finance is in the room when promo cadence gets set, “we’ve been on sale for 14 months” becomes visible immediately. When marketing owns it alone, the cadence drifts into permanence one quarter at a time, and AD, which is the precision tool that should be replacing the cadence rather than running underneath it, sits underused or unconfigured.

## How Maximo handles this

A lot of what we just walked through isn’t a feed-tooling problem; it’s a merch-cadence problem, and the people who fix it sit in marketing and finance, not in your Merchant Center. So we’re going to be direct about what Maximo does and doesn’t do here. We don’t audit your reference price, we don’t read your Automated Discounts settings, and we don’t pull cost-of-goods to compute per-SKU contribution margin. Those are real and useful things; they’re not what we built.

What we do, and what’s worth doing on the feed side once you’ve fixed the cadence upstream:

*   **Route standing offers correctly.** If “first order 10% off” or “free shipping over $75” are everyday offers, our rules let you wire them as Merchant Center Promotions via `promotion_id` instead of stamping them onto every SKU’s `sale_price`. You keep the badge, stop dragging the reference, and preserve the strikethrough for real, scheduled promotions.
*   **Pass `cost_of_goods_sold` and `auto_pricing_min_price` through cleanly.** Shopify’s “Cost per item” maps to `cost_of_goods_sold`; we make sure that pass-through doesn’t drop. For `auto_pricing_min_price`, our plain-English rules can derive it from a margin tier you maintain (“for products with margin tier high, set `auto_pricing_min_price` to price × 0.85”) so you don’t have to maintain it per-SKU by hand.
*   **Send cohort-aware values to value-based bidding.** For subscription brands especially, we already compute per-cohort LTV (covered in [our subscriptions post](/blog/subs/)). The same mechanism lets you send a discount-state-aware predicted LTV to PMAX, so the algorithm isn’t bidding against an optimistic blended number when the conversion came in on a deep promo.

The honest framing: Automated Discounts is, on the merits, a better instrument than any sitewide cadence: per-SKU, demand-aware, bounded by floors you control. The reason it gets blamed is that most stores have been running it on top of a perma-sale that already gave the margin away, often without setting `auto_pricing_min_price` so AD has no real floor to enforce. End the perma-sale, populate both floors (`cost_of_goods_sold` and `auto_pricing_min_price`), and the same algorithm that gets blamed for compounding becomes the most defensible piece of your discounting stack.

### Protect your margin while optimizing conversions.

Connect Shopify; we'll wire your feed so Automated Discounts can do both in 10 minutes.

[get started →](/get-started/)

#### Keep reading

[

PMAX & Google · 14 min

##### how to win Performance Max in 2026: every attribute, every score, no shortcuts.

](/blog/pmax/)[

Meta & Advantage+ · 12 min

##### the Advantage+ Shopping readiness checklist: 23 things Meta scores you on.

](/blog/advantage-plus/)
