This tutorial documents a script that audits conversion goal activity across all accounts under your MCC. It writes the results to a Google Sheet, tags the health of each goal, color-codes the status, and emails a summary only when there's an issue.
There are no unnecessary tools or third-party dashboards, just raw control and complete visibility.
This script audits every Google Ads account under your MCC and evaluates the status of each conversion goal over a defined lookback period. Instead of guessing which goals are active or broken, this system gives you a real-time snapshot in a Google Sheet and only alerts you when something needs your attention.
There will be two email notifications: one for accounts that need attention and the other for accounts that are all good.
Example:
Messages:
Got itโyour script needs two distinct email outputs:
โ โAll Goodโ Email (if no issues are found)
โ ๏ธ โNeeds Attentionโ Email (if any goals are inactive, missing, or low-performing)
Hereโs precisely how both versions should look, using your sample data and current logic:
Message #1
All monitored conversion goals are reporting activity over the last 60 days.
No issues found in any accounts.
View the full report:
https://docs.google.com/spreadsheets/d/yoursheetid
Message #2
The following conversion goals may need review (e.g., no recent conversions or inactive):
Bright Widgets Inc (123-456-789) - Contact Form Submission (Needs Attention)
Acme Corp (987-654-321) - Schedule Call (Inactive)
Acme Corp (987-654-321) - Free Trial Signup (Needs Attention)
Rocket Leads (456-789-123) - (configured, no activity) (No Recent Conversions)
Beta Test Group (321-654-987) - (not set) (Inactive)
Zebra Analytics (999-111-222) - Whitepaper Download (Needs Attention)
View the full report:
https://docs.google.com/spreadsheets/d/yoursheetid
Before deploying the script, make sure your environment is configured correctly. This script is designed to run within a Google Ads MCC context to write data and send alerts.
This is the script in its complete, unedited form. All comments, formatting, and logic are exactly as originally written. Do not modify this if you plan to follow the breakdown later in this tutorial.
// ========== CONFIGURATION ========== const SHEET_ID = '1PvpW3eUl5fqRwabBg0P7D8LKzW83MOTVX1KDBMB3ipA'; const LOOKBACK_DAYS = 60; const EXCLUDED_ACCOUNT_IDS = [ '000-000-000', '000-000-000', '000-000-000' ]; const RECIPIENT_EMAILS = '[email protected]'; // =================================== function main() { const sheet = SpreadsheetApp.openById(SHEET_ID).getSheets()[0]; sheet.clear(); sheet.appendRow(['Account Name', 'Account ID', 'Goal Name', 'Conversions', 'Status', 'Date Range']); const startDate = getDateXDaysAgo(LOOKBACK_DAYS); const endDate = getTodayDate(); const dateRangeLabel = `Last ${LOOKBACK_DAYS} days (${startDate} to ${endDate})`; const accounts = MccApp.accounts().get(); const flagged = []; let rowIndex = 2; while (accounts.hasNext()) { const account = accounts.next(); const accountId = account.getCustomerId(); const accountName = account.getName(); Logger.log(`โณ Checking account: ${accountName} (${accountId})`); if (EXCLUDED_ACCOUNT_IDS.includes(accountId)) { Logger.log(`๐ซ Skipping excluded account: ${accountName} (${accountId})`); continue; } try { MccApp.select(account); const query = ` SELECT ConversionTypeName, Conversions FROM CAMPAIGN_PERFORMANCE_REPORT DURING ${startDate},${endDate} `; const report = AdsApp.report(query); const rows = report.rows(); const goalMap = {}; while (rows.hasNext()) { const row = rows.next(); const goalName = row['ConversionTypeName'] || '(not set)'; const conversions = parseFloat(row['Conversions']) || 0; if (!goalMap[goalName]) { goalMap[goalName] = 0; } goalMap[goalName] += conversions; } if (Object.keys(goalMap).length === 0) { Logger.log(`โ ๏ธ No conversion data returned for account: ${accountName} (${accountId})`); const status = 'No Recent Conversions'; sheet.appendRow([ accountName, accountId, '(configured, no activity)', 0, status, dateRangeLabel ]); setStatusColumnColor(sheet, rowIndex, status); flagged.push(`${accountName} (${accountId}) - no conversion goals triggered`); rowIndex++; continue; } for (const [goalName, totalConversions] of Object.entries(goalMap)) { let status; if (totalConversions > 0) { status = 'Active'; } else if (goalName.toLowerCase().includes('test') || goalName === '(not set)') { status = 'Inactive'; } else { status = 'Needs Attention'; } sheet.appendRow([ accountName, accountId, goalName, totalConversions, status, dateRangeLabel ]); setStatusColumnColor(sheet, rowIndex, status); if (status !== 'Active') { flagged.push(`${accountName} (${accountId}) - ${goalName} (${status})`); } rowIndex++; } } catch (e) { Logger.log(`โ Error processing account: ${accountName} (${accountId}) - ${e.message}`); } } if (flagged.length > 0) { const subject = 'โ ๏ธ Conversion Goals Needing Attention'; const body = `The following conversion goals may need review (e.g., no recent conversions or inactive):\n\n` + flagged.join('\n') + `\n\nView the full report:\nhttps://docs.google.com/spreadsheets/d/${SHEET_ID}`; MailApp.sendEmail(RECIPIENT_EMAILS, subject, body); } else { Logger.log('โ All goals are reporting conversions.'); } } // ๐จ Apply color to only the "Status" column (Column E) function setStatusColumnColor(sheet, row, status) { const range = sheet.getRange(row, 5); // Column E switch (status) { case 'Active': range.setBackground('#d9ead3'); // Light green break; case 'Inactive': range.setBackground('#f4cccc'); // Light red break; case 'Needs Attention': range.setBackground('#fff2cc'); // Light yellow break; case 'No Recent Conversions': range.setBackground('#e6e6fa'); // Light purple break; default: range.setBackground(null); } } // ๐ Helpers function getTodayDate() { const date = new Date(); return Utilities.formatDate(date, AdsApp.currentAccount().getTimeZone(), 'yyyyMMdd'); } function getDateXDaysAgo(days) { const date = new Date(); date.setDate(date.getDate() - days); return Utilities.formatDate(date, AdsApp.currentAccount().getTimeZone(), 'yyyyMMdd'); }
This walkthrough shows exactly how to set up the conversion goal health checker for your Google Ads MCC account. Follow each step to get the system running, writing to Google Sheets, and sending alerts when something breaks.
Start by setting up the output destination.
You will deploy this script inside your MCC account.
The first time you use the script, you need to authorize it.
You must complete this step or the script will fail to run.
Inside the script, review and modify the following:
You can leave the rest of the script exactly as it is.
Run it once manually to make sure everything works.
To make this automatic, set it to run on a recurring schedule.
The script will run automatically and only alert you when something is off.
When the script runs, it checks all conversion goals. If any are inactive or not firing, you receive an email.
This script is not just a data dump. It's a watchdog for every conversion goal across your entire Google Ads MCC. You get alerted when something breaks, stalls, or quietly stops tracking. When everything is fine, you get silence.
It checks every account you manage, flags inactive or underperforming goals, and delivers a filtered, no-noise report straight to your inbox. The Google Sheet gives you a live view of goal status by account, goal name, and conversion volume, with visual cues baked in.
Youโre not digging through interfaces. Youโre not waiting until the end of the month to realize youโve been blind. Youโre in control before it becomes a problem.
Tired of guessing whether your tracking works? Want a team that builds systems instead of just reports?
Bright Vessel doesnโt just manage Google Ads, we engineer performance visibility at scale. Whether you need help deploying this script, integrating it into a larger automation stack, or building a custom analytics layer that tells you something, weโre ready.
Talk to the team that builds what other agencies fake.
"*" indicates required fields
"*" indicates required fields
"*" indicates required fields