Op 17 augustus 2026 verandert Google hoe target-gebaseerd bieden werkt voor campagnes die door budget beperkt worden. Zit je Target CPA of Target ROAS ver van wat je campagnes werkelijk leveren, dan gaat dat verschil je geld kosten. In deze blog lees je wat er verandert, wie het raakt, en krijg je een gratis Google Ads script dat elke risicocampagne in je account vindt.
Wat er verandert op 17 augustus 2026
Nu presteert een campagne die door budget beperkt wordt vaak stilletjes beter dan haar target. Je stelt een Target CPA van 10 euro in, en omdat het budget op is voordat de dag om is, haalt Google conversies binnen voor 5 euro. Je krijgt meer en goedkopere conversies dan je target vroeg.
Vanaf 17 augustus laat Google die budgetbeperkte campagnes leveren richting het target dat je hebt ingesteld. Dat target van 10 euro dat op 5 euro draaide, kruipt omhoog richting 10 euro. Zelfde budget, tot twee keer zo dure conversies, en dus minder conversies voor je geld. Google's eigen helppagina over de wijziging bevestigt hoe dit werkt.
Bij Target ROAS werkt het precies andersom. Een campagne met een target van 300% die eigenlijk 600% levert, zakt richting die 300%.
Je target was een zacht plafond waar Google onder ging zitten als het kon. Vanaf de 17e is het het getal waar Google naartoe werkt.
Wie dit echt raakt
Twee dingen moeten allebei waar zijn voordat een campagne geraakt wordt:
- De campagne gebruikt een target-gebaseerde strategie: Target CPA of Target ROAS (inclusief Conversies maximaliseren of Conversiewaarde maximaliseren met een ingesteld target).
- De campagne is "Beperkt door budget".
Is een campagne niet budgetbeperkt, dan verandert er niets. De eerste klus is dus: vind de campagnes die allebei target-gebaseerd en budgetbeperkt zijn, en check hoe ver het target van de werkelijke prestaties is weggedreven.
"Beperkt door budget" zie je in de campagnestatus in de Google Ads interface. Maar elke campagne met de hand nalopen, over een heel account of een MCC, is traag. Daar is het script hieronder voor.
Waarom een ruim target ineens geld kost
Neem een Search-campagne, beperkt door budget, met een Target CPA van 10 euro, die werkelijk converteert op 5 euro.
Voor 17 augustus: de campagne geeft haar budget uit aan conversies van zo'n 5 euro. Bij 500 euro budget per dag zijn dat ruwweg 100 conversies.
Na 17 augustus: de campagne mag leveren richting 10 euro. Diezelfde 500 euro per dag koopt nu conversies tot 10 euro per stuk, dus je komt dichter bij 50 uit voor hetzelfde geld.
Er veranderde niets aan je product, je funnel of je concurrentie. Alleen het target dat je te ruim had laten staan.
De fix: breng je target naar je werkelijke cijfers, voor de 17e
De fix is simpel gezegd: zet elk target in lijn met je recente werkelijke prestaties, zodat er geen ruimte overblijft die Google kan opmaken.
De zorg zit in hoe je het target verplaatst.
Elke targetwijziging start de leerfase van Smart Bidding opnieuw. Verander je een target in één keer fors, dan kan de prestatie ontregeld raken terwijl het systeem opnieuw leert. Verander je het target elke paar dagen, dan komt het nooit tot rust, want elke wijziging reset het venster weer.
Dus:
- Is het verschil klein, ruwweg 15 tot 20% of minder, doe dan één aanpassing naar je recente werkelijke cijfers en houd die vast.
- Is het verschil groot, verklein het dan in stappen van maximaal 20% per keer, en geef elke stap een volledige conversiecyclus om te settelen voor de volgende. Voor de meeste accounts is dat ongeveer een week. Voor accounts met laag volume duurt het langer: wacht tot de cijfers stabiel zijn, niet alleen tot er zeven dagen voorbij zijn.
Begin je zo'n drie weken voor 17 augustus, dan heb je ruimte voor twee of drie schone stappen. Genoeg om de meeste verschillen te dichten zonder iets te schokken.
Nog één ding. Google heeft sinds 6 juli ook een Bid Target Adjustment Tool in de interface, die per campagne targetwijzigingen voorstelt. Handig. Het script hieronder is de versie op schaal: het controleert al je campagnes in één keer en vertelt welke verschillen als eerste aandacht verdienen.
Het gratis script: vind elk weggedreven target in je account
Dit script scant een account, vergelijkt het target van elke target-gebaseerde campagne met de werkelijke CPA of ROAS van de laatste 30 dagen, en markeert de budgetbeperkte campagnes waar het target te ruim staat. Het past niets aan. Het rapporteert alleen, naar je logs en, als je wilt, naar een Google Sheet of een e-mail.
Zo draai je het:
- Ga in Google Ads naar Tools, dan Bulkacties, dan Scripts, dan de plusknop voor een nieuw script.
- Plak het script erin.
- Optioneel: zet een Google Sheet URL en een e-mailadres in het CONFIG-blok bovenaan.
- Autoriseer, klik op Preview, en lees de logs.
/**
* Coby Agency - Target CPA / Target ROAS drift audit
* -------------------------------------------------------------
* From 17 August 2026 Google steers budget-limited campaigns that use a
* target-based strategy (Target CPA / Target ROAS) toward the target you
* actually set, instead of quietly beating it. A target that sits looser
* than your real performance will cost you: CPA drifts up toward the target,
* ROAS drifts down toward it, for the same budget.
*
* This script scans an account, compares each target to the last 30 days of
* actual performance, and flags the campaigns to tighten before the 17th.
* It changes nothing in the account. It only reports.
*
* How to use:
* 1. Google Ads > Tools > Bulk actions > Scripts > + New script.
* 2. Paste this in, Authorise, Preview. Read the Logs.
* 3. Optional: paste a Google Sheet URL and / or an email below to get the
* report out of the logs.
*
* MCC: run it per account, or wrap main() in an AdsManagerApp account
* iterator if you manage several.
* -------------------------------------------------------------
*/
var CONFIG = {
DATE_RANGE: 'LAST_30_DAYS', // window for "actual" CPA / ROAS
MIN_CONVERSIONS: 15, // skip thin-data campaigns (unreliable actuals)
BUDGET_LOST_IS_THRESHOLD: 0.10, // > 10% search budget lost IS = budget-limited
DRIFT_FLAG_PCT: 0.15, // flag when the target is > 15% looser than actual
MAX_STEP_PCT: 0.20, // never suggest moving more than 20% per step
SPREADSHEET_URL: '', // optional: a Google Sheet URL to write results to
EMAIL: '' // optional: address to email the summary to
};
function main() {
var query =
'SELECT campaign.id, campaign.name, campaign.advertising_channel_type, ' +
'campaign.bidding_strategy_type, ' +
'campaign.target_cpa.target_cpa_micros, ' +
'campaign.maximize_conversions.target_cpa_micros, ' +
'campaign.target_roas.target_roas, ' +
'campaign.maximize_conversion_value.target_roas, ' +
'metrics.cost_micros, metrics.conversions, metrics.conversions_value, ' +
'metrics.search_budget_lost_impression_share ' +
'FROM campaign ' +
"WHERE campaign.status = 'ENABLED' " +
'AND segments.date DURING ' + CONFIG.DATE_RANGE;
var rows = AdsApp.search(query);
var results = [];
while (rows.hasNext()) {
var r = rows.next();
var c = r.campaign || {};
var m = r.metrics || {};
// Find the target (campaign-level strategies only; see note on portfolio below)
var type = null, target = null;
if (c.targetCpa && c.targetCpa.targetCpaMicros) {
type = 'CPA'; target = micros(c.targetCpa.targetCpaMicros);
} else if (c.maximizeConversions && c.maximizeConversions.targetCpaMicros) {
type = 'CPA'; target = micros(c.maximizeConversions.targetCpaMicros);
} else if (c.targetRoas && c.targetRoas.targetRoas) {
type = 'ROAS'; target = Number(c.targetRoas.targetRoas);
} else if (c.maximizeConversionValue && c.maximizeConversionValue.targetRoas) {
type = 'ROAS'; target = Number(c.maximizeConversionValue.targetRoas);
}
if (!type || !target) continue; // not a target-based campaign, or no target set
var conv = Number(m.conversions || 0);
if (conv < CONFIG.MIN_CONVERSIONS) continue;
var cost = micros(m.costMicros);
var value = Number(m.conversionsValue || 0);
var budgetLostIS = Number(m.searchBudgetLostImpressionShare || 0);
var channel = c.advertisingChannelType || '';
var actual, driftPct, looser, suggestion;
if (type === 'CPA') {
actual = cost / conv;
looser = target > actual; // Google may pay up to the target
driftPct = actual > 0 ? (target - actual) / actual : 0;
suggestion = Math.max(actual, target * (1 - CONFIG.MAX_STEP_PCT));
} else {
actual = cost > 0 ? value / cost : 0;
looser = target < actual; // Google may accept down to the target
driftPct = target > 0 ? (actual - target) / target : 0;
suggestion = Math.min(actual, target * (1 + CONFIG.MAX_STEP_PCT));
}
// Budget-limited detection is reliable for Search/Shopping only
var budgetLimited;
if (budgetLostIS > CONFIG.BUDGET_LOST_IS_THRESHOLD) budgetLimited = 'Yes';
else if (channel === 'SEARCH' || channel === 'SHOPPING') budgetLimited = 'No';
else budgetLimited = 'Unknown';
var flagged = looser && Math.abs(driftPct) >= CONFIG.DRIFT_FLAG_PCT;
var action;
if (!flagged) action = 'OK, target close to actual';
else if (budgetLimited === 'Yes') action = 'TIGHTEN before 17 Aug';
else if (budgetLimited === 'Unknown') action = 'Check if budget-limited, then tighten';
else action = 'Watch (not budget-limited today)';
results.push({
name: c.name, channel: channel, type: type, target: target, actual: actual,
driftPct: driftPct, budgetLimited: budgetLimited, budgetLostIS: budgetLostIS,
suggestion: suggestion, conv: conv, flagged: flagged, action: action
});
}
// Most urgent first: budget-limited + flagged, then by drift size
var rank = { 'TIGHTEN before 17 Aug': 3, 'Check if budget-limited, then tighten': 2, 'Watch (not budget-limited today)': 1, 'OK, target close to actual': 0 };
results.sort(function (a, b) {
return (rank[b.action] - rank[a.action]) || (Math.abs(b.driftPct) - Math.abs(a.driftPct));
});
report(results);
if (CONFIG.SPREADSHEET_URL) writeSheet(results);
if (CONFIG.EMAIL) emailSummary(results);
}
function micros(v) { return Number(v || 0) / 1000000; }
function pct(x) { return (x >= 0 ? '+' : '') + Math.round(x * 100) + '%'; }
function money(x) { return x.toFixed(2); }
function roas(x) { return x.toFixed(2) + 'x'; }
function report(results) {
var tighten = results.filter(function (r) { return r.action.indexOf('TIGHTEN') === 0; });
Logger.log('Target drift audit, ' + CONFIG.DATE_RANGE + ', ' + results.length +
' target-based campaigns with enough data.');
Logger.log(tighten.length + ' are budget-limited with a loose target. Fix these before 17 August.\n');
Logger.log(['CAMPAIGN', 'CHANNEL', 'TYPE', 'TARGET', 'ACTUAL', 'DRIFT', 'BUDGET-LTD', 'NEXT STEP (<=20%)', 'ACTION'].join(' | '));
results.forEach(function (r) {
var t = r.type === 'CPA' ? money(r.target) : roas(r.target);
var a = r.type === 'CPA' ? money(r.actual) : roas(r.actual);
var s = r.type === 'CPA' ? money(r.suggestion) : roas(r.suggestion);
Logger.log([trim(r.name, 34), r.channel, r.type, t, a, pct(r.driftPct), r.budgetLimited, s, r.action].join(' | '));
});
Logger.log('\nNote: portfolio (shared) target strategies are not covered here, their target lives on the strategy, not the campaign. Check those in Tools > Shared library > Bid strategies.');
}
function trim(s, n) { s = String(s || ''); return s.length > n ? s.substring(0, n - 1) + '.' : s; }
function writeSheet(results) {
var ss = SpreadsheetApp.openByUrl(CONFIG.SPREADSHEET_URL);
var sh = ss.getActiveSheet();
sh.clear();
sh.appendRow(['Campaign', 'Channel', 'Type', 'Target', 'Actual', 'Drift', 'Budget-limited', 'Search budget lost IS', 'Next step (<=20%)', 'Conversions', 'Action']);
results.forEach(function (r) {
sh.appendRow([r.name, r.channel, r.type,
r.type === 'CPA' ? r.target : r.target,
r.type === 'CPA' ? r.actual : r.actual,
Math.round(r.driftPct * 100) / 100,
r.budgetLimited, Math.round(r.budgetLostIS * 100) / 100,
r.suggestion, r.conv, r.action]);
});
Logger.log('Written to sheet: ' + CONFIG.SPREADSHEET_URL);
}
function emailSummary(results) {
var tighten = results.filter(function (r) { return r.action.indexOf('TIGHTEN') === 0; });
var body = 'Target drift audit (' + CONFIG.DATE_RANGE + ')\n' +
tighten.length + ' budget-limited campaigns with a loose target to fix before 17 August:\n\n';
tighten.forEach(function (r) {
var t = r.type === 'CPA' ? money(r.target) : roas(r.target);
var a = r.type === 'CPA' ? money(r.actual) : roas(r.actual);
body += '- ' + r.name + ' (' + r.type + '): target ' + t + ', actual ' + a + ', drift ' + pct(r.driftPct) + '\n';
});
MailApp.sendEmail(CONFIG.EMAIL, 'Google Ads target drift audit', body);
Logger.log('Emailed summary to ' + CONFIG.EMAIL);
}
Zo lees je de output
Het rapport toont elke target-gebaseerde campagne met genoeg data, de meest urgente eerst. De kolommen die ertoe doen:
Drift is hoe ver je target van je werkelijke cijfers zit. Een Target CPA van 10 euro tegen een werkelijke CPA van 5 euro toont +100%.
Budget-limited is Yes, No of Unknown. De wijziging raakt alleen budgetbeperkte campagnes, dus de Yes-rijen zijn je prioriteit. Unknown zie je bij Performance Max en Demand Gen, waar scripts de budgetstatus niet kunnen uitlezen. Check die dus even in de interface.
Action vertelt je wat te doen. "TIGHTEN before 17 Aug" betekent budgetbeperkt met een ruim target: die pak je als eerste. "Next step" stelt een target voor dat maximaal 20% dichter bij je werkelijke cijfers ligt, als veilige eerste stap.
Twee beperkingen om te kennen. Het script leest targets op campagneniveau, dus portfolio-biedstrategieën (gedeelde strategieën) check je apart onder Gedeelde bibliotheek. En campagnes met minder dan 15 conversies in het venster slaat het over, omdat die cijfers te veel ruis bevatten om op te sturen.
Voor de 17e, in het kort
Vind je target-gebaseerde campagnes met de status Beperkt door budget. Meet het verschil met je werkelijke cijfers van de laatste 30 dagen. Staat het target ruim, breng het dan richting je echte cijfers, in stappen van 20% of minder, met een conversiecyclus ertussen. Daarna vasthouden, en de laatste week van augustus goed kijken.
Doe je dat, dan is 17 augustus voor jouw account een non-event in plaats van een stille prijsverhoging.
Wil je een tweede paar ogen?
Wil je liever dat iemand dit over je account draait en je precies vertelt welke targets je moet verplaatsen en hoeveel? Vraag een gratis audit aan, dan kijken we mee. Meer over hoe we betaalde accounts draaien lees je op de SEA-dienstenpagina.
Geschreven door Marloes Slotboom, oprichter van Coby Agency. Laatst bijgewerkt: juli 2026.