知识库首页 广告 cpc.txt

cpc

本地来源:广告/ads-scripts/cpc.txt

/**
 * 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(仅本地保留,不入库不部署)