Request for Script to Adjust tROAS at Campaign/Ad Group/Asset Level

77 views
Skip to first unread message

rambabu mahawar

unread,
Jul 16, 2025, 8:05:55 AMJul 16
to adwords...@googlegroups.com
Hi,

I wanted to check if there's any existing script available that can help automate the adjustment of target ROAS (tROAS) at the campaign, ad group, or asset level based on performance data.

If you have something already built or can suggest an approach, I’d appreciate your guidance.

Looking forward to your response.


--
Best Regards,
Ram Mahawar
Manager - SEM | MARKETING



***************************************************************
** This email contains confidential information some or all of which may be legally privileged. It is for the exclusive use of the intended recipient. If you are not the intended recipient, please delete this message and notify the sender immediately. Unauthorized publication, use, dissemination, forwarding, printing, or copying of this e-mail and its attachments is strictly prohibited. Please consider your environmental responsibility before printing this e-mail. ** 
***************************************************************

Google Ads Scripts Forum Advisor

unread,
Jul 16, 2025, 12:20:16 PMJul 16
to adwords...@googlegroups.com
Hi Ram,

Thank you for reaching out to the Google Ads Scripts Support team.

The setTargetRoas() which is available for campaigns is not available at ad groups and asset levels. You may refer to this document to know supported methods for AdGroupBiddingCampaignBidding and AdsApp.Asset

I recommend following our blog post for any future announcements about this functionality.

Thanks,
 
Google Logo Google Ads Scripts Team

Feedback
How was our support today?

rating1    rating2    rating3    rating4    rating5
[2025-07-16 16:19:43Z GMT] This message is in relation to case "ref:!00D1U01174p.!500Ht01svotZ:ref" (ADR-00319082)



Frengky Ismail

unread,
Jul 16, 2025, 1:09:07 PMJul 16
to Google Ads Scripts Forum
Sayangnya, tidak ada satu skrip tunggal yang secara universal dapat mengotomatiskan penyesuaian tROAS di semua platform periklanan.  Otomatisasi ini sangat bergantung pada platform yang Anda gunakan (misalnya, Google Ads, Facebook Ads, Microsoft Advertising), API yang disediakan platform tersebut, dan kompleksitas aturan penyesuaian yang Anda inginkan.
 
Namun, saya dapat memberikan panduan umum tentang bagaimana Anda bisa mendekati masalah ini, dan contoh kode untuk beberapa skenario umum.  Ingatlah bahwa kode ini memerlukan pengetahuan pemrograman dan akses ke API platform periklanan yang Anda gunakan..
 
Langkah-langkah Umum:
 
1. Pilih Platform dan API: Tentukan platform periklanan yang Anda gunakan dan pelajari API-nya.  Setiap platform memiliki API dan dokumentasinya sendiri.  Anda perlu memahami bagaimana mengakses data kinerja kampanye, grup iklan, dan aset melalui API ini...
2. Kumpulkan Data Kinerja: Gunakan API untuk mengambil data kinerja yang relevan, seperti biaya, konversi, dan ROAS untuk setiap tingkat (kampanye, grup iklan, aset).  Periode waktu data yang Anda kumpulkan akan memengaruhi akurasi penyesuaian tROAS...
3. Buat Algoritma Penyesuaian:  Ini adalah bagian yang paling penting dan paling kompleks. Anda perlu menentukan algoritma yang akan menentukan bagaimana tROAS disesuaikan berdasarkan data kinerja.  Beberapa pendekatan umum meliputi:
- Aturan Berbasis Ambang Batas:  Jika ROAS di bawah ambang batas tertentu, turunkan tROAS. Jika di atas ambang batas, naikkan tROAS...
- Pengoptimalan Berbasis Mesin Belajar: Gunakan algoritma pembelajaran mesin (seperti regresi linier, pohon keputusan, atau jaringan syaraf tiruan) untuk memprediksi ROAS optimal berdasarkan data historis dan menyesuaikan tROAS secara otomatis.  Ini membutuhkan lebih banyak data dan keahlian pemrograman yang lebih canggih...
- Penggunaan Data Eksternal:  Integrasikan data eksternal (misalnya, tren penjualan, data ekonomi) untuk meningkatkan akurasi prediksi ROAS..
4. Implementasi dan Pengujian: Terjemahkan algoritma Anda ke dalam kode dan uji secara menyeluruh sebelum menerapkannya ke kampanye Anda yang sebenarnya.  Mulailah dengan skala kecil dan pantau hasilnya dengan cermat.
 
Contoh Kode (Konseptual - Google Ads):
 
Kode berikut adalah contoh konseptual dan perlu dimodifikasi sesuai dengan API Google Ads dan kebutuhan spesifik Anda.  Ini menunjukkan bagaimana Anda mungkin mengambil data dan melakukan penyesuaian sederhana berbasis ambang batas..
 
python
  
# Ini hanyalah contoh konseptual dan memerlukan library Google Ads API yang sesuai.
# Ganti dengan kode yang sesuai dengan library dan API Anda.

def adjust_troas(campaign_data, target_roas_threshold):
  for campaign in campaign_data:
    if campaign['roas'] < target_roas_threshold:
      # Turunkan tROAS
      new_troas = campaign['troas'] * 0.9 # Contoh: Turunkan 10%
    else:
      # Naikkan tROAS
      new_troas = campaign['troas'] * 1.1 # Contoh: Naikkan 10%
    # Update tROAS di Google Ads API menggunakan new_troas
    update_campaign_troas(campaign['id'], new_troas)

# ... fungsi lain untuk mengambil data dan update tROAS di Google Ads API ...

--
-- You received this message because you are subscribed to the Google Groups AdWords Scripts Forum group. Please do not reply to this email. To post to this group or unsubscribe please visit https://developers.google.com/adwords/scripts/community.
---
You received this message because you are subscribed to the Google Groups "Google Ads Scripts Forum" group.
To unsubscribe from this group and stop receiving emails from it, send an email to adwords-scrip...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/adwords-scripts/CAMT%3DkicLHhmVMzTZro_JFM%3DSsC2kzUh1O89s4L7W9AxS2tKGFg%40mail.gmail.com.

Christof Van Dun

unread,
Jul 22, 2025, 3:52:49 AMJul 22
to Google Ads Scripts Forum
Hello, 

I think there has been a misunderstanding. On 15/07 I sent the following email to : googleadsscr...@google.com
This did not involve a question about a tROAS for app campaigns. 

Thank you for looking into this. 

Regards, 
Christof

---

Christof Van Dun <chri...@amiforyou.com>
Tue 15 Jul, 13:46 (7 days ago)
to googleadsscripts-support

Hello,

Can you please assist us in adjusting the following script, this so we can use it to check whether a dealer (in a campaign name) is also mentioned in the final url and in the adcopy.
You can find this script in Google Ads account 751-069-3948.

Thank you for your help.

---

/**
 * This Google Ads Script checks if a dealer name, extracted from the campaign name,
 * matches the final URL and ad copy of all active ads within active ad groups.
 *
 * The script assumes the dealer name is located in the campaign name after the first underscore.
 * For example, if a campaign is named "Brand_DealerName_CampaignType", it will extract "DealerName".
 *
 * It generates a report in a new Google Sheet, detailing matches and discrepancies.
 */
function main() {
  // --- Configuration ---
  // Name for the Google Sheet where the report will be generated.
  const SPREADSHEET_NAME = 'Google Ads Dealer Name Verification Report';
  // Sheet headers for the report.
  const HEADERS = [
    'Campaign Name',
    'Campaign ID',
    'Dealer Name (from Campaign Name)',
    'Ad Group Name',
    'Ad Group ID',
    'Ad ID',
    'Ad Type',
    'Final URL',
    'Ad Copy (Headlines & Descriptions)',
    'URL Contains Dealer Name?',
    'Ad Copy Contains Dealer Name?',
    'Discrepancy Details'
  ];

  // Get or create the spreadsheet for reporting
  const spreadsheet = getOrCreateSpreadsheet(SPREADSHEET_NAME);
  const sheet = spreadsheet.getActiveSheet();

  // Clear previous content and set headers
  sheet.clearContents();
  sheet.appendRow(HEADERS);
  sheet.setFrozenRows(1); // Freeze the header row
  sheet.getRange(1, 1, 1, HEADERS.length).setBackground('#cfe2f3').setFontWeight('bold'); // Style headers

  Logger.log(`Starting Google Ads Dealer Name Verification Script.`);
  Logger.log(`Report will be generated in: ${spreadsheet.getUrl()}`);

  let campaignsIterator;
  try {
    // Get all active campaigns
    campaignsIterator = AdsApp.campaigns()
      .withCondition('Status = ENABLED')
      .get();
  } catch (e) {
    Logger.log(`Error fetching campaigns: ${e.message}`);
    sheet.appendRow(['Error fetching campaigns:', e.message]);
    return;
  }

  if (!campaignsIterator.hasNext()) {
    Logger.log('No active campaigns found.');
    sheet.appendRow(['No active campaigns found.']);
    return;
  }

  let totalDiscrepancies = 0;

  while (campaignsIterator.hasNext()) {
    const campaign = campaignsIterator.next();
    const campaignName = campaign.getName();
    const campaignId = campaign.getId();

    // Extract dealer name from campaign name (text after the first underscore)
    const dealerNameMatch = campaignName.match(/_(.+?)_/); // Gets text between first and second underscore
    let dealerName = '';
    if (dealerNameMatch && dealerNameMatch[1]) {
      dealerName = dealerNameMatch[1].trim().toLowerCase();
    } else {
      Logger.log(`Skipping campaign '${campaignName}' (ID: ${campaignId}) - Could not extract dealer name from campaign name.`);
      sheet.appendRow([
        campaignName,
        campaignId,
        'N/A (No underscore found for dealer name)',
        '', '', '', '', '', '',
        'N/A', 'N/A', 'Could not extract dealer name from campaign name. Expected format: Brand_DealerName_CampaignType'
      ]);
      continue; // Skip to the next campaign
    }

    Logger.log(`Processing campaign: '${campaignName}' (ID: ${campaignId}) - Extracted Dealer Name: '${dealerName}'`);

    let adGroupsIterator;
    try {
      // Get all active ad groups within the current campaign
      adGroupsIterator = campaign.adGroups()
        .withCondition('Status = ENABLED')
        .get();
    } catch (e) {
      Logger.log(`Error fetching ad groups for campaign '${campaignName}': ${e.message}`);
      sheet.appendRow([
        campaignName, campaignId, dealerName,
        'Error fetching ad groups', '', '', '', '', '',
        'N/A', 'N/A', `Error: ${e.message}`
      ]);
      continue;
    }

    if (!adGroupsIterator.hasNext()) {
      Logger.log(`No active ad groups found for campaign '${campaignName}'.`);
      sheet.appendRow([
        campaignName, campaignId, dealerName,
        'No active ad groups', '', '', '', '', '',
        'N/A', 'N/A', 'No active ad groups found'
      ]);
      continue;
    }

    while (adGroupsIterator.hasNext()) {
      const adGroup = adGroupsIterator.next();
      const adGroupName = adGroup.getName();
      const adGroupId = adGroup.getId();

      let adsIterator;
      try {
        // Get all active ads within the current ad group
        adsIterator = adGroup.ads()
          .withCondition('Status = ENABLED')
          .get();
      } catch (e) {
        Logger.log(`Error fetching ads for ad group '${adGroupName}' in campaign '${campaignName}': ${e.message}`);
        sheet.appendRow([
          campaignName, campaignId, dealerName,
          adGroupName, adGroupId,
          'Error fetching ads', '', '', '',
          'N/A', 'N/A', `Error: ${e.message}`
        ]);
        continue;
      }

      if (!adsIterator.hasNext()) {
        Logger.log(`No active ads found for ad group '${adGroupName}' in campaign '${campaignName}'.`);
        sheet.appendRow([
          campaignName, campaignId, dealerName,
          adGroupName, adGroupId,
          'No active ads', '', '', '',
          'N/A', 'N/A', 'No active ads found'
        ]);
        continue;
      }

      while (adsIterator.hasNext()) {
        const ad = adsIterator.next();
        const adId = ad.getId();
        const adType = ad.getType();
        let finalUrl = '';
        let adCopy = '';
        let urlMatches = false;
        let adCopyMatches = false;
        let discrepancyDetails = '';

        try {
          finalUrl = ad.urls().getFinalUrl() || '';
          // Check if dealer name is in the URL (case-insensitive)
          urlMatches = finalUrl.toLowerCase().includes(dealerName);

          // Get ad copy based on ad type
          if (adType === 'EXPANDED_TEXT_AD') {
            const eta = ad.asExpandedTextAd();
            adCopy += (eta.getHeadlinePart1() || '') + ' ';
            adCopy += (eta.getHeadlinePart2() || '') + ' ';
            adCopy += (eta.getHeadlinePart3() || '') + ' ';
            adCopy += (eta.getDescription1() || '') + ' ';
            adCopy += (eta.getDescription2() || '');
          } else if (adType === 'RESPONSIVE_SEARCH_AD') {
            const rsa = ad.asResponsiveSearchAd();
            rsa.getHeadlines().forEach(h => adCopy += h.getText() + ' ');
            rsa.getDescriptions().forEach(d => adCopy += d.getText() + ' ');
          } else if (adType === 'CALL_ONLY_AD') {
            const coa = ad.asCallOnlyAd();
            adCopy += (coa.getDescription1() || '') + ' ';
            adCopy += (coa.getDescription2() || '');
          }
          // Add other ad types as needed (e.g., HTML5_AD, IMAGE_AD, DYNAMIC_SEARCH_AD, etc.)
          // For ad types like IMAGE_AD or HTML5_AD, checking ad copy might not be relevant.
          // You might want to skip ad copy check for these or add specific logic.
          else {
            Logger.log(`Unsupported ad type '${adType}' for ad ID ${adId}. Skipping ad copy check.`);
            adCopy = `(Ad Type: ${adType} - Ad copy check skipped)`;
            adCopyMatches = true; // Assume match if not checking
          }

          // Check if dealer name is in the ad copy (case-insensitive)
          adCopyMatches = adCopy.toLowerCase().includes(dealerName);

          // Determine discrepancies
          if (!urlMatches || !adCopyMatches) {
            totalDiscrepancies++;
            if (!urlMatches) {
              discrepancyDetails += 'Dealer name NOT found in Final URL. ';
            }
            if (!adCopyMatches) {
              discrepancyDetails += 'Dealer name NOT found in Ad Copy. ';
            }
            Logger.log(`Discrepancy found for Ad ID ${adId} in Ad Group '${adGroupName}' (Campaign: '${campaignName}'). Details: ${discrepancyDetails}`);
          }

        } catch (e) {
          Logger.log(`Error processing ad ID ${adId} in ad group '${adGroupName}': ${e.message}`);
          discrepancyDetails = `Error processing ad: ${e.message}`;
          urlMatches = 'ERROR';
          adCopyMatches = 'ERROR';
        }

        // Append row to the spreadsheet
        sheet.appendRow([
          campaignName,
          campaignId,
          dealerName,
          adGroupName,
          adGroupId,
          adId,
          adType,
          finalUrl,
          adCopy.trim(),
          urlMatches ? 'Yes' : 'No',
          adCopyMatches ? 'Yes' : 'No',
          discrepancyDetails.trim()
        ]);
      }
    }
  }

  Logger.log(`Script finished. Total discrepancies found: ${totalDiscrepancies}`);
  Logger.log(`Report available at: ${spreadsheet.getUrl()}`);
}

/**
 * Gets an existing Google Sheet by name or creates a new one if it doesn't exist.
 * @param {string} spreadsheetName The name of the spreadsheet.
 * @returns {GoogleAppsScript.Spreadsheet.Spreadsheet} The spreadsheet object.
 */
function getOrCreateSpreadsheet(spreadsheetName) {
  const files = DriveApp.getFilesByName(spreadsheetName);
  if (files.hasNext()) {
    const file = files.next();
    return SpreadsheetApp.open(file);
  } else {
    // Create a new spreadsheet in the root folder
    const newSpreadsheet = SpreadsheetApp.create(spreadsheetName);
    Logger.log(`Created new spreadsheet: ${newSpreadsheet.getUrl()}`);
    return newSpreadsheet;
  }
}

--
Met vriendelijke groeten | Cordialement | Best regards
Christof van Dun
Online Marketeer
 amiforyou

T | +32 3 349 18 10
De Gerlachekaai 20, 2000 Antwerpen
amiforyou.com
amiforyou LinkedIn Facebook Instagram


--
-- You received this message because you are subscribed to the Google Groups AdWords Scripts Forum group. Please do not reply to this email. To post to this group or unsubscribe please visit https://developers.google.com/adwords/scripts/community.
---
You received this message because you are subscribed to the Google Groups "Google Ads Scripts Forum" group.
To unsubscribe from this group and stop receiving emails from it, send an email to adwords-scrip...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/adwords-scripts/CAMT%3DkicLHhmVMzTZro_JFM%3DSsC2kzUh1O89s4L7W9AxS2tKGFg%40mail.gmail.com.


--
Met vriendelijke groeten | Cordialement | Best regards
Christof van Dun
Online Marketeer

Google Ads Scripts Forum Advisor

unread,
Jul 22, 2025, 4:56:43 AMJul 22
to adwords...@googlegroups.com

Hi Christof,

Please be informed that the information provided on that email is not related to the tROAS for app campaigns. However, I am re-sending you the information in this thread.

I would like to inform you that our support is focused on guiding users to troubleshoot specific issues within their own scripts, rather than providing hands-on implementation or creating scripts on a user's behalf.

I could see that you are getting the error "ad.asResponsiveSearchAd is not a function". I would like to inform you that there is no method called asResponsiveSearchAd in the Google Ads Scripts. I would suggest you refer to this document to know supported methods for AdsApp.Ad. Also, you may check AdsApp.​ResponsiveSearchAd to know available methods for response search ads.

I hope this helps! Feel free to get back to us for any further concerns.

Thanks,
 
Google Logo Google Ads Scripts Team

Feedback
How was our support today?

rating1    rating2    rating3    rating4    rating5

[2025-07-22 08:56:04Z GMT] This message is in relation to case "ref:!00D1U01174p.!500Ht01svotZ:ref" (ADR-00319082)



Reply all
Reply to author
Forward
0 new messages