โฆษณา
Across 9.11 million Shopee ad clicks on 80+ Thai shops between February and August 2026, the price of a click moved by 2.01x depending only on the hour it was bought [5]. Not one of those campaigns had a lever for that. Every Shopee ads guide describes the campaign form the same way — a Target ROAS box and a daily budget box, two dials tuned against each other — and that description is wrong in a way that costs money. The two boxes do not reach an individual auction as two instructions. They collapse into one number before any bid is placed, only one of them is active at a time, and the inactive one is doing nothing whatsoever. A campaign can sit for a month with a seller adjusting a target that has not moved a single bid.
This post derives what actually reaches the auction. It is the readable version of Lemmas 1a and 1b of our technical note, CTR & Target ROAS under CPM and CPC [1], which works the algebra out in full. The result matters because it changes the diagnostic question. Not "is my Target ROAS right?" but "is my Target ROAS the thing setting my bid at all?" — and there is a one-line test, computable from two numbers Shopee already shows you.
The thesis: a Target ROAS and a budget are two ways of setting one scalar. That scalar is a threshold on value-to-cost, it is the campaign’s marginal ROAS, and it decides how far down the campaign’s own value ranking the platform will buy — never which auctions it prefers. What would falsify this: a marketplace bidder that let a seller instruction reorder auctions rather than move a cut-off along an order the platform already fixed.
The ceiling, in one line, before the part that is new
Start with the campaign aggregate, because that is the level a Target ROAS is enforced at. Write CR for the probability a click converts, V for the attributed order value when it does, and R* for the target you entered. Expected attributed revenue per click is CR × V. The instruction "return at least R* baht of attributed GMV per baht of spend" is therefore an instruction about price, and it rearranges into a ceiling on what a click may cost.
CPC_max = CR x V / R*
Example: CR = 2%, V = THB 500, R* = 5.00
value per click v = 0.02 x 500 = THB 10.00
CPC_max = 10.00 / 5.00 = THB 2.00That ceiling is the shared premise of this cluster; its consequences for creative and for impression billing are worked out separately in CTR and Target ROAS under CPM vs CPC. Two readings of it are routinely inverted. The target sits in the denominator, so it is an inverse aggressiveness control: raising it lowers the price the bidder may pay, it does not raise the return the campaign earns. And on Shopee GMV Max you never type a bid: per Shopee’s seller education materials the campaign is auto-bid, and you supply a budget and a target while the platform sets the per-click price [2]. CPC_max is a constraint on the platform’s bidder, not a field you fill in.
How Shopee ads bidding works inside a single auction
The campaign aggregate is not where bidding happens. Shopee bids in each auction separately, and the question is what your two instructions look like by the time they arrive there. Index auctions by i. Write v_i for the value the campaign expects from winning auction i, and c_i for what it would clear at. The platform is maximising attributed value subject to the two constraints it holds — the ROAS constraint across the campaign, and the budget across the period.
Attaching a multiplier to each constraint and reading off the coefficient on winning auction i gives a bid with a strikingly simple shape. In the generalized second-price family that search and marketplace ad systems use, a truthful bid takes exactly this form — estimated value divided by a scalar [4]. Both of your instructions, and nothing else you can enter, appear inside that one scalar.
b_i = v_i / beta
where beta = (L_R x R* + L_B) / (1 + L_R)
and the campaign wins auction i <=> v_i / c_i > beta
L_R, L_B = the platform’s multipliers on your ROAS
and budget constraints respectivelyRead the win rule again, because everything below is a consequence of it. That scalar — call it beta — is a threshold on value-to-cost and nothing else. The last auction the campaign wins is the one where v_i / c_i equals beta exactly, which means beta is the campaign’s marginal ROAS, the return on the next baht, while the figure Shopee reports back to you is the aggregate over every auction won. Those are different numbers about the same campaign in the same instant, and the second is always the larger.
You do not choose which auctions your campaign buys. You choose how far down a ranking the platform has already fixed it is willing to go.
Four things that follow, and the first corrects the formula above
1. CPC_max is an average, not a cap on any auction
Where the ROAS constraint is the binding one, beta works out below R*. That has a concrete consequence: b_i is larger than v_i / R* in every single auction. The campaign is permitted to pay more than the "ceiling" on individual impressions, and the aggregate constraint still holds because cheap auctions subsidise expensive ones. In a simulation of 400,000 auctions with dispersed values and clearing prices, more than half the auctions a campaign wins clear above v_i / R* at every target we tested [1]. So a seller who reads CPC_max as "my ads will never pay more than THB 2.00 for a click" has misread it. The correct reading is "the clicks will average THB 2.00, and individual ones will be well either side".
2. Neither lever can prefer one auction to another
Beta is uniform across the campaign. The only thing that varies from auction to auction is v_i, and v_i is the platform’s estimate of what the traffic is worth, not your instruction. This is the standard result for value-ranked ad auctions: a uniform multiplier on estimated value moves a cut-off along an ordering the platform has already fixed, and cannot reorder it [3]. This is why "my Target ROAS should stop it buying junk traffic" does not work: raising the target raises the cut-off along a ranking, and if the ranking is wrong about which traffic converts for you, a higher cut-off inherits the same error. Splitting the campaign is the only mechanism that gives you a second threshold. Segmentation, not targeting, is the seller-side lever with any power to discriminate.
3. A binding budget prunes in the right order, crudely in level
Because raising the budget multiplier raises beta, a budget cut drops the lowest value-to-cost auctions first. This is the rehabilitation of a lever most optimisation writing treats as blunt. A budget is crude about how much it removes and precise about what it removes — and it is the only instrument in the system denominated in money spent rather than in value claimed, which is the reason it is the only one that can bound a loss.
4. The gap between reported and marginal ROAS is dispersion
The ratio of what the campaign reports to what its next baht returns is governed entirely by how widely v_i / c_i varies across the auctions it enters. A campaign whose auctions were identical would have no gap at all. A campaign spanning branded search and broad discovery has a very large one. The table below is simulated rather than measured — the distribution is assumed, only the threshold structure is derived — but the direction is not in doubt.
| Target entered | Marginal ROAS | Reported ROAS | Reported ÷ marginal | Auctions won above v_i / R* |
|---|---|---|---|---|
| 6.00 | 1.26 | 6.00 | 4.75x | 53% |
| 10.00 | 3.56 | 10.00 | 2.81x | 56% |
| 16.67 | 7.66 | 16.67 | 2.18x | 58% |
Simulated, not measured: 400,000 auctions with independent lognormal values and clearing prices, the campaign playing b_i = v_i / beta with beta set so the reported ROAS meets the target. Source: DataGlass Labs technical note, Lemma 1a [1].
Which of your two levers is actually setting the bid
Now the second result, and the practical one. The ROAS constraint fixes a price and says nothing at all about volume. That is easy to show: attributed GMV is the number of clicks times the value of a click, spend is the number of clicks times the price of a click, and the click count cancels when you divide. Every volume satisfies a Target ROAS. Which makes the budget the only instrument in the whole system that bounds quantity.
ROAS(p) = (Q(p) x v) / (p x Q(p)) = v / p <- Q cancels
so the constraint is satisfied <=> p <= v / R* = CPC_max
and the operative bid is the lesser of two ceilings:
p* = min( CPC_max , p_B ) p_B = the price at which
spend exhausts the budgetThree regimes, and a campaign is always in exactly one. ROAS-bound: the campaign would spend less at its ceiling than the budget allows, so the target sets the bid and the budget is slack. Budget-bound: the ceiling would overspend the budget, so the money sets the bid and the entered target never comes into it. Demand-bound: the campaign runs out of audience before it runs out of either constraint, and neither lever is doing anything at all.
| Regime | What binds | Reported ROAS | What moves profit |
|---|---|---|---|
| ROAS-bound | CPC_max | Equals the entered target; budget underspends | The target |
| Budget-bound | The budget-implied price | Above the entered target | The budget |
| Demand-bound | Available audience | At or above the target; both levers slack | Neither — creative, price, assortment |
The three exhaust the cases, and a campaign’s realised spend against its budget separates them: an underspending campaign is ROAS-bound or demand-bound, a fully spent one is budget-bound.
The one-line diagnostic
Here is the payoff. In the budget-bound regime the campaign reports a ROAS above the target you entered, and because both quantities are the same value-per-click divided by a price, the ratios invert exactly. Divide the entered target by the reported ROAS and you get the fraction of its permitted price the campaign is actually bidding.
p* / CPC_max = R* (entered) / ROAS (reported)
Example: entered 5.00, reported 6.32
5.00 / 6.32 = 0.79
-> the campaign is bidding 79% of its permitted price
-> the budget is binding; the target is inertThis inverts the most common reading in marketplace ads. A campaign posting 6.32 against an entered 5.00 looks like a campaign with room — over target, so raise the budget or loosen the target and let it run. It is the opposite. The overshoot is the measure of how hard the budget is squeezing the campaign off a ceiling, and that ceiling is worth exactly zero to you when the target is set at break-even — the subject of the companion post, the Target ROAS that actually maximizes profit [1]. The overshoot measures grip, not performance.
A campaign reporting twice its target is not outperforming. It is bidding half its permitted price, and something else is holding it there.
A worked example: the target that moved nothing
One campaign, one product, on the attributed basis throughout. A click converts 2% of the time into an attributed order worth THB 500, so a click is worth v = THB 10.00. Contribution retained after cost of goods, category commission, transaction and payment fees, the seller-funded voucher share and a returns reserve is 20% — a mid-market figure on Shopee Thailand’s published fee schedule [6] — which makes break-even ROAS 1 ÷ 0.20 = 5.00. Click supply is iso-elastic with elasticity 1.5, normalised so that a THB 1.20 bid buys 1,000 clicks a day. The daily budget is THB 2,400. The seller sweeps the Target ROAS from 8.33 down to 3.00.
| Target entered | Bid (THB) | Spend (THB) | Profit after ads (THB) | Bid set by |
|---|---|---|---|---|
| 8.33 | 1.20 | 1,200 | +800 | the target |
| 6.32 — the crossover | 1.58 | 2,396 | +632 | the target |
| 5.00 — break-even | 1.58 | 2,400 | +631 | the budget |
| 4.00 | 1.58 | 2,400 | +631 | the budget |
| 3.00 | 1.58 | 2,400 | +631 | the budget |
Computed from an assumed iso-elastic click-supply curve, which is not fitted to any account. Source: DataGlass Labs technical note, worked example, Cases 2 and 3 [1].
Every target from 6.32 down to zero produces one identical campaign — same bid, same spend, same profit — because the operative bid is the lesser of the two ceilings and the budget-implied one has won. The budget-implied price contains no R* anywhere in it. A seller lowering the target from 5.00 to 3.00 to "chase volume" changes nothing, sees nothing change, and reasonably concludes that the target does not do very much. It does. It is simply switched off.
Same campaign both times. With the budget binding, the profit line goes flat below the crossover at 6.32 — the entered target has stopped reaching the bid. With the budget slack, the same targets run to a loss of THB 6,173 a day. What the budget costs when it is slack is nothing; what it saves when the target is wrong is THB 2,135 a day at a target of 4.00.
Where this argument breaks
- The bid derivation assumes the platform is maximising attributed value under your two stated constraints. If Shopee’s bidder carries objectives you did not enter — a floor on delivery, a smoothing rule, a pacing target of its own — those add terms this model does not have.
- Traffic bought at a higher price converts worse, so the value of a click is not really constant in the bid. That makes the ceiling implicit rather than closed-form, and it is why the worked example understates how quickly the ceiling stops paying. The companion post on the profit-maximizing target prices that decline explicitly.
- A platform that paces selectively rather than uniformly keeps its best-ranked auctions when the budget binds. That raises the realised value per click and attributes too much of the reported overshoot to price. The diagnostic still tells you the budget is binding; it will overstate how far below the ceiling the bid sits.
- The dispersion table is simulated. The value and cost distributions are assumed lognormal, which is a convenient family and not a measured fact about Shopee’s auction. Treat the direction as robust and the magnitudes as illustrative.
- Everything here is on the platform’s own attribution rule. Attributed GMV is not incremental GMV, and the gap is a further markup on the correct target rather than a correction to this argument.
Methodology
No claim in this post rests on DataGlass customer data. The bid structure, the two-regime result and the diagnostic ratio are derived from the definitions set out in our technical note [1], which states its notation in full and uses no dataset. The dispersion table is a simulation of 400,000 auctions with independent lognormal values and clearing prices. The worked example is arithmetic on an assumed iso-elastic click-supply curve with elasticity 1.5, a 2% click conversion rate, a THB 500 attributed order value and a 20% contribution margin — chosen to be representative of a mid-market Thai marketplace listing rather than fitted to one. The measured Shopee figures we do publish, including CPC and CPM by hour of day across 333.9 million impressions, are in our open benchmarks dataset [5] under CC BY 4.0.
Frequently asked questions
Two dials, one wire. Divide the target you entered by the ROAS you were shown, and you know which of them is connected.