← All field notes
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 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 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 — 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 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.

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 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:

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 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 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. 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 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:

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.