RSA Booster v1.5 (Single Account)

Supercharge your Google Ads Search Campaigns performance by automatically monitoring RSA ads headlines and description.

Hey, before I leave you to your script I have an announcement to make: you can now get me (and soon my team) to create custom automations for you!

You can book us for a one-off project, or on a monthly basis. We offer:

  • Up to Unlimited requests

  • Unlimited revisions

  • Free technical project analysis 

  • Tailored recommendations

  • High priority support

  • Full access to the source code

  • Whitelabeling

  • 30 days money back guarantee

This is perfect for you if your business relies heavily on advertising for revenue, but lacks technical capabilities to automate tasks to increase productivity. Ideal for mid to large Ecommerce, startups that raised funding, agencies, or corporates with small marketing teams.

Platforms we automate: Google Ads, Meta Ads, Linkedin Ads, Native, & more.

The tools we use: Custom scripts, Zapier, Make, n8n, Supermetrics, Funnel, & more.

We’re still working on the website, so if you’re interested in learning more submit a request here. Describe your needs, attach your company website, and let’s see if we’re a fit.

Looking forward to taking your ads game to the next level! 🚀 

RSA Booster v1.5 - Why Use This Script

In our data-dominated world creatives often get put at second place. Optimizing responsive search ads (RSA) headlines and descriptions is crucial for Search campaigns performance.

To ensure that your RSA assets are performing optimally across all campaigns you can analyze the Assets > Table view: Assets report, but this shows headlines and descriptions performance aggregated across all Search campaigns.

What the Script Does

The RSA Booster v1.5 script allows you to get granular data, at the ad-level. After generating the report, and it sends an email if Low performing assets are found.

On top of it, the Google Sheet template provided allows you to automatically generate Low-performing headlines and description alternatives directly in the sheet - if you provide your own OpenAI API Key.

Here’s what’s provided:

  • A report of RSA assets with performance label

  • Detailed information for each asset, including:
    - Campaign and Ad Group names
    - Ad Label for unique identification
    - Asset Type (Headline or Description)
    - Clicks, Impressions, and CTR
    - Asset text and performance label
    - Date range for the performance data

  • An alert system that emails designated recipients when low-performing assets are detected, including a summary of key details and a direct link to the report for immediate review.

  • A second script inside the Google Sheet to create low-performing assets alternatives directly inside the sheet.

How It Works

  1. Connects to your Google Ads account to gather asset performance data across all campaigns and ad groups.

  2. Retrieves performance data based on user-defined criteria, such as minimum clicks and a selected look-back window.

  3. Analyzes asset performance and labels assets as “LOW,” “LEARNING,” “GOOD,” or “BEST.”

  4. Generates ad labels (e.g., “Ad 1,” “Ad 2”) within ad groups to uniquely identify ads (as most of the time not all ads are labeled, making it had to identify where each headline belongs).

  5. Exports results to a specified Google Sheets URL.

  6. Sends email alerts when low-performing assets are identified, summarizing key information and including a direct link to the report. (If no low-performing assets are found, the report remains updated but no email is sent.)

  7. Generates 3 alternative assets directly in your language in use, if you manually trigger a second script, placed inside the sheet, from the custom menu RSA Booster > Generate alternative assets

How to Use It

  1. Create a copy of this Google Sheet template and copy its URL. If you want to generate assets aternatives directly in the sheet:

    • Click on Extensions > Apps Script

    • On the left, click on Project settings

    • Scroll down, and under Script Properties click Add script property

    • Under Property type “API_KEY”

    • Under Value enter your OpenAI API key

  2. Copy-Paste the script below into a new Google Ads Script in your account.

  3. Set the following parameters in the script:

    • NUMBER_OF_CLICKS: Define the minimum click threshold for evaluation (default: 100).

    • LOOKBACK_WINDOW: Specify the performance data timeframe (e.g., ‘LAST_30_DAYS’ or ‘LAST_7_DAYS’).

    • SPREADSHEET_URL: Paste the URL of your Google Sheet template copy.

    • EMAIL_ADDRESSES: List the email recipients for alerts.

  4. Run the script or schedule it to run automatically at your desired intervals. My recommendation would be to run it weekly.

  5. Generate alternative assets directly in the sheet by clicking RSA Booster > Generate alternative assets in the custom menu at the top

Reviewing the Results

After running the script, navigate to your Google Sheets report and review the output.

  • Focus on assets marked as “LOW” in the “Performance Label” column.

  • Review campaign and ad group context to understand asset placement.

  • Analyze CTR and other metrics to identify potential issues.

  • Consider checking for trends or patterns across multiple reports over time to identify consistent performance issues or improvements.

Based on your findings, consider:

  • Updating low-performing headlines or descriptions with more engaging content.

  • Adjusting targeting or bidding strategies for ad groups with consistent low performance.

  • Testing new variations of RSAs to boost overall ad quality.

Better headlines or descriptions can significantly improve ad performance by enhancing relevance and engagement, leading to higher click-through rates and potentially lower CPCs.

But remember, while this script provides valuable insights, always use your expertise and knowledge of your business goals when making final decisions about your Google Ads strategy.

Credits

P.S. I wanted to give credit to Slava Wagner, who inpired me to create this script with one of his super interistng Make automations. Check out his Linkedin profile here.

Customize this script

Want to an MCC version? Maybe customize the prompt, or specific filters? We got you covered!

Submit a request here. Describe your needs, attach your company website, and let’s see if we’re a fit.

The code

/**
 * RSA BOOSTER v1.5
 * Created by Francesco Cifardi
 * For issues or questions reach out on Linkedin (https://www.linkedin.com/in/francescocifardi/)
 * Free to use and share, a mention would be appreciated :-)
 * 
 * This script monitors and evaluates asset performance across Google Ads campaigns,
 * identifying low-performing assets and generating comprehensive reports. It exports
 * data to a Google Sheet and sends email alerts when low-performing assets are detected.
 * 
 * Key Features:
 * - Tracks performance metrics (clicks, impressions, CTR)
 * - Identifies low-performing assets
 * - Generates unique ad labels within ad groups
 * - Creates detailed performance reports
 * - Sends email alerts for low-performing assets
 * 
 * Note: The provided Google Sheet template includes an additional Apps Script that can
 * generate alternative suggestions for low-performing assets using OpenAI's API
 * (requires API key configuration).
 * 
 * For complete setup instructions and documentation, visit:
 * https://medialauncher.beehiiv.com/p/rsa-booster
 */

// User-modifiable criteria:
var NUMBER_OF_CLICKS = 100;  // Minimum number of clicks to qualify for evaluation
var LOOKBACK_WINDOW = 'LAST_30_DAYS'; // Look-back window for performance data
// Spreadsheet template: https://docs.google.com/spreadsheets/d/1ydQFFkGojmcxjvp3PkXaT-CpypawHpeil2xSD5qaB2c/copy
var SPREADSHEET_URL = 'YOUR_URL'; // Add the URL of your Google Sheets template copy h
var EMAIL_ADDRESSES = [
  '[email protected]'
  // Add more email addresses as needed, between signle quotes, and comma separated
];

function main() {
  // Get date range for reporting
  const dates = getLookbackDates(LOOKBACK_WINDOW);
  
  // Initialize spreadsheet
  const ss = SpreadsheetApp.openByUrl(SPREADSHEET_URL);
  const sheet = ss.getActiveSheet();
  
  // Define headers first
  const headers = [
    'Campaign', 'Ad Group', 'Ad Label', 'Clicks', 'Impressions', 'CTR', 
    'Asset Type', 'Asset Text', 'Performance Label',
    'Report Start Date', 'Report End Date'
  ];
  
  // Clear existing data while preserving formatting
  const lastRow = Math.max(sheet.getLastRow(), 1);
  const lastCol = Math.max(sheet.getLastColumn(), headers.length);
  if (lastRow > 1) { // Only clear if there's data (preserve headers)
    sheet.getRange(2, 1, lastRow - 1, lastCol).clearContent();
  }
  
  // Set headers
  sheet.getRange(1, 1, 1, headers.length).setValues([headers]);
  
  // Rest of query remains the same
  const query = `
    SELECT
      ad_group_ad.ad.id,
      ad_group_ad.ad.responsive_search_ad.headlines,
      ad_group_ad.ad.responsive_search_ad.descriptions,
      campaign.name,
      ad_group.name,
      metrics.clicks,
      metrics.impressions
    FROM ad_group_ad
    WHERE 
      campaign.status = "ENABLED"
      AND metrics.clicks > ${NUMBER_OF_CLICKS}
      AND segments.date DURING ${LOOKBACK_WINDOW}
      LIMIT 1000
  `;
  
  const response = AdsApp.search(query);
  let rowData = [];
  let lowPerformingAssets = [];
  let adGroupCounter = {}; // Track ad counts per ad group
  
  while (response.hasNext()) {
    const row = response.next();
    const jsonString = JSON.stringify(row);
    const data = JSON.parse(jsonString);
    
    // Generate ad label
    const adGroupKey = `${row.campaign.name}_${row.adGroup.name}`;
    adGroupCounter[adGroupKey] = (adGroupCounter[adGroupKey] || 0) + 1;
    const adLabel = `Ad ${adGroupCounter[adGroupKey]}`;
    
    // Process headlines and descriptions
    const assets = processAssets(data);
    
    // Create row for each asset
    assets.forEach(asset => {
      if (asset.performanceLabel === 'LOW') {
        lowPerformingAssets.push({
          campaign: row.campaign.name,
          adGroup: row.adGroup.name,
          adLabel: adLabel,
          type: asset.type,
          text: asset.text
        });
      }
      
      rowData.push([
        row.campaign.name,
        row.adGroup.name,
        adLabel,  // Add ad label to the row
        row.metrics.clicks,
        row.metrics.impressions,
        (row.metrics.clicks/row.metrics.impressions * 100).toFixed(2) + '%',
        asset.type,
        asset.text,
        asset.performanceLabel,
        dates.startDate,
        dates.endDate
      ]);
    });
  }
  
  // Write all data at once
  if (rowData.length > 0) {
    sheet.getRange(2, 1, rowData.length, headers.length).setValues(rowData);
  }
  
  // Send email if there are low performing assets
  if (lowPerformingAssets.length > 0) {
    sendLowPerformanceAlert(lowPerformingAssets, SPREADSHEET_URL);
  }
}

// New function to process assets and return structured data
function processAssets(data) {
  const assets = [];
  
  function findAssets(obj, context) {
    if (typeof obj === 'object') {
      let currentAsset = {};
      
      for (const key in obj) {
        if (Array.isArray(obj[key])) {
          for (const item of obj[key]) {
            findAssets(item, key);
          }
        } else if (typeof obj[key] === 'object') {
          findAssets(obj[key], key);
        } else if (key === 'text') {
          currentAsset.type = context === 'headlines' ? 'Headline' : 'Description';
          currentAsset.text = obj[key];
        } else if (key === 'assetPerformanceLabel') {
          currentAsset.performanceLabel = obj[key];
          assets.push({...currentAsset});
          currentAsset = {};
        }
      }
    }
  }
  
  findAssets(data, '');
  return assets;
}

// New function to calculate date range based on lookback window
function getLookbackDates(lookbackWindow) {
  const endDate = new Date();
  let startDate = new Date();
  
  switch(lookbackWindow) {
    case 'LAST_30_DAYS':
      startDate.setDate(endDate.getDate() - 30);
      break;
    case 'LAST_7_DAYS':
      startDate.setDate(endDate.getDate() - 7);
      break;
    // Add more cases as needed
  }
  
  return {
    startDate: startDate.toISOString().split('T')[0],
    endDate: endDate.toISOString().split('T')[0]
  };
}

// Updated email alert function
function sendLowPerformanceAlert(lowPerformingAssets, spreadsheetUrl) {
  const subject = 'Google Ads Alert: Low Performing RSA Assets Detected';
  
  let emailBody = 'The following RSA assets are showing low performance:\n\n';
  
  lowPerformingAssets.forEach(asset => {
    emailBody += `Campaign: ${asset.campaign}\n`;
    emailBody += `Ad Group: ${asset.adGroup}\n`;
    emailBody += `Ad Label: ${asset.adLabel}\n`;
    emailBody += `Asset Type: ${asset.type}\n`;
    emailBody += `Asset Text: ${asset.text}\n\n`;
  });
  
  emailBody += `\nView full report here: ${spreadsheetUrl}`;
  
  MailApp.sendEmail({
    to: EMAIL_ADDRESSES.join(','),
    subject: subject,
    body: emailBody
  });
}

Have questions or feedback? Hit reply. Loved it? Share it! 🙂

And if this was forwarded to you, but you’re not subscribed yet…