Google Ads Export Script (v2 – עם תמיכה מורחבת ב-Performance Max)
הוראות התקנה:
- פתח Google Ads עם החשבון [email protected].
- בחר חשבון לקוח ספציפי (למשל קו פרישה).
- Tools 🔧 → Bulk Actions → Scripts → לחץ "+".
- העתק את כל הקוד למטה והדבק בעורך הסקריפטים.
- שמור, לחץ Authorize ואשר הרשאות גישה ל-Sheets.
- לחץ Preview ואז Run.
- בלוגים של הסקריפט (תפריט "Logs") יופיע URL של ה-Sheet שנוצר. שתף אותו "כל מי שיש לו את הקישור – מציג".
מה כלול בגרסה הזו:
- דוחות כלליים: Account Summary, Campaigns, Ad Groups, Keywords, Search Terms, Ads, Negative Candidates
- דוחות ייחודיים ל-Performance Max:
- P1 – ביצועי Asset Groups
- P2 – פירוק לפי ערוץ (Search / Display / YouTube / Shopping)
- P3 – תמות חיפוש (Search Categories) – הגרסה של pMax לסיירץ' טרמס
- P4 – ביצועי מוצרים (לחשבונות עם Shopping feed)
- P5 – איכות נכסים (Best / Good / Low)
- P6 – בדיקת קניבליזציה עם קמפיין Brand
- P7 – Audit checklist אוטומטי לבעיות נפוצות
הקוד:
/**
* Google Ads Script: ייצוא ביצועים מקיף ל-Google Sheets
*
* מה זה עושה:
* רץ בתוך Google Ads (לא דרך API חיצוני, אז אין צורך בDeveloper Token).
* מייצא 6 דוחות חיוניים ל-Google Sheet שאתה מציין למטה.
*
* איך מתקינים:
* 1. ב-Google Ads → Tools 🔧 → Bulk Actions → Scripts → "+"
* 2. הדבק את הקוד הזה
* 3. במשתנה SHEET_URL למטה - הדבק URL של Google Sheet (חדש או קיים)
* (אם תשאיר ריק - הסקריפט ייצור Sheet חדש בריצה הראשונה)
* 4. Authorize - אשר הרשאות גישה ל-Sheets
* 5. Preview ואז Run
*
* הסקריפט מייצא ל-30 ימים אחרונים. אפשר לשנות בקבוע DATE_RANGE.
*/
// ===== הגדרות - תוכל לשנות =====
var SHEET_URL = ''; // הדבק כאן URL של Google Sheet, או השאר ריק ליצירת חדש
var DATE_RANGE = 'LAST_30_DAYS'; // אפשרויות: LAST_7_DAYS, LAST_14_DAYS, LAST_30_DAYS, THIS_MONTH, LAST_MONTH
var MIN_IMPRESSIONS_FOR_SEARCH_TERMS = 5; // מינימום הופעות לכלול search term בדוח
// ================================
function main() {
var sheet = getOrCreateSpreadsheet();
Logger.log('Spreadsheet: ' + sheet.getUrl());
exportAccountSummary(sheet);
exportCampaigns(sheet);
exportAdGroups(sheet);
exportKeywords(sheet);
exportSearchTerms(sheet);
exportAds(sheet);
exportNegativeCandidates(sheet);
// Performance Max specific
exportPMaxAssetGroups(sheet);
exportPMaxChannelBreakdown(sheet);
exportPMaxSearchCategories(sheet);
exportPMaxListingGroups(sheet);
exportPMaxAssetPerformance(sheet);
exportBrandCannibalizationCheck(sheet);
exportPMaxAuditChecklist(sheet);
// Index sheet עם summary ולינקים
buildIndexSheet(sheet);
Logger.log('=========================================');
Logger.log('סיום! ה-Sheet זמין כאן:');
Logger.log(sheet.getUrl());
Logger.log('=========================================');
}
function getOrCreateSpreadsheet() {
if (SHEET_URL && SHEET_URL.length > 10) {
return SpreadsheetApp.openByUrl(SHEET_URL);
}
var account = AdsApp.currentAccount();
var name = 'Google Ads Export - ' + account.getName() + ' - ' + Utilities.formatDate(new Date(), 'GMT', 'yyyy-MM-dd');
var ss = SpreadsheetApp.create(name);
Logger.log('יצרתי Sheet חדש: ' + ss.getUrl());
Logger.log('שמור את ה-URL הזה ב-SHEET_URL להרצות הבאות.');
return ss;
}
function clearOrCreateSheet(ss, name) {
var sh = ss.getSheetByName(name);
if (sh) sh.clear();
else sh = ss.insertSheet(name);
return sh;
}
function writeRows(sheet, headers, rows) {
if (rows.length === 0) {
sheet.getRange(1, 1).setValue('No data for the selected date range.');
return;
}
sheet.getRange(1, 1, 1, headers.length).setValues([headers]).setFontWeight('bold').setBackground('#1a73e8').setFontColor('#ffffff');
sheet.getRange(2, 1, rows.length, headers.length).setValues(rows);
sheet.setFrozenRows(1);
sheet.autoResizeColumns(1, headers.length);
}
// ---------- Account Summary ----------
function exportAccountSummary(ss) {
var sh = clearOrCreateSheet(ss, '0_Account_Summary');
var account = AdsApp.currentAccount();
var stats = account.getStatsFor(DATE_RANGE);
var rows = [
['Account Name', account.getName()],
['Account ID', account.getCustomerId()],
['Currency', account.getCurrencyCode()],
['Time Zone', account.getTimeZone()],
['Date Range', DATE_RANGE],
['Report Generated', new Date().toString()],
['', ''],
['Impressions', stats.getImpressions()],
['Clicks', stats.getClicks()],
['CTR', (stats.getCtr() * 100).toFixed(2) + '%'],
['Cost', stats.getCost()],
['Average CPC', stats.getAverageCpc()],
['Conversions', stats.getConversions()],
['Conv. Rate', stats.getClicks() > 0 ? (stats.getConversions() / stats.getClicks() * 100).toFixed(2) + '%' : '0%'],
['Cost / Conv. (CPA)', stats.getConversions() > 0 ? (stats.getCost() / stats.getConversions()).toFixed(2) : 'N/A'],
];
sh.getRange(1, 1, rows.length, 2).setValues(rows);
sh.getRange(1, 1, rows.length, 1).setFontWeight('bold');
sh.autoResizeColumns(1, 2);
}
// ---------- Campaigns ----------
function exportCampaigns(ss) {
var sh = clearOrCreateSheet(ss, '1_Campaigns');
var headers = ['Campaign', 'Status', 'Type', 'Budget (Daily)', 'Bid Strategy',
'Impressions', 'Clicks', 'CTR %', 'Cost', 'Avg CPC',
'Conversions', 'Cost/Conv', 'Conv Rate %', 'Conv Value', 'ROAS'];
var rows = [];
var iter = AdsApp.campaigns().forDateRange(DATE_RANGE).get();
while (iter.hasNext()) {
var c = iter.next();
var s = c.getStatsFor(DATE_RANGE);
var budget = 0;
try { budget = c.getBudget().getAmount(); } catch (e) {}
var convValue = 0;
try { convValue = s.getConversionValue(); } catch (e) {}
rows.push([
c.getName(),
c.isEnabled() ? 'ENABLED' : (c.isPaused() ? 'PAUSED' : 'REMOVED'),
c.getAdvertisingChannelType(),
budget,
c.getBiddingStrategyType ? c.getBiddingStrategyType() : '',
s.getImpressions(),
s.getClicks(),
(s.getCtr() * 100).toFixed(2),
s.getCost(),
s.getAverageCpc(),
s.getConversions(),
s.getConversions() > 0 ? (s.getCost() / s.getConversions()).toFixed(2) : '',
s.getClicks() > 0 ? (s.getConversions() / s.getClicks() * 100).toFixed(2) : '',
convValue,
s.getCost() > 0 ? (convValue / s.getCost()).toFixed(2) : '',
]);
}
writeRows(sh, headers, rows);
}
// ---------- Ad Groups ----------
function exportAdGroups(ss) {
var sh = clearOrCreateSheet(ss, '2_AdGroups');
var headers = ['Campaign', 'Ad Group', 'Status', 'Impressions', 'Clicks',
'CTR %', 'Cost', 'Avg CPC', 'Conversions', 'Cost/Conv', 'Conv Rate %'];
var rows = [];
var iter = AdsApp.adGroups().forDateRange(DATE_RANGE).get();
while (iter.hasNext()) {
var ag = iter.next();
var s = ag.getStatsFor(DATE_RANGE);
if (s.getImpressions() === 0) continue;
rows.push([
ag.getCampaign().getName(),
ag.getName(),
ag.isEnabled() ? 'ENABLED' : 'PAUSED',
s.getImpressions(),
s.getClicks(),
(s.getCtr() * 100).toFixed(2),
s.getCost(),
s.getAverageCpc(),
s.getConversions(),
s.getConversions() > 0 ? (s.getCost() / s.getConversions()).toFixed(2) : '',
s.getClicks() > 0 ? (s.getConversions() / s.getClicks() * 100).toFixed(2) : '',
]);
}
writeRows(sh, headers, rows);
}
// ---------- Keywords ----------
function exportKeywords(ss) {
var sh = clearOrCreateSheet(ss, '3_Keywords');
var headers = ['Campaign', 'Ad Group', 'Keyword', 'Match Type', 'Status',
'Quality Score', 'Impressions', 'Clicks', 'CTR %', 'Cost',
'Avg CPC', 'Conversions', 'Cost/Conv', 'Conv Rate %'];
var rows = [];
var iter = AdsApp.keywords().forDateRange(DATE_RANGE).get();
while (iter.hasNext()) {
var k = iter.next();
var s = k.getStatsFor(DATE_RANGE);
if (s.getImpressions() === 0) continue;
var qs = '';
try { qs = k.getQualityScore(); } catch (e) {}
rows.push([
k.getCampaign().getName(),
k.getAdGroup().getName(),
k.getText(),
k.getMatchType(),
k.isEnabled() ? 'ENABLED' : 'PAUSED',
qs,
s.getImpressions(),
s.getClicks(),
(s.getCtr() * 100).toFixed(2),
s.getCost(),
s.getAverageCpc(),
s.getConversions(),
s.getConversions() > 0 ? (s.getCost() / s.getConversions()).toFixed(2) : '',
s.getClicks() > 0 ? (s.getConversions() / s.getClicks() * 100).toFixed(2) : '',
]);
}
writeRows(sh, headers, rows);
}
// ---------- Search Terms ----------
function exportSearchTerms(ss) {
var sh = clearOrCreateSheet(ss, '4_SearchTerms');
var headers = ['Campaign', 'Ad Group', 'Search Term', 'Match Type',
'Impressions', 'Clicks', 'CTR %', 'Cost', 'Conversions',
'Cost/Conv', 'Conv Rate %'];
var rows = [];
var query =
'SELECT campaign.name, ad_group.name, search_term_view.search_term, ' +
'segments.search_term_match_type, metrics.impressions, metrics.clicks, ' +
'metrics.ctr, metrics.cost_micros, metrics.conversions ' +
'FROM search_term_view ' +
'WHERE segments.date DURING ' + DATE_RANGE + ' ' +
'AND metrics.impressions >= ' + MIN_IMPRESSIONS_FOR_SEARCH_TERMS + ' ' +
'ORDER BY metrics.cost_micros DESC';
var report = AdsApp.report(query);
var rowsIter = report.rows();
while (rowsIter.hasNext()) {
var r = rowsIter.next();
var clicks = Number(r['metrics.clicks']) || 0;
var conv = Number(r['metrics.conversions']) || 0;
var cost = (Number(r['metrics.cost_micros']) || 0) / 1000000;
rows.push([
r['campaign.name'],
r['ad_group.name'],
r['search_term_view.search_term'],
r['segments.search_term_match_type'],
Number(r['metrics.impressions']) || 0,
clicks,
((Number(r['metrics.ctr']) || 0) * 100).toFixed(2),
cost.toFixed(2),
conv,
conv > 0 ? (cost / conv).toFixed(2) : '',
clicks > 0 ? (conv / clicks * 100).toFixed(2) : '',
]);
}
writeRows(sh, headers, rows);
}
// ---------- Negative Candidates ----------
function exportNegativeCandidates(ss) {
var sh = clearOrCreateSheet(ss, '5_NegativeCandidates');
var headers = ['Search Term', 'Campaign', 'Cost (No Conversions)', 'Clicks', 'Impressions', 'Recommendation'];
var rows = [];
var query =
'SELECT campaign.name, search_term_view.search_term, ' +
'metrics.impressions, metrics.clicks, metrics.cost_micros, metrics.conversions ' +
'FROM search_term_view ' +
'WHERE segments.date DURING ' + DATE_RANGE + ' ' +
'AND metrics.conversions = 0 ' +
'AND metrics.cost_micros > 0 ' +
'ORDER BY metrics.cost_micros DESC ' +
'LIMIT 200';
var report = AdsApp.report(query);
var rowsIter = report.rows();
while (rowsIter.hasNext()) {
var r = rowsIter.next();
var cost = (Number(r['metrics.cost_micros']) || 0) / 1000000;
var clicks = Number(r['metrics.clicks']) || 0;
var imps = Number(r['metrics.impressions']) || 0;
var rec = '';
if (clicks >= 5 && cost >= 5) rec = 'Strong negative candidate';
else if (clicks >= 3) rec = 'Possible negative';
else rec = 'Monitor';
rows.push([r['search_term_view.search_term'], r['campaign.name'], cost.toFixed(2), clicks, imps, rec]);
}
writeRows(sh, headers, rows);
}
// ---------- Ads ----------
function exportAds(ss) {
var sh = clearOrCreateSheet(ss, '6_Ads');
var headers = ['Campaign', 'Ad Group', 'Ad Type', 'Status', 'Headline 1', 'Headline 2',
'Description 1', 'Final URL', 'Impressions', 'Clicks', 'CTR %',
'Cost', 'Conversions'];
var rows = [];
var iter = AdsApp.ads().forDateRange(DATE_RANGE).get();
while (iter.hasNext()) {
var ad = iter.next();
var s = ad.getStatsFor(DATE_RANGE);
if (s.getImpressions() === 0) continue;
var h1 = '', h2 = '', d1 = '';
try {
if (ad.getType() === 'EXPANDED_TEXT_AD') {
h1 = ad.asType().expandedTextAd().getHeadlinePart1();
h2 = ad.asType().expandedTextAd().getHeadlinePart2();
d1 = ad.asType().expandedTextAd().getDescription();
} else if (ad.getType() === 'RESPONSIVE_SEARCH_AD') {
var rsa = ad.asType().responsiveSearchAd();
var hs = rsa.getHeadlines();
if (hs && hs.length) {
h1 = hs[0] && hs[0].text ? hs[0].text : '';
h2 = hs[1] && hs[1].text ? hs[1].text : '';
}
var ds = rsa.getDescriptions();
if (ds && ds.length) d1 = ds[0] && ds[0].text ? ds[0].text : '';
}
} catch (e) {}
rows.push([
ad.getCampaign().getName(),
ad.getAdGroup().getName(),
ad.getType(),
ad.isEnabled() ? 'ENABLED' : 'PAUSED',
h1, h2, d1,
ad.urls().getFinalUrl ? ad.urls().getFinalUrl() : '',
s.getImpressions(),
s.getClicks(),
(s.getCtr() * 100).toFixed(2),
s.getCost(),
s.getConversions(),
]);
}
writeRows(sh, headers, rows);
}
// ============================================================
// Performance Max specific reports
// ============================================================
// ---------- pMax Asset Groups ----------
function exportPMaxAssetGroups(ss) {
var sh = clearOrCreateSheet(ss, 'P1_PMax_AssetGroups');
var headers = ['Campaign', 'Asset Group', 'Status', 'Impressions', 'Clicks',
'CTR %', 'Cost', 'Conversions', 'Conv Value', 'Cost/Conv', 'ROAS'];
var rows = [];
var query =
'SELECT campaign.name, asset_group.name, asset_group.status, ' +
'metrics.impressions, metrics.clicks, metrics.ctr, metrics.cost_micros, ' +
'metrics.conversions, metrics.conversions_value ' +
'FROM asset_group ' +
'WHERE segments.date DURING ' + DATE_RANGE + ' ' +
'ORDER BY metrics.cost_micros DESC';
try {
var report = AdsApp.report(query);
var rIter = report.rows();
while (rIter.hasNext()) {
var r = rIter.next();
var imps = Number(r['metrics.impressions']) || 0;
var clicks = Number(r['metrics.clicks']) || 0;
var cost = (Number(r['metrics.cost_micros']) || 0) / 1000000;
var conv = Number(r['metrics.conversions']) || 0;
var val = Number(r['metrics.conversions_value']) || 0;
rows.push([
r['campaign.name'],
r['asset_group.name'],
r['asset_group.status'],
imps, clicks,
((Number(r['metrics.ctr']) || 0) * 100).toFixed(2),
cost.toFixed(2),
conv, val.toFixed(2),
conv > 0 ? (cost / conv).toFixed(2) : '',
cost > 0 ? (val / cost).toFixed(2) : '',
]);
}
} catch (e) { Logger.log('AssetGroups error: ' + e); }
writeRows(sh, headers, rows);
}
// ---------- pMax Channel Breakdown ----------
function exportPMaxChannelBreakdown(ss) {
var sh = clearOrCreateSheet(ss, 'P2_PMax_ChannelBreakdown');
var headers = ['Campaign', 'Channel', 'Sub-Channel', 'Impressions', 'Clicks',
'CTR %', 'Cost', 'Conversions', 'Cost/Conv'];
var rows = [];
var query =
'SELECT campaign.name, segments.ad_network_type, ' +
'metrics.impressions, metrics.clicks, metrics.ctr, metrics.cost_micros, metrics.conversions ' +
'FROM campaign ' +
'WHERE campaign.advertising_channel_type = "PERFORMANCE_MAX" ' +
'AND segments.date DURING ' + DATE_RANGE + ' ' +
'ORDER BY metrics.cost_micros DESC';
try {
var report = AdsApp.report(query);
var rIter = report.rows();
while (rIter.hasNext()) {
var r = rIter.next();
var clicks = Number(r['metrics.clicks']) || 0;
var conv = Number(r['metrics.conversions']) || 0;
var cost = (Number(r['metrics.cost_micros']) || 0) / 1000000;
rows.push([
r['campaign.name'],
r['segments.ad_network_type'],
'',
Number(r['metrics.impressions']) || 0,
clicks,
((Number(r['metrics.ctr']) || 0) * 100).toFixed(2),
cost.toFixed(2),
conv,
conv > 0 ? (cost / conv).toFixed(2) : '',
]);
}
} catch (e) { Logger.log('ChannelBreakdown error: ' + e); }
writeRows(sh, headers, rows);
}
// ---------- pMax Search Categories ----------
function exportPMaxSearchCategories(ss) {
var sh = clearOrCreateSheet(ss, 'P3_PMax_SearchCategories');
var headers = ['Campaign', 'Category Label', 'Impressions', 'Clicks', 'Conversions', 'Note'];
var rows = [];
var query =
'SELECT campaign.name, campaign_search_term_insight.category_label, ' +
'metrics.impressions, metrics.clicks, metrics.conversions ' +
'FROM campaign_search_term_insight ' +
'WHERE segments.date DURING ' + DATE_RANGE + ' ' +
'ORDER BY metrics.impressions DESC ' +
'LIMIT 500';
try {
var report = AdsApp.report(query);
var rIter = report.rows();
while (rIter.hasNext()) {
var r = rIter.next();
var imps = Number(r['metrics.impressions']) || 0;
var clicks = Number(r['metrics.clicks']) || 0;
var conv = Number(r['metrics.conversions']) || 0;
var note = '';
if (clicks >= 10 && conv === 0) note = 'High clicks no conversions - candidate for negative theme';
else if (imps >= 1000 && clicks < 5) note = 'High impressions low clicks - possibly irrelevant';
rows.push([r['campaign.name'], r['campaign_search_term_insight.category_label'], imps, clicks, conv, note]);
}
} catch (e) { Logger.log('SearchCategories error: ' + e); }
writeRows(sh, headers, rows);
}
// ---------- pMax Listing Groups (Shopping) ----------
function exportPMaxListingGroups(ss) {
var sh = clearOrCreateSheet(ss, 'P4_PMax_Products');
var headers = ['Campaign', 'Product Title', 'Product ID', 'Impressions',
'Clicks', 'Cost', 'Conversions', 'Conv Value', 'ROAS'];
var rows = [];
var query =
'SELECT campaign.name, segments.product_title, segments.product_item_id, ' +
'metrics.impressions, metrics.clicks, metrics.cost_micros, ' +
'metrics.conversions, metrics.conversions_value ' +
'FROM shopping_performance_view ' +
'WHERE segments.date DURING ' + DATE_RANGE + ' ' +
'ORDER BY metrics.cost_micros DESC ' +
'LIMIT 500';
try {
var report = AdsApp.report(query);
var rIter = report.rows();
while (rIter.hasNext()) {
var r = rIter.next();
var cost = (Number(r['metrics.cost_micros']) || 0) / 1000000;
var val = Number(r['metrics.conversions_value']) || 0;
rows.push([
r['campaign.name'],
r['segments.product_title'],
r['segments.product_item_id'],
Number(r['metrics.impressions']) || 0,
Number(r['metrics.clicks']) || 0,
cost.toFixed(2),
Number(r['metrics.conversions']) || 0,
val.toFixed(2),
cost > 0 ? (val / cost).toFixed(2) : '',
]);
}
} catch (e) {
Logger.log('ProductsReport error (probably not an ecom account): ' + e);
sh.getRange(1, 1).setValue('No shopping data (likely a non-ecom account)');
return;
}
writeRows(sh, headers, rows);
}
// ---------- pMax Asset Performance ----------
function exportPMaxAssetPerformance(ss) {
var sh = clearOrCreateSheet(ss, 'P5_PMax_Assets');
var headers = ['Campaign', 'Asset Group', 'Asset Type', 'Performance Label',
'Asset Text/Source', 'Field Type'];
var rows = [];
var query =
'SELECT campaign.name, asset_group.name, asset.type, ' +
'asset_group_asset.performance_label, asset.text_asset.text, ' +
'asset.image_asset.full_size.url, asset_group_asset.field_type ' +
'FROM asset_group_asset ' +
'WHERE asset_group_asset.status = "ENABLED" ' +
'LIMIT 1000';
try {
var report = AdsApp.report(query);
var rIter = report.rows();
while (rIter.hasNext()) {
var r = rIter.next();
var src = r['asset.text_asset.text'] || r['asset.image_asset.full_size.url'] || '';
rows.push([
r['campaign.name'],
r['asset_group.name'],
r['asset.type'],
r['asset_group_asset.performance_label'],
src,
r['asset_group_asset.field_type'],
]);
}
} catch (e) { Logger.log('AssetPerformance error: ' + e); }
writeRows(sh, headers, rows);
}
// ---------- Brand Cannibalization Check ----------
function exportBrandCannibalizationCheck(ss) {
var sh = clearOrCreateSheet(ss, 'P6_BrandCannibalizationCheck');
sh.getRange(1, 1).setValue('בדיקת קניבליזציה בין pMax ל-Search Brand').setFontWeight('bold').setFontSize(14);
sh.getRange(2, 1).setValue('פירוט: pMax עלולה "לגנוב" את הטראפיק שמלכתחילה היה מגיע מקמפיין ברנד.');
sh.getRange(3, 1).setValue('ניתן לבדוק על ידי בידוד טראפיק שכולל את שם המותג ב-search categories.');
var headers = ['Campaign Type', 'Campaign', 'Impressions', 'Clicks', 'Cost', 'Conversions', 'Cost/Conv'];
var rows = [];
var query =
'SELECT campaign.name, campaign.advertising_channel_type, ' +
'metrics.impressions, metrics.clicks, metrics.cost_micros, metrics.conversions ' +
'FROM campaign ' +
'WHERE campaign.status = "ENABLED" ' +
'AND segments.date DURING ' + DATE_RANGE + ' ' +
'AND (campaign.advertising_channel_type = "PERFORMANCE_MAX" OR campaign.advertising_channel_type = "SEARCH") ' +
'ORDER BY metrics.cost_micros DESC';
try {
var report = AdsApp.report(query);
var rIter = report.rows();
while (rIter.hasNext()) {
var r = rIter.next();
var clicks = Number(r['metrics.clicks']) || 0;
var conv = Number(r['metrics.conversions']) || 0;
var cost = (Number(r['metrics.cost_micros']) || 0) / 1000000;
rows.push([
r['campaign.advertising_channel_type'],
r['campaign.name'],
Number(r['metrics.impressions']) || 0,
clicks, cost.toFixed(2), conv,
conv > 0 ? (cost / conv).toFixed(2) : '',
]);
}
} catch (e) { Logger.log('Cannibalization error: ' + e); }
if (rows.length) {
sh.getRange(5, 1, 1, headers.length).setValues([headers]).setFontWeight('bold').setBackground('#1a73e8').setFontColor('#ffffff');
sh.getRange(6, 1, rows.length, headers.length).setValues(rows);
sh.autoResizeColumns(1, headers.length);
}
}
// ---------- pMax Audit Checklist ----------
function exportPMaxAuditChecklist(ss) {
var sh = clearOrCreateSheet(ss, 'P7_PMax_Audit');
var rows = [['Check', 'Campaign', 'Status', 'Recommendation']];
var iter = AdsApp.performanceMaxCampaigns ? AdsApp.performanceMaxCampaigns().get() : null;
if (!iter) {
sh.getRange(1, 1).setValue('Performance Max API לא זמין בסקריפט זה. בדיקה ידנית נדרשת.');
return;
}
while (iter.hasNext()) {
var c = iter.next();
var name = c.getName();
var status = c.isEnabled() ? 'ENABLED' : 'PAUSED';
if (status !== 'ENABLED') continue;
// Asset groups count
var agCount = 0;
try {
var agIter = c.assetGroups().get();
while (agIter.hasNext()) { agIter.next(); agCount++; }
} catch (e) {}
if (agCount <= 1) {
rows.push(['Asset Groups Count', name, agCount + ' asset group(s)',
'מומלץ פיצול לפי תמה/קהל - לפחות 2-3 asset groups']);
}
// Final URL Expansion
try {
var urlExp = c.urlExpansionOptOut ? c.urlExpansionOptOut() : null;
if (urlExp === false) {
rows.push(['Final URL Expansion', name, 'ENABLED',
'בדוק אם זה רלוונטי - אם יש דפים לא ממירים באתר, כבה או הגבל path']);
}
} catch (e) {}
}
if (rows.length === 1) {
rows.push(['—', '—', 'אין pMax campaigns פעילים או שלא זוהו בעיות', '']);
}
sh.getRange(1, 1, rows.length, 4).setValues(rows);
sh.getRange(1, 1, 1, 4).setFontWeight('bold').setBackground('#1a73e8').setFontColor('#ffffff');
sh.autoResizeColumns(1, 4);
}
// ---------- Index ----------
function buildIndexSheet(ss) {
var sh = clearOrCreateSheet(ss, '_Index');
ss.setActiveSheet(sh);
ss.moveActiveSheet(1);
var rows = [
['Google Ads Export', ''],
['Account', AdsApp.currentAccount().getName()],
['Generated', new Date().toString()],
['Date Range', DATE_RANGE],
['', ''],
['Sheet', 'Description'],
['0_Account_Summary', 'KPIs ברמת חשבון'],
['1_Campaigns', 'ביצועים ברמת קמפיין'],
['2_AdGroups', 'ביצועים ברמת קבוצת מודעות'],
['3_Keywords', 'מילות מפתח עם Quality Score'],
['4_SearchTerms', 'Search terms ראשיים (סף מינימום הופעות)'],
['5_NegativeCandidates', 'מועמדים ל-keywords שליליות (כסף בלי המרות)'],
['6_Ads', 'ביצועי מודעות'],
['', ''],
['Performance Max specific:', ''],
['P1_PMax_AssetGroups', 'ביצועי asset groups'],
['P2_PMax_ChannelBreakdown', 'פירוק לפי ערוץ (Search/Display/YouTube/Shopping)'],
['P3_PMax_SearchCategories', 'תמות חיפוש שגוגל זיהתה (במקום search terms קלאסיים)'],
['P4_PMax_Products', 'ביצועי מוצרים (אם יש Shopping feed)'],
['P5_PMax_Assets', 'איכות נכסים (Best/Good/Low) לפי asset'],
['P6_BrandCannibalizationCheck', 'בדיקת קניבליזציה בין pMax לקמפיין Brand'],
['P7_PMax_Audit', 'Checklist אוטומטי לבעיות נפוצות'],
];
sh.getRange(1, 1, rows.length, 2).setValues(rows);
sh.getRange(1, 1, 1, 2).setFontWeight('bold').setFontSize(14);
sh.getRange(6, 1, 1, 2).setFontWeight('bold').setBackground('#1a73e8').setFontColor('#ffffff');
sh.autoResizeColumns(1, 2);
}