/**
* Global keyword CPC floor script (Google Ads Scripts)
* Purpose: set all eligible keyword bids below MIN_CPC up to MIN_CPC.
*
* Use steps:
* 1) Keep DRY_RUN=true and run once (preview/log only)
* 2) Check logs
* 3) Set DRY_RUN=false and run SHARD_INDEX=0..7 (8 runs total)
*/
const CONFIG = {
MIN_CPC: 0.12,
DRY_RUN: false,
RUN_ALL_AT_ONCE: true,
ONLY_ENABLED: true,
REQUIRE_IMPRESSIONS: false,
DATE_RANGE: 'LAST_30_DAYS',
// Sharding avoids timeout in large accounts.
SHARD_COUNT: 8,
SHARD_INDEX: 0,
SELECTOR_LIMIT: 200000,
MAX_UPDATES_PER_RUN: 12000,
LOG_SAMPLE_LIMIT: 30,
};
function main() {
let selector = AdsApp.keywords();
if (CONFIG.ONLY_ENABLED) {
selector = selector
.withCondition('CampaignStatus = ENABLED')
.withCondition('AdGroupStatus = ENABLED')
.withCondition('Status = ENABLED');
}
if (CONFIG.REQUIRE_IMPRESSIONS) {
selector = selector
.forDateRange(CONFIG.DATE_RANGE)
.withCondition('Impressions > 0');
}
selector = selector.withLimit(CONFIG.SELECTOR_LIMIT);
const it = selector.get();
const stats = {
scanned: 0,
inShard: 0,
wouldUpdate: 0,
updated: 0,
skippedShard: 0,
skippedNoBid: 0,
skippedAlreadyAtOrAboveFloor: 0,
skippedNonManualStrategy: 0,
errors: 0,
};
const samples = [];
while (it.hasNext()) {
const kw = it.next();
stats.scanned++;
const adGroup = kw.getAdGroup();
const campaign = adGroup.getCampaign();
if (
!CONFIG.RUN_ALL_AT_ONCE &&
(campaign.getId() % CONFIG.SHARD_COUNT) !== CONFIG.SHARD_INDEX
) {
stats.skippedShard++;
continue;
}
stats.inShard++;
const strategy = String(campaign.getBiddingStrategyType ? campaign.getBiddingStrategyType() : '');
if (strategy && strategy !== 'MANUAL_CPC') {
stats.skippedNonManualStrategy++;
continue;
}
const oldCpc = kw.bidding().getCpc();
if (oldCpc === null || oldCpc === undefined || isNaN(oldCpc)) {
stats.skippedNoBid++;
continue;
}
if (oldCpc >= CONFIG.MIN_CPC - 1e-9) {
stats.skippedAlreadyAtOrAboveFloor++;
continue;
}
stats.wouldUpdate++;
if (samples.length < CONFIG.LOG_SAMPLE_LIMIT) {
samples.push(
campaign.getName() + ' | ' +
adGroup.getName() + ' | ' +
kw.getText() + ' | ' +
kw.getMatchType() + ' | ' +
oldCpc + ' -> ' + CONFIG.MIN_CPC
);
}
if (CONFIG.DRY_RUN) continue;
if (stats.updated >= CONFIG.MAX_UPDATES_PER_RUN) {
Logger.log('Reached MAX_UPDATES_PER_RUN, stop this shard run.');
break;
}
try {
kw.bidding().setCpc(CONFIG.MIN_CPC);
stats.updated++;
} catch (e) {
stats.errors++;
}
}
Logger.log('=== CPC floor summary ===');
Logger.log('MIN_CPC=' + CONFIG.MIN_CPC + ', DRY_RUN=' + CONFIG.DRY_RUN);
Logger.log('RUN_ALL_AT_ONCE=' + CONFIG.RUN_ALL_AT_ONCE);
Logger.log('SHARD=' + CONFIG.SHARD_INDEX + '/' + CONFIG.SHARD_COUNT);
Logger.log('SELECTOR_LIMIT=' + CONFIG.SELECTOR_LIMIT);
Logger.log(JSON.stringify(stats));
if (stats.scanned >= CONFIG.SELECTOR_LIMIT) {
Logger.log(
'WARNING: scanned reached selector limit. Increase SELECTOR_LIMIT if account has more keywords.'
);
}
if (samples.length) {
Logger.log('--- sample updates ---');
for (let i = 0; i < samples.length; i++) Logger.log(samples[i]);
}
}
本文档为站内渲染。原始文件本地路径:saas/source/ads/广告-ads-scripts-cpc-d6510d.txt(仅本地保留,不入库不部署)