{"id":"3136bea6-be3f-4269-ba9d-015054832338","shortId":"t2fV5d","kind":"skill","title":"google-ads-scripts","tagline":"Google Ads Scripts Reference — JavaScript automation, AdsApp object model, selectors, GAQL queries, 10+ working script patterns, MCC parallel processing, Sheets integration","description":"# Google Ads Scripts Reference\n\nComplete reference for Google Ads Scripts: JavaScript automation within Google Ads, AdsApp object model, selectors and iterators, GAQL queries, common script patterns with working code, Google Sheets integration, MCC parallel processing, and best practices.\n\nFull docs: https://cogny.com/docs/google-ads-scripts\n\n## Usage\n\n```\n/google-ads-scripts                          # Full overview\n/google-ads-scripts budget pacing            # Budget pacing monitor script\n/google-ads-scripts negative keywords        # Search term negative keyword miner\n/google-ads-scripts quality score tracker    # Quality score logging to Sheets\n/google-ads-scripts MCC parallel             # MCC parallel processing pattern\n/google-ads-scripts selectors                # Selector and iterator patterns\n/google-ads-scripts GAQL                     # GAQL queries in scripts\n/google-ads-scripts anomaly detection        # Anomaly alerting script\n```\n\n## Instructions\n\nYou are a Google Ads Scripts expert. Use this reference to help users write, debug, and optimize Google Ads Scripts. Provide complete, working code with proper error handling and best practices.\n\nWhen the user asks a question, find the relevant section below and provide precise, actionable answers with ready-to-use JavaScript code.\n\nIf the user provides a specific topic as an argument, focus on that area. Otherwise, provide an overview of capabilities and common patterns.\n\nKey principles:\n- Always include a `DRY_RUN` flag in scripts that modify the account\n- Log changes before making them\n- Use `try/catch` for error handling\n- Prefer GAQL (`AdsApp.search()`) for complex queries\n- Batch Google Sheets writes for performance\n- Remind users about the 30-minute execution limit (60 min for MCC)\n\n---\n\n## Overview\n\nGoogle Ads Scripts let you programmatically control Google Ads using JavaScript. Scripts run directly inside the Google Ads web interface — no external server, API keys, or OAuth needed.\n\n**Key capabilities:**\n- Read and modify campaigns, ad groups, ads, keywords, extensions\n- Query performance data using GAQL (Google Ads Query Language)\n- Integrate with Google Sheets for dashboards and logging\n- Send email alerts via MailApp\n- Make HTTP requests via UrlFetchApp\n- Run on a schedule (hourly, daily, weekly, monthly)\n\n**Single-account vs MCC scripts:**\n\n| Feature | Single-Account | MCC Script |\n|---------|---------------|------------|\n| Scope | One account | All accounts under MCC |\n| Entry point | `main()` | `main()` with `AdsManagerApp` |\n| Execution limit | 30 minutes | 60 minutes |\n| Parallel execution | No | Yes, `executeInParallel()` |\n\n**Language:** JavaScript ES5 with some ES6. `let`, `const`, arrow functions, template literals work. `async`/`await`, `import`/`export`, `class` do not.\n\n## AdsApp Object Model\n\nThe `AdsApp` object is the root of all single-account operations.\n\n### Entity Hierarchy\n\n```\nAdsApp\n  +-- campaigns()\n  |     +-- adGroups()\n  |     |     +-- ads()\n  |     |     +-- keywords()\n  |     |     +-- audiences()\n  |     +-- extensions()\n  +-- adGroups()        (account-level shortcut)\n  +-- ads()             (account-level shortcut)\n  +-- keywords()        (account-level shortcut)\n  +-- negativeKeywords()\n  +-- shoppingCampaigns()\n  +-- videoCampaigns()\n  +-- labels()\n  +-- budgets()\n  +-- biddingStrategies()\n```\n\n### Common Entity Methods\n\n```javascript\n// Status\nentity.isEnabled()\nentity.isPaused()\nentity.isRemoved()\nentity.enable()\nentity.pause()\nentity.remove()\n\n// Naming\nentity.getName()\nentity.setName('New Name')\n\n// Stats (requires date range)\nentity.getStatsFor('LAST_30_DAYS')\nentity.getStatsFor('20250101', '20250131')\n\n// Stats object methods\nvar stats = entity.getStatsFor('LAST_30_DAYS');\nstats.getImpressions()\nstats.getClicks()\nstats.getCtr()\nstats.getAverageCpc()\nstats.getCost()\nstats.getConversions()\nstats.getConversionRate()\n```\n\n## Selectors and Iterators\n\nEvery entity collection uses the selector-iterator pattern — the fundamental data access pattern in every script.\n\n### Selector Methods\n\n```javascript\nvar keywords = AdsApp.keywords()\n  .withCondition('Status = ENABLED')\n  .withCondition('CampaignStatus = ENABLED')\n  .withCondition('AdGroupStatus = ENABLED')\n  .withCondition('Ctr < 0.01')\n  .forDateRange('LAST_30_DAYS')\n  .orderBy('Impressions DESC')\n  .withLimit(100)\n  .get();\n```\n\n**`.withCondition(condition)`** — Filter entities. Operators: `=`, `!=`, `>`, `<`, `>=`, `<=`, `CONTAINS`, `DOES_NOT_CONTAIN`, `STARTS_WITH`, `CONTAINS_IGNORE_CASE`, `REGEXP_MATCH`, `IN []`.\n\n```javascript\n.withCondition('Name CONTAINS \"brand\"')\n.withCondition('Name REGEXP_MATCH \"^(buy|shop|order).*\"')\n.withCondition('QualityScore > 5')\n.withCondition('LabelNames CONTAINS_ANY [\"Priority\", \"Monitor\"]')\n.withCondition('CampaignName IN [\"Search - Brand\", \"Search - Generic\"]')\n```\n\n**`.forDateRange(dateRange)`** — Required for metrics-based conditions. Predefined ranges: `TODAY`, `YESTERDAY`, `LAST_7_DAYS`, `THIS_WEEK_SUN_TODAY`, `THIS_WEEK_MON_TODAY`, `LAST_WEEK`, `LAST_14_DAYS`, `LAST_30_DAYS`, `LAST_BUSINESS_WEEK`, `LAST_WEEK_SUN_SAT`, `THIS_MONTH`, `LAST_MONTH`, `ALL_TIME`.\n\n```javascript\n// Custom date range\n.forDateRange('20250101', '20250131')\n```\n\n**`.orderBy(orderSpec)`** — Sort results (`ASC` / `DESC`).\n\n**`.withLimit(limit)`** — Cap the number of results.\n\n### Iterator Pattern\n\n```javascript\nvar iterator = AdsApp.keywords()\n  .withCondition('Status = ENABLED')\n  .forDateRange('LAST_7_DAYS')\n  .withCondition('Impressions > 100')\n  .get();\n\nwhile (iterator.hasNext()) {\n  var keyword = iterator.next();\n  var stats = keyword.getStatsFor('LAST_7_DAYS');\n  Logger.log(keyword.getText() + ': ' + stats.getClicks() + ' clicks');\n}\n```\n\n## GAQL in Scripts\n\nGoogle Ads Query Language provides SQL-like access to the full Google Ads API from within scripts.\n\n### AdsApp.search(query)\n\nReturns an iterator of result rows as JavaScript objects.\n\n```javascript\nfunction main() {\n  var query = \"SELECT \" +\n    \"campaign.name, \" +\n    \"campaign.status, \" +\n    \"metrics.impressions, \" +\n    \"metrics.clicks, \" +\n    \"metrics.cost_micros, \" +\n    \"metrics.conversions \" +\n    \"FROM campaign \" +\n    \"WHERE campaign.status = 'ENABLED' \" +\n    \"AND segments.date DURING LAST_30_DAYS \" +\n    \"ORDER BY metrics.cost_micros DESC\";\n\n  var results = AdsApp.search(query);\n\n  while (results.hasNext()) {\n    var row = results.next();\n    Logger.log(row.campaign.name +\n      ' | Cost: $' + (row.metrics.costMicros / 1000000).toFixed(2) +\n      ' | Conversions: ' + row.metrics.conversions);\n  }\n}\n```\n\n### AdsApp.report(query)\n\nReturns a report object with export capabilities — best for bulk data and Sheets integration.\n\n```javascript\nvar report = AdsApp.report(\n  \"SELECT \" +\n  \"ad_group_criterion.keyword.text, \" +\n  \"ad_group_criterion.quality_info.quality_score, \" +\n  \"metrics.impressions, \" +\n  \"metrics.clicks, \" +\n  \"metrics.conversions \" +\n  \"FROM keyword_view \" +\n  \"WHERE campaign.status = 'ENABLED' \" +\n  \"AND segments.date DURING LAST_30_DAYS\"\n);\n\n// Export directly to Google Sheets\nvar sheet = SpreadsheetApp.openByUrl('YOUR_SHEET_URL').getActiveSheet();\nreport.exportToSheet(sheet);\n```\n\n### Search vs Report\n\n| Feature | `AdsApp.search()` | `AdsApp.report()` |\n|---------|-------------------|-------------------|\n| Return type | Row iterator | Report object |\n| Field access | `row.campaign.name` | Row iterator or `exportToSheet()` |\n| Sheet export | Manual | Built-in `exportToSheet()` |\n| Best for | Processing rows individually | Bulk export, dashboards |\n\n## Common Script Patterns\n\n### 1. Pause Low-Performing Keywords\n\n```javascript\nfunction main() {\n  var CONFIG = {\n    MIN_QUALITY_SCORE: 4,\n    MIN_CTR: 0.005,\n    MIN_IMPRESSIONS: 100,\n    DATE_RANGE: 'LAST_30_DAYS',\n    DRY_RUN: true\n  };\n\n  var query = \"SELECT \" +\n    \"ad_group_criterion.keyword.text, \" +\n    \"ad_group_criterion.keyword.match_type, \" +\n    \"ad_group_criterion.quality_info.quality_score, \" +\n    \"ad_group.name, \" +\n    \"campaign.name, \" +\n    \"metrics.impressions, \" +\n    \"metrics.clicks, \" +\n    \"metrics.ctr, \" +\n    \"metrics.cost_micros, \" +\n    \"metrics.conversions \" +\n    \"FROM keyword_view \" +\n    \"WHERE campaign.status = 'ENABLED' \" +\n    \"AND ad_group.status = 'ENABLED' \" +\n    \"AND ad_group_criterion.status = 'ENABLED' \" +\n    \"AND metrics.impressions > \" + CONFIG.MIN_IMPRESSIONS + \" \" +\n    \"AND segments.date DURING \" + CONFIG.DATE_RANGE;\n\n  var results = AdsApp.search(query);\n  var pauseCount = 0;\n\n  while (results.hasNext()) {\n    var row = results.next();\n    var qs = row.adGroupCriterion.qualityInfo.qualityScore;\n    var ctr = row.metrics.ctr;\n\n    if (qs < CONFIG.MIN_QUALITY_SCORE || ctr < CONFIG.MIN_CTR) {\n      Logger.log('PAUSE: \"' + row.adGroupCriterion.keyword.text + '\"' +\n        ' | QS: ' + qs + ' | CTR: ' + (ctr * 100).toFixed(2) + '%' +\n        ' | Campaign: ' + row.campaign.name);\n\n      if (!CONFIG.DRY_RUN) {\n        var keywords = AdsApp.keywords()\n          .withCondition('Text = \"' + row.adGroupCriterion.keyword.text + '\"')\n          .withCondition('AdGroupName = \"' + row.adGroup.name + '\"')\n          .withCondition('CampaignName = \"' + row.campaign.name + '\"')\n          .get();\n        if (keywords.hasNext()) keywords.next().pause();\n      }\n      pauseCount++;\n    }\n  }\n\n  Logger.log('Total keywords to pause: ' + pauseCount);\n  if (CONFIG.DRY_RUN) Logger.log('DRY RUN — no changes applied.');\n}\n```\n\n### 2. Budget Pacing Monitor\n\n```javascript\nfunction main() {\n  var CONFIG = {\n    MONTHLY_BUDGET: 10000,\n    ALERT_THRESHOLD_OVER: 0.15,\n    ALERT_THRESHOLD_UNDER: 0.20,\n    ALERT_EMAIL: 'team@example.com',\n    CAMPAIGN_LABEL: 'Budget-Monitor'\n  };\n\n  var today = new Date();\n  var daysInMonth = new Date(today.getFullYear(), today.getMonth() + 1, 0).getDate();\n  var dayOfMonth = today.getDate();\n  var expectedSpend = (CONFIG.MONTHLY_BUDGET / daysInMonth) * dayOfMonth;\n\n  var firstOfMonth = Utilities.formatDate(\n    new Date(today.getFullYear(), today.getMonth(), 1), 'UTC', 'yyyyMMdd');\n  var todayStr = Utilities.formatDate(today, 'UTC', 'yyyyMMdd');\n\n  var campaigns = AdsApp.campaigns()\n    .withCondition('Status = ENABLED')\n    .withCondition('LabelNames CONTAINS_ANY [\"' + CONFIG.CAMPAIGN_LABEL + '\"]')\n    .forDateRange(firstOfMonth, todayStr)\n    .get();\n\n  var totalSpend = 0;\n  var campaignDetails = [];\n\n  while (campaigns.hasNext()) {\n    var campaign = campaigns.next();\n    var stats = campaign.getStatsFor(firstOfMonth, todayStr);\n    var spend = stats.getCost();\n    totalSpend += spend;\n    campaignDetails.push({\n      name: campaign.getName(),\n      spend: spend,\n      conversions: stats.getConversions()\n    });\n  }\n\n  var paceRatio = totalSpend / expectedSpend;\n  var projectedSpend = (totalSpend / dayOfMonth) * daysInMonth;\n  var status = 'ON PACE';\n\n  if (paceRatio > (1 + CONFIG.ALERT_THRESHOLD_OVER)) status = 'OVERSPENDING';\n  else if (paceRatio < (1 - CONFIG.ALERT_THRESHOLD_UNDER)) status = 'UNDERSPENDING';\n\n  Logger.log('Day ' + dayOfMonth + '/' + daysInMonth +\n    ' | Expected: $' + expectedSpend.toFixed(2) +\n    ' | Actual: $' + totalSpend.toFixed(2) +\n    ' | Projected: $' + projectedSpend.toFixed(2) +\n    ' | Status: ' + status);\n\n  if (status !== 'ON PACE') {\n    var body = 'Budget Pacing Alert\\n\\nStatus: ' + status +\n      '\\nDay ' + dayOfMonth + ' of ' + daysInMonth +\n      '\\nMonthly budget: $' + CONFIG.MONTHLY_BUDGET.toFixed(2) +\n      '\\nExpected spend: $' + expectedSpend.toFixed(2) +\n      '\\nActual spend: $' + totalSpend.toFixed(2) +\n      '\\nProjected: $' + projectedSpend.toFixed(2);\n\n    MailApp.sendEmail(CONFIG.ALERT_EMAIL, '[Google Ads] Budget Alert: ' + status, body);\n  }\n}\n```\n\n### 3. Search Terms Negative Keyword Miner\n\n```javascript\nfunction main() {\n  var CONFIG = {\n    MIN_COST: 50,\n    MAX_CONVERSIONS: 0,\n    MIN_CLICKS: 10,\n    DATE_RANGE: 'LAST_30_DAYS',\n    NEGATIVE_MATCH_TYPE: 'EXACT',\n    DRY_RUN: true,\n    LOG_SHEET_URL: ''\n  };\n\n  var query = \"SELECT \" +\n    \"search_term_view.search_term, \" +\n    \"campaign.name, \" +\n    \"ad_group.name, \" +\n    \"metrics.impressions, \" +\n    \"metrics.clicks, \" +\n    \"metrics.cost_micros, \" +\n    \"metrics.conversions \" +\n    \"FROM search_term_view \" +\n    \"WHERE campaign.status = 'ENABLED' \" +\n    \"AND metrics.cost_micros > \" + (CONFIG.MIN_COST * 1000000) + \" \" +\n    \"AND metrics.clicks > \" + CONFIG.MIN_CLICKS + \" \" +\n    \"AND metrics.conversions <= \" + CONFIG.MAX_CONVERSIONS + \" \" +\n    \"AND segments.date DURING \" + CONFIG.DATE_RANGE;\n\n  var results = AdsApp.search(query);\n  var negatives = [];\n\n  while (results.hasNext()) {\n    var row = results.next();\n    var term = row.searchTermView.searchTerm;\n    var cost = row.metrics.costMicros / 1000000;\n\n    negatives.push({ term: term, campaign: row.campaign.name, cost: cost });\n\n    Logger.log('NEGATIVE: \"' + term + '\" | Cost: $' + cost.toFixed(2) +\n      ' | Clicks: ' + row.metrics.clicks + ' | Campaign: ' + row.campaign.name);\n\n    if (!CONFIG.DRY_RUN) {\n      var campaigns = AdsApp.campaigns()\n        .withCondition('Name = \"' + row.campaign.name + '\"').get();\n      if (campaigns.hasNext()) {\n        var campaign = campaigns.next();\n        if (CONFIG.NEGATIVE_MATCH_TYPE === 'EXACT') {\n          campaign.createNegativeKeyword('[' + term + ']');\n        } else if (CONFIG.NEGATIVE_MATCH_TYPE === 'PHRASE') {\n          campaign.createNegativeKeyword('\"' + term + '\"');\n        } else {\n          campaign.createNegativeKeyword(term);\n        }\n      }\n    }\n  }\n\n  Logger.log('Total negative keyword candidates: ' + negatives.length);\n}\n```\n\n### 4. Broken URL Checker\n\n```javascript\nfunction main() {\n  var CONFIG = {\n    ALERT_EMAIL: 'team@example.com',\n    PAUSE_BROKEN: false,\n    MIN_IMPRESSIONS: 10,\n    DATE_RANGE: 'LAST_7_DAYS'\n  };\n\n  var brokenUrls = [];\n\n  var ads = AdsApp.ads()\n    .withCondition('Status = ENABLED')\n    .withCondition('CampaignStatus = ENABLED')\n    .withCondition('AdGroupStatus = ENABLED')\n    .forDateRange(CONFIG.DATE_RANGE)\n    .withCondition('Impressions > ' + CONFIG.MIN_IMPRESSIONS)\n    .get();\n\n  while (ads.hasNext()) {\n    var ad = ads.next();\n    var url = ad.urls().getFinalUrl();\n    if (url) {\n      var status = checkUrl(url);\n      if (status !== 200) {\n        brokenUrls.push({\n          campaign: ad.getCampaign().getName(),\n          adGroup: ad.getAdGroup().getName(),\n          url: url,\n          httpStatus: status\n        });\n        Logger.log('BROKEN [' + status + ']: ' + url);\n        if (CONFIG.PAUSE_BROKEN) ad.pause();\n      }\n    }\n  }\n\n  if (brokenUrls.length > 0 && CONFIG.ALERT_EMAIL) {\n    var body = 'Broken URL Report\\n\\n';\n    for (var i = 0; i < brokenUrls.length; i++) {\n      body += 'HTTP ' + brokenUrls[i].httpStatus + ' | ' +\n        brokenUrls[i].campaign + ' | ' + brokenUrls[i].url + '\\n';\n    }\n    MailApp.sendEmail(CONFIG.ALERT_EMAIL,\n      '[Google Ads] ' + brokenUrls.length + ' Broken URLs', body);\n  }\n}\n\nfunction checkUrl(url) {\n  try {\n    var response = UrlFetchApp.fetch(url, {\n      muteHttpExceptions: true,\n      followRedirects: true,\n      validateHttpsCertificates: false\n    });\n    return response.getResponseCode();\n  } catch (e) {\n    return 0;\n  }\n}\n```\n\n### 5. Bid Adjustments by Time of Day (Dayparting)\n\n```javascript\nfunction main() {\n  var CONFIG = {\n    DATE_RANGE: 'LAST_30_DAYS',\n    MAX_BID_MODIFIER: 0.30,\n    MIN_BID_MODIFIER: -0.50,\n    MIN_CONVERSIONS: 5\n  };\n\n  var query = \"SELECT \" +\n    \"campaign.name, \" +\n    \"segments.hour, \" +\n    \"metrics.impressions, \" +\n    \"metrics.clicks, \" +\n    \"metrics.conversions, \" +\n    \"metrics.cost_micros \" +\n    \"FROM campaign \" +\n    \"WHERE campaign.status = 'ENABLED' \" +\n    \"AND segments.date DURING \" + CONFIG.DATE_RANGE;\n\n  var results = AdsApp.search(query);\n  var hourlyData = {};\n\n  while (results.hasNext()) {\n    var row = results.next();\n    var name = row.campaign.name;\n    var hour = row.segments.hour;\n\n    if (!hourlyData[name]) hourlyData[name] = {};\n    if (!hourlyData[name][hour]) {\n      hourlyData[name][hour] = { clicks: 0, conversions: 0, cost: 0 };\n    }\n    hourlyData[name][hour].clicks += row.metrics.clicks;\n    hourlyData[name][hour].conversions += row.metrics.conversions;\n    hourlyData[name][hour].cost += row.metrics.costMicros / 1000000;\n  }\n\n  for (var campaignName in hourlyData) {\n    var hours = hourlyData[campaignName];\n    var totalConv = 0, totalClicks = 0;\n    for (var h in hours) {\n      totalConv += hours[h].conversions;\n      totalClicks += hours[h].clicks;\n    }\n    var avgCvr = totalClicks > 0 ? totalConv / totalClicks : 0;\n\n    Logger.log('\\n' + campaignName + ' | Avg CVR: ' + (avgCvr * 100).toFixed(2) + '%');\n\n    for (var hour = 0; hour < 24; hour++) {\n      var data = hours[hour] || { clicks: 0, conversions: 0 };\n      var hourCvr = data.clicks > 0 ? data.conversions / data.clicks : 0;\n      var modifier = 0;\n\n      if (avgCvr > 0 && data.conversions >= CONFIG.MIN_CONVERSIONS) {\n        modifier = (hourCvr / avgCvr) - 1;\n        modifier = Math.max(CONFIG.MIN_BID_MODIFIER, Math.min(CONFIG.MAX_BID_MODIFIER, modifier));\n      }\n\n      Logger.log('  Hour ' + hour + ' | Conv: ' + data.conversions +\n        ' | Modifier: ' + (modifier > 0 ? '+' : '') + (modifier * 100).toFixed(0) + '%');\n    }\n  }\n}\n```\n\n### 6. Quality Score Tracker\n\nLog quality score history to Google Sheets (Google Ads does not retain QS history natively).\n\n```javascript\nfunction main() {\n  var CONFIG = {\n    SHEET_URL: 'YOUR_GOOGLE_SHEET_URL',\n    SHEET_NAME: 'QS History',\n    MIN_IMPRESSIONS: 50\n  };\n\n  var sheet = SpreadsheetApp.openByUrl(CONFIG.SHEET_URL)\n    .getSheetByName(CONFIG.SHEET_NAME);\n\n  if (sheet.getLastRow() === 0) {\n    sheet.appendRow([\n      'Date', 'Campaign', 'Ad Group', 'Keyword', 'Match Type',\n      'Quality Score', 'Expected CTR', 'Ad Relevance', 'Landing Page Exp',\n      'Impressions', 'Clicks', 'Cost', 'Conversions'\n    ]);\n  }\n\n  var today = Utilities.formatDate(new Date(), 'UTC', 'yyyy-MM-dd');\n\n  var query = \"SELECT \" +\n    \"campaign.name, ad_group.name, \" +\n    \"ad_group_criterion.keyword.text, ad_group_criterion.keyword.match_type, \" +\n    \"ad_group_criterion.quality_info.quality_score, \" +\n    \"ad_group_criterion.quality_info.search_predicted_ctr, \" +\n    \"ad_group_criterion.quality_info.creative_quality_score, \" +\n    \"ad_group_criterion.quality_info.post_click_quality_score, \" +\n    \"metrics.impressions, metrics.clicks, metrics.cost_micros, metrics.conversions \" +\n    \"FROM keyword_view \" +\n    \"WHERE campaign.status = 'ENABLED' \" +\n    \"AND ad_group.status = 'ENABLED' \" +\n    \"AND ad_group_criterion.status = 'ENABLED' \" +\n    \"AND metrics.impressions > \" + CONFIG.MIN_IMPRESSIONS + \" \" +\n    \"AND segments.date DURING LAST_7_DAYS\";\n\n  var results = AdsApp.search(query);\n  var rows = [];\n\n  while (results.hasNext()) {\n    var row = results.next();\n    var qi = row.adGroupCriterion.qualityInfo;\n    rows.push([\n      today, row.campaign.name, row.adGroup.name,\n      row.adGroupCriterion.keyword.text, row.adGroupCriterion.keyword.matchType,\n      qi.qualityScore || '', qi.searchPredictedCtr || '',\n      qi.creativeQualityScore || '', qi.postClickQualityScore || '',\n      row.metrics.impressions, row.metrics.clicks,\n      (row.metrics.costMicros / 1000000).toFixed(2), row.metrics.conversions\n    ]);\n  }\n\n  if (rows.length > 0) {\n    sheet.getRange(sheet.getLastRow() + 1, 1, rows.length, rows[0].length).setValues(rows);\n  }\n  Logger.log('Logged ' + rows.length + ' keywords');\n}\n```\n\n### 7. Ad Copy Testing (RSA Performance)\n\n```javascript\nfunction main() {\n  var CONFIG = {\n    MIN_IMPRESSIONS: 500,\n    DATE_RANGE: 'LAST_30_DAYS',\n    WINNER_CTR_LIFT: 0.20,\n    LOSER_CTR_DROP: -0.20,\n    ALERT_EMAIL: 'team@example.com'\n  };\n\n  var query = \"SELECT \" +\n    \"campaign.name, ad_group.name, ad_group_ad.ad.id, \" +\n    \"ad_group_ad.ad_strength, \" +\n    \"metrics.impressions, metrics.clicks, metrics.ctr, \" +\n    \"metrics.conversions, metrics.cost_micros \" +\n    \"FROM ad_group_ad \" +\n    \"WHERE campaign.status = 'ENABLED' AND ad_group.status = 'ENABLED' \" +\n    \"AND ad_group_ad.status = 'ENABLED' \" +\n    \"AND ad_group_ad.ad.type = 'RESPONSIVE_SEARCH_AD' \" +\n    \"AND metrics.impressions > \" + CONFIG.MIN_IMPRESSIONS + \" \" +\n    \"AND segments.date DURING \" + CONFIG.DATE_RANGE;\n\n  var results = AdsApp.search(query);\n  var adGroups = {};\n\n  while (results.hasNext()) {\n    var row = results.next();\n    var agKey = row.campaign.name + ' > ' + row.adGroup.name;\n    if (!adGroups[agKey]) adGroups[agKey] = [];\n    adGroups[agKey].push({\n      adId: row.adGroupAd.ad.id,\n      strength: row.adGroupAd.adStrength,\n      impressions: row.metrics.impressions,\n      clicks: row.metrics.clicks,\n      ctr: row.metrics.ctr,\n      conversions: row.metrics.conversions\n    });\n  }\n\n  var winners = [], losers = [];\n\n  for (var agKey in adGroups) {\n    var ads = adGroups[agKey];\n    if (ads.length < 2) continue;\n\n    var totalClicks = 0, totalImpr = 0;\n    for (var i = 0; i < ads.length; i++) {\n      totalClicks += ads[i].clicks;\n      totalImpr += ads[i].impressions;\n    }\n    var avgCtr = totalImpr > 0 ? totalClicks / totalImpr : 0;\n\n    for (var j = 0; j < ads.length; j++) {\n      var ctrLift = avgCtr > 0 ? (ads[j].ctr - avgCtr) / avgCtr : 0;\n      if (ctrLift >= CONFIG.WINNER_CTR_LIFT) winners.push({ adGroup: agKey, ad: ads[j], lift: ctrLift });\n      else if (ctrLift <= CONFIG.LOSER_CTR_DROP) losers.push({ adGroup: agKey, ad: ads[j], lift: ctrLift });\n    }\n  }\n\n  Logger.log('Winners: ' + winners.length + ' | Losers: ' + losers.length);\n}\n```\n\n### 8. Budget Allocation by ROAS\n\n```javascript\nfunction main() {\n  var CONFIG = {\n    TOTAL_DAILY_BUDGET: 500,\n    MIN_BUDGET: 20,\n    MAX_BUDGET: 200,\n    DATE_RANGE: 'LAST_14_DAYS',\n    CAMPAIGN_LABEL: 'Auto-Budget',\n    MIN_CONVERSIONS: 3,\n    DRY_RUN: true\n  };\n\n  var campaigns = AdsApp.campaigns()\n    .withCondition('Status = ENABLED')\n    .withCondition('LabelNames CONTAINS_ANY [\"' + CONFIG.CAMPAIGN_LABEL + '\"]')\n    .forDateRange(CONFIG.DATE_RANGE)\n    .get();\n\n  var campaignData = [];\n  while (campaigns.hasNext()) {\n    var campaign = campaigns.next();\n    var stats = campaign.getStatsFor(CONFIG.DATE_RANGE);\n    var cost = stats.getCost();\n    var convValue = stats.getConversionValue();\n\n    campaignData.push({\n      campaign: campaign,\n      name: campaign.getName(),\n      cost: cost,\n      roas: cost > 0 ? convValue / cost : 0,\n      conversions: stats.getConversions(),\n      currentBudget: campaign.getBudget().getAmount()\n    });\n  }\n\n  campaignData.sort(function(a, b) { return b.roas - a.roas; });\n\n  var totalRoas = 0;\n  for (var i = 0; i < campaignData.length; i++) {\n    if (campaignData[i].conversions >= CONFIG.MIN_CONVERSIONS) totalRoas += campaignData[i].roas;\n  }\n\n  for (var j = 0; j < campaignData.length; j++) {\n    var c = campaignData[j];\n    var newBudget = (c.conversions >= CONFIG.MIN_CONVERSIONS && totalRoas > 0)\n      ? (c.roas / totalRoas) * CONFIG.TOTAL_DAILY_BUDGET\n      : CONFIG.MIN_BUDGET;\n\n    newBudget = Math.max(CONFIG.MIN_BUDGET, Math.min(CONFIG.MAX_BUDGET, Math.round(newBudget * 100) / 100));\n\n    Logger.log(c.name + ' | ROAS: ' + c.roas.toFixed(2) +\n      ' | Current: $' + c.currentBudget.toFixed(2) + ' | New: $' + newBudget.toFixed(2));\n\n    if (!CONFIG.DRY_RUN) c.campaign.getBudget().setAmount(newBudget);\n  }\n\n  if (CONFIG.DRY_RUN) Logger.log('DRY RUN — no budgets changed.');\n}\n```\n\n### 9. Anomaly Detection\n\n```javascript\nfunction main() {\n  var CONFIG = {\n    LOOKBACK_DAYS: 14,\n    SPIKE_THRESHOLD: 2.0,\n    DROP_THRESHOLD: 0.5,\n    ALERT_EMAIL: 'team@example.com',\n    METRICS: ['impressions', 'clicks', 'cost_micros', 'conversions']\n  };\n\n  var today = new Date();\n  var yesterday = new Date(today.getTime() - 86400000);\n  var lookbackStart = new Date(today.getTime() - (CONFIG.LOOKBACK_DAYS + 1) * 86400000);\n\n  var yesterdayStr = Utilities.formatDate(yesterday, 'UTC', 'yyyyMMdd');\n  var lookbackStr = Utilities.formatDate(lookbackStart, 'UTC', 'yyyyMMdd');\n\n  var query = \"SELECT campaign.name, \" +\n    \"metrics.impressions, metrics.clicks, metrics.cost_micros, \" +\n    \"metrics.conversions, segments.date \" +\n    \"FROM campaign WHERE campaign.status = 'ENABLED' \" +\n    \"AND segments.date BETWEEN '\" +\n      lookbackStr.substring(0, 4) + '-' + lookbackStr.substring(4, 6) + '-' + lookbackStr.substring(6, 8) +\n    \"' AND '\" +\n      yesterdayStr.substring(0, 4) + '-' + yesterdayStr.substring(4, 6) + '-' + yesterdayStr.substring(6, 8) + \"'\";\n\n  var results = AdsApp.search(query);\n  var campaignDays = {};\n\n  while (results.hasNext()) {\n    var row = results.next();\n    var name = row.campaign.name;\n    var date = row.segments.date;\n    if (!campaignDays[name]) campaignDays[name] = {};\n    campaignDays[name][date] = {\n      impressions: row.metrics.impressions,\n      clicks: row.metrics.clicks,\n      cost_micros: row.metrics.costMicros,\n      conversions: row.metrics.conversions\n    };\n  }\n\n  var alerts = [];\n  var yesterdayDate = Utilities.formatDate(yesterday, 'UTC', 'yyyy-MM-dd');\n\n  for (var campaignName in campaignDays) {\n    var days = campaignDays[campaignName];\n    var yesterdayData = days[yesterdayDate];\n    if (!yesterdayData) continue;\n\n    for (var m = 0; m < CONFIG.METRICS.length; m++) {\n      var metric = CONFIG.METRICS[m];\n      var sum = 0, count = 0;\n      for (var d in days) {\n        if (d !== yesterdayDate) { sum += days[d][metric] || 0; count++; }\n      }\n      if (count === 0) continue;\n      var avg = sum / count;\n      var current = yesterdayData[metric] || 0;\n\n      if (avg > 0) {\n        var ratio = current / avg;\n        if (ratio >= CONFIG.SPIKE_THRESHOLD || ratio <= CONFIG.DROP_THRESHOLD) {\n          alerts.push({\n            campaign: campaignName, metric: metric,\n            type: ratio >= CONFIG.SPIKE_THRESHOLD ? 'SPIKE' : 'DROP',\n            current: current, average: avg, ratio: ratio\n          });\n        }\n      }\n    }\n  }\n\n  if (alerts.length > 0) {\n    var body = 'Anomaly Report — ' + yesterdayDate + '\\n\\n';\n    for (var a = 0; a < alerts.length; a++) {\n      body += alerts[a].type + ': ' + alerts[a].campaign +\n        ' | ' + alerts[a].metric + ' | ' + alerts[a].ratio.toFixed(2) + 'x avg\\n';\n    }\n    MailApp.sendEmail(CONFIG.ALERT_EMAIL, '[Google Ads] ' + alerts.length + ' Anomalies', body);\n  }\n}\n```\n\n### 10. Landing Page Performance\n\n```javascript\nfunction main() {\n  var CONFIG = {\n    DATE_RANGE: 'LAST_30_DAYS',\n    MIN_CLICKS: 50,\n    SHEET_URL: 'YOUR_GOOGLE_SHEET_URL',\n    SHEET_NAME: 'Landing Pages'\n  };\n\n  var query = \"SELECT \" +\n    \"landing_page_view.unexpanded_final_url, \" +\n    \"metrics.clicks, metrics.impressions, \" +\n    \"metrics.cost_micros, metrics.conversions, \" +\n    \"metrics.conversions_value \" +\n    \"FROM landing_page_view \" +\n    \"WHERE segments.date DURING \" + CONFIG.DATE_RANGE + \" \" +\n    \"AND metrics.clicks > \" + CONFIG.MIN_CLICKS + \" \" +\n    \"ORDER BY metrics.clicks DESC\";\n\n  var results = AdsApp.search(query);\n  var pages = [];\n\n  while (results.hasNext()) {\n    var row = results.next();\n    var cost = row.metrics.costMicros / 1000000;\n    var cvr = row.metrics.clicks > 0 ? row.metrics.conversions / row.metrics.clicks : 0;\n    var roas = cost > 0 ? row.metrics.conversionsValue / cost : 0;\n\n    pages.push([\n      row.landingPageView.unexpandedFinalUrl,\n      row.metrics.clicks, row.metrics.impressions,\n      cost.toFixed(2), row.metrics.conversions,\n      row.metrics.conversionsValue.toFixed(2),\n      (cvr * 100).toFixed(2) + '%', roas.toFixed(2)\n    ]);\n  }\n\n  if (CONFIG.SHEET_URL && pages.length > 0) {\n    var sheet = SpreadsheetApp.openByUrl(CONFIG.SHEET_URL)\n      .getSheetByName(CONFIG.SHEET_NAME);\n    sheet.clear();\n    sheet.appendRow(['URL', 'Clicks', 'Impressions', 'Cost', 'Conversions',\n      'Conv Value', 'CVR', 'ROAS']);\n    sheet.getRange(2, 1, pages.length, pages[0].length).setValues(pages);\n    Logger.log('Exported ' + pages.length + ' landing pages');\n  }\n}\n```\n\n## Google Sheets Integration\n\n### Opening a Spreadsheet\n\n```javascript\nvar ss = SpreadsheetApp.openByUrl('https://docs.google.com/spreadsheets/d/SHEET_ID/edit');\nvar ss = SpreadsheetApp.openById('SHEET_ID');\nvar sheet = ss.getSheetByName('Data');\n```\n\n### Reading Data\n\n```javascript\nvar values = sheet.getRange('A1:D10').getValues();  // 2D array\nvar allData = sheet.getDataRange().getValues();\nvar value = sheet.getRange('B2').getValue();         // Single cell\n```\n\n### Writing Data\n\n```javascript\nsheet.getRange('A1').setValue('Campaign Name');\nsheet.appendRow(['2025-02-11', 'Brand', 150, 12]);\n\n// Batch write (much faster than appendRow in a loop)\nvar data = [['Campaign', 'Clicks'], ['Brand', 150], ['Generic', 300]];\nsheet.getRange(1, 1, data.length, data[0].length).setValues(data);\n```\n\n## Email and Notifications\n\n```javascript\n// Simple email\nMailApp.sendEmail('team@example.com', 'Subject', 'Body text');\n\n// HTML email\nMailApp.sendEmail({\n  to: 'team@example.com',\n  subject: '[Google Ads] Weekly Report',\n  htmlBody: '<h1>Report</h1><p>Details here.</p>'\n});\n```\n\n## Scheduling\n\nSet up in **Tools & Settings > Bulk Actions > Scripts**. Available frequencies: Hourly, Daily, Weekly, Monthly.\n\n**Recommended schedules:**\n- Budget pacing: Daily or hourly\n- Negative keyword mining: Weekly\n- Quality score tracking: Daily\n- Anomaly detection: Daily (morning)\n- Broken URL checks: Weekly\n- Dashboards: Daily\n\n## Error Handling\n\n```javascript\nfunction main() {\n  try {\n    processKeywords();\n  } catch (e) {\n    Logger.log('ERROR: ' + e.message + '\\nStack: ' + e.stack);\n    MailApp.sendEmail('team@example.com',\n      '[Google Ads Script] Error', 'Error: ' + e.message);\n  }\n}\n```\n\n**Execution time limits:** 30 minutes (single account), 60 minutes (MCC). Handle gracefully:\n\n```javascript\nvar startTime = new Date().getTime();\nvar MAX_RUNTIME = 25 * 60 * 1000;  // 25 min buffer\n\nwhile (iterator.hasNext()) {\n  if (new Date().getTime() - startTime > MAX_RUNTIME) {\n    Logger.log('Approaching time limit. Stopping.');\n    break;\n  }\n  // Process...\n}\n```\n\n## MCC Scripts\n\n### Basic MCC Script\n\n```javascript\nfunction main() {\n  var accounts = AdsManagerApp.accounts()\n    .withCondition('Impressions > 0')\n    .forDateRange('LAST_7_DAYS')\n    .withLimit(50)\n    .get();\n\n  while (accounts.hasNext()) {\n    var account = accounts.next();\n    AdsManagerApp.select(account);\n    Logger.log('Processing: ' + account.getName());\n    // Now AdsApp refers to this account\n  }\n}\n```\n\n### Parallel Processing with executeInParallel()\n\n```javascript\nfunction main() {\n  var accounts = AdsManagerApp.accounts()\n    .withCondition('Impressions > 0')\n    .forDateRange('LAST_7_DAYS')\n    .get();\n\n  accounts.executeInParallel('processAccount', 'aggregateResults');\n}\n\nfunction processAccount() {\n  var account = AdsApp.currentAccount();\n  var result = { name: account.getName(), id: account.getCustomerId(), campaigns: [] };\n\n  var campaigns = AdsApp.campaigns()\n    .withCondition('Status = ENABLED')\n    .forDateRange('LAST_7_DAYS').get();\n\n  while (campaigns.hasNext()) {\n    var c = campaigns.next();\n    var s = c.getStatsFor('LAST_7_DAYS');\n    result.campaigns.push({\n      name: c.getName(), clicks: s.getClicks(),\n      cost: s.getCost(), conversions: s.getConversions()\n    });\n  }\n\n  return JSON.stringify(result);  // Must return string\n}\n\nfunction aggregateResults(results) {\n  for (var i = 0; i < results.length; i++) {\n    if (results[i].getStatus() === 'OK') {\n      var data = JSON.parse(results[i].getReturnValue());\n      Logger.log(data.name + ': ' + data.campaigns.length + ' campaigns');\n    } else {\n      Logger.log('Error: ' + results[i].getError());\n    }\n  }\n}\n```\n\n**executeInParallel() rules:**\n- Per-account function must return a string (`JSON.stringify()`)\n- Callback receives array of `ExecutionResult` objects (`.getStatus()`, `.getReturnValue()`, `.getError()`, `.getCustomerId()`)\n- Max 50 accounts per call\n- Each account execution has 30-minute limit\n\n## Best Practices\n\n1. **Preview mode first** — always click Preview before Run\n2. **Log before modifying** — `Logger.log('Pausing: ' + keyword.getText())` before `keyword.pause()`\n3. **DRY_RUN flag** — every modifying script should have `DRY_RUN: true` in config\n4. **Incremental rollout** — start with one campaign, expand after validation\n5. **Error notifications** — send emails on failure, do not rely on checking logs\n6. **Use labels** — target scripts via labels instead of hardcoded campaign names\n7. **Batch Sheets writes** — use `setValues()` with 2D arrays, not `appendRow()` in loops\n8. **Document scripts** — add a comment block with purpose, schedule, author, config explanation\n\n## Limitations\n\n| Limitation | Details |\n|-----------|---------|\n| Execution time | 30 min (single), 60 min (MCC) |\n| No async/await | Synchronous execution only |\n| Limited ES6 | No `import`/`export`, no `class` |\n| No Display/Video APIs | Limited support via scripts |\n| No Performance Max | Read-only (limited reporting) |\n| URL fetch | 50 MB response limit, 60s timeout |\n| MailApp | 100 recipients/day |\n| No npm/modules | Cannot import external libraries |\n| executeInParallel() | Max 50 accounts per call |\n\n## Resources\n\n- **Google Ads Scripts Docs:** https://developers.google.com/google-ads/scripts/docs/overview\n- **AdsApp Reference:** https://developers.google.com/google-ads/scripts/docs/reference/adsapp/adsapp\n- **GAQL Reference:** https://developers.google.com/google-ads/api/docs/query/overview\n- **Script Examples:** https://developers.google.com/google-ads/scripts/docs/solutions\n- **Full Cogny Docs:** https://cogny.com/docs/google-ads-scripts","tags":["google","ads","scripts","claude","code","marketing","skills","cognyai","agent-skills","ai-agents","claude-code","claude-skills"],"capabilities":["skill","source-cognyai","skill-google-ads-scripts","topic-agent-skills","topic-ai-agents","topic-claude-code","topic-claude-skills","topic-cluade-mcp","topic-cursor","topic-geo","topic-growth-hacking","topic-llm","topic-marketing","topic-mcp","topic-seo"],"categories":["claude-code-marketing-skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/cognyai/claude-code-marketing-skills/google-ads-scripts","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add cognyai/claude-code-marketing-skills","source_repo":"https://github.com/cognyai/claude-code-marketing-skills","install_from":"skills.sh"}},"qualityScore":"0.471","qualityRationale":"deterministic score 0.47 from registry signals: · indexed on github topic:agent-skills · 42 github stars · SKILL.md body (33,559 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-05-01T06:56:26.600Z","embedding":null,"createdAt":"2026-04-18T22:23:35.125Z","updatedAt":"2026-05-01T06:56:26.600Z","lastSeenAt":"2026-05-01T06:56:26.600Z","tsv":"'-0.20':1848 '-0.50':1473 '-02':2661 '-11':2662 '/docs/google-ads-scripts':68,3177 '/google-ads-scripts':70,73,80,88,97,104,110,116 '/google-ads/api/docs/query/overview':3166 '/google-ads/scripts/docs/overview':3156 '/google-ads/scripts/docs/reference/adsapp/adsapp':3161 '/google-ads/scripts/docs/solutions':3171 '/spreadsheets/d/sheet_id/edit'');':2619 '0':897,1004,1049,1175,1390,1403,1447,1527,1529,1531,1559,1561,1578,1581,1594,1603,1605,1609,1612,1615,1618,1643,1647,1695,1807,1814,1946,1948,1952,1967,1970,1974,1981,1987,2099,2102,2117,2121,2138,2152,2273,2283,2355,2365,2367,2380,2384,2394,2397,2428,2439,2543,2546,2550,2553,2573,2598,2688,2835,2871,2935 '0.005':842 '0.01':504 '0.15':980 '0.20':984,1844 '0.30':1469 '0.5':2213 '1':825,1003,1022,1089,1098,1625,1810,1811,2240,2595,2684,2685,2995 '10':17,1178,1323,2468 '100':513,639,845,924,1588,1645,2169,2170,2564,3135 '1000':2802 '10000':976 '1000000':730,1218,1249,1547,1801,2539 '12':2665 '14':586,2043,2207 '150':2664,2680 '2':732,926,965,1110,1113,1116,1138,1142,1146,1149,1262,1590,1803,1942,2175,2178,2181,2456,2559,2562,2566,2568,2594,3004 '2.0':2210 '20':2036 '200':1368,2039 '2025':2660 '20250101':449,609 '20250131':450,610 '24':1596 '25':2800,2803 '2d':2638,3069 '3':1159,2052,3013 '30':240,350,446,458,507,589,710,772,849,1182,1464,1839,2480,2782,2990,3093 '300':2682 '4':839,1306,2274,2276,2284,2286,3027 '5':546,1448,1476,3037 '50':1172,1684,2484,2841,2982,3128,3145 '500':1835,2033 '6':1648,2277,2279,2287,2289,3050 '60':244,352,2786,2801,3096 '60s':3132 '7':573,635,650,1327,1772,1822,2838,2874,2900,2912,3062 '8':2020,2280,2290,3075 '86400000':2232,2241 '9':2197 'a.roas':2114 'a1':2635,2655 'access':482,667,801 'account':213,325,332,337,339,392,405,410,415,2785,2831,2846,2849,2858,2867,2883,2964,2983,2987,3146 'account-level':404,409,414 'account.getcustomerid':2890 'account.getname':2852,2888 'accounts.executeinparallel':2877 'accounts.hasnext':2844 'accounts.next':2847 'action':168,2724 'actual':1111 'ad':3,6,27,34,40,127,141,250,257,266,283,285,294,399,408,660,672,1154,1332,1354,1423,1660,1699,1708,1823,1867,1869,1883,1937,1957,1961,1982,1996,1997,2010,2011,2464,2710,2774,3151 'ad.getadgroup':1374 'ad.getcampaign':1371 'ad.pause':1387 'ad.urls':1358 'ad_group.name':862,1200,1731,1856 'ad_group.status':877,1759,1874 'ad_group_ad.ad':1858 'ad_group_ad.ad.id':1857 'ad_group_ad.ad.type':1880 'ad_group_ad.status':1877 'ad_group_criterion.keyword.match':858,1733 'ad_group_criterion.keyword.text':756,857,1732 'ad_group_criterion.quality_info.creative':1740 'ad_group_criterion.quality_info.post':1743 'ad_group_criterion.quality_info.quality':757,860,1735 'ad_group_criterion.quality_info.search':1737 'ad_group_criterion.status':880,1762 'add':3078 'adgroup':398,403,1373,1898,1909,1911,1913,1935,1938,1994,2008 'adgroupnam':939 'adgroupstatus':500,1341 'adid':1916 'adjust':1450 'ads.hasnext':1352 'ads.length':1941,1954,1976 'ads.next':1355 'adsapp':11,41,379,383,396,2854,3157 'adsapp.ads':1333 'adsapp.campaigns':1033,1272,2058,2894 'adsapp.currentaccount':2884 'adsapp.keywords':492,629,934 'adsapp.report':735,754,793 'adsapp.search':226,677,719,792,893,1234,1499,1776,1895,2293,2527 'adsmanagerapp':347 'adsmanagerapp.accounts':2832,2868 'adsmanagerapp.select':2848 'aggregateresult':2879,2930 'agkey':1905,1910,1912,1914,1933,1939,1995,2009 'alert':120,307,977,981,985,1127,1156,1315,1849,2214,2326,2444,2447,2450,2453 'alerts.length':2427,2441,2465 'alerts.push':2409 'alldata':2641 'alloc':2022 'alway':202,2999 'anomali':117,119,2198,2431,2466,2747 'answer':169 'api':272,673,3113 'appendrow':2671,3072 'appli':964 'approach':2816 'area':190 'argument':186 'array':2639,2973,3070 'arrow':367 'asc':615 'ask':157 'async':372 'async/await':3100 'audienc':401 'author':3085 'auto':2048 'auto-budget':2047 'autom':10,37 'avail':2726 'averag':2422 'avg':1585,2387,2396,2401,2423,2458 'avgctr':1965,1980,1985,1986 'avgcvr':1576,1587,1617,1624 'await':373 'b':2111 'b.roas':2113 'b2':2647 'base':566 'basic':2824 'batch':230,2666,3063 'best':62,152,744,814,2993 'bid':1449,1467,1471,1629,1633 'biddingstrategi':423 'block':3081 'bodi':1124,1158,1394,1407,1427,2430,2443,2467,2701 'brand':536,557,2663,2679 'break':2820 'broken':1307,1319,1381,1386,1395,1425,2751 'brokenurl':1330,1409,1412,1415 'brokenurls.length':1389,1405,1424 'brokenurls.push':1369 'budget':74,76,422,966,975,991,1012,1125,1136,1155,2021,2032,2035,2038,2049,2157,2159,2163,2166,2195,2734 'budget-monitor':990 'buffer':2805 'built':811 'built-in':810 'bulk':746,819,2723 'busi':592 'buy':541 'c':2143,2906 'c.campaign.getbudget':2185 'c.conversions':2148 'c.currentbudget.tofixed':2177 'c.getname':2916 'c.getstatsfor':2910 'c.name':2172 'c.roas':2153 'c.roas.tofixed':2174 'call':2985,3148 'callback':2971 'campaign':282,397,702,927,988,1032,1055,1253,1265,1271,1280,1370,1414,1488,1698,2045,2057,2077,2091,2092,2265,2410,2449,2657,2677,2891,2893,2953,3033,3060 'campaign.createnegativekeyword':1287,1295,1298 'campaign.getbudget':2106 'campaign.getname':1069,2094 'campaign.getstatsfor':1059,2081 'campaign.name':694,863,1199,1480,1730,1855,2257 'campaign.status':695,704,766,874,1211,1490,1756,1871,2267 'campaigndata':2073,2126,2132,2144 'campaigndata.length':2123,2140 'campaigndata.push':2090 'campaigndata.sort':2108 'campaignday':2296,2309,2311,2313,2340,2343 'campaigndetail':1051 'campaigndetails.push':1067 'campaignnam':554,942,1550,1556,1584,2338,2344,2411 'campaigns.hasnext':1053,1278,2075,2904 'campaigns.next':1056,1281,2078,2907 'campaignstatus':497,1338 'candid':1304 'cannot':3139 'cap':619 'capabl':196,278,743 'case':528 'catch':1444,2764 'cell':2650 'chang':215,963,2196 'check':2753,3048 'checker':1309 'checkurl':1364,1429 'class':376,3110 'click':655,1177,1222,1263,1526,1535,1574,1602,1714,1744,1922,1959,2219,2318,2483,2520,2585,2678,2917,3000 'code':54,146,176 'cogni':3173 'cogny.com':67,3176 'cogny.com/docs/google-ads-scripts':66,3175 'collect':472 'comment':3080 'common':49,198,424,822 'complet':30,144 'complex':228 'condit':516,567 'config':835,973,1169,1314,1460,1671,1832,2029,2204,2476,3026,3086 'config.alert':1090,1099,1151,1391,1420,2461 'config.campaign':1041,2066 'config.date':889,1230,1344,1495,1891,2069,2082,2515 'config.drop':2407 'config.dry':930,957,1268,2183,2189 'config.lookback':2238 'config.loser':2004 'config.max':1225,1632,2165 'config.metrics':2361 'config.metrics.length':2357 'config.min':884,911,915,1216,1221,1348,1620,1628,1766,1886,2129,2149,2158,2162,2519 'config.monthly':1011 'config.monthly_budget.tofixed':1137 'config.negative':1283,1291 'config.pause':1385 'config.sheet':1688,1691,2570,2577,2580 'config.spike':2404,2416 'config.total':2155 'config.winner':1990 'const':366 'contain':520,523,526,535,549,1039,2064 'continu':1943,2351,2385 'control':255 'conv':1639,2589 'convers':733,1072,1174,1226,1475,1528,1540,1570,1604,1621,1716,1926,2051,2103,2128,2130,2150,2222,2323,2588,2921 'convvalu':2088,2100 'copi':1824 'cost':728,1171,1217,1247,1255,1256,1260,1530,1545,1715,2085,2095,2096,2098,2101,2220,2320,2537,2549,2552,2587,2919 'cost.tofixed':1261,2558 'count':2366,2381,2383,2389 'ctr':503,841,907,914,916,922,923,1707,1739,1842,1846,1924,1984,1991,2005 'ctrlift':1979,1989,2000,2003,2014 'current':2176,2391,2400,2420,2421 'currentbudget':2105 'custom':605 'cvr':1586,2541,2563,2591 'd':2370,2374,2378 'd10':2636 'daili':320,2031,2156,2729,2736,2746,2749,2756 'dashboard':302,821,2755 'data':290,481,747,1599,2628,2630,2652,2676,2687,2691,2945 'data.campaigns.length':2952 'data.clicks':1608,1611 'data.conversions':1610,1619,1640 'data.length':2686 'data.name':2951 'date':442,606,846,996,1000,1019,1179,1324,1461,1697,1721,1836,2040,2226,2230,2236,2306,2315,2477,2795,2810 'daterang':561 'day':447,459,508,574,587,590,636,651,711,773,850,1105,1183,1328,1454,1465,1773,1840,2044,2206,2239,2342,2347,2372,2377,2481,2839,2875,2901,2913 'dayofmonth':1007,1014,1081,1106,1132 'daypart':1455 'daysinmonth':998,1013,1082,1107,1134 'dd':1726,2335 'debug':137 'desc':511,616,716,2524 'detail':2715,3090 'detect':118,2199,2748 'developers.google.com':3155,3160,3165,3170 'developers.google.com/google-ads/api/docs/query/overview':3164 'developers.google.com/google-ads/scripts/docs/overview':3154 'developers.google.com/google-ads/scripts/docs/reference/adsapp/adsapp':3159 'developers.google.com/google-ads/scripts/docs/solutions':3169 'direct':262,775 'display/video':3112 'doc':65,3153,3174 'docs.google.com':2618 'docs.google.com/spreadsheets/d/sheet_id/edit'');':2617 'document':3076 'dri':205,851,960,1188,2053,2192,3014,3022 'drop':1847,2006,2211,2419 'e':1445,2765 'e.message':2768,2778 'e.stack':2770 'els':1095,1289,1297,2001,2954 'email':306,986,1152,1316,1392,1421,1850,2215,2462,2692,2697,2704,3041 'enabl':495,498,501,632,705,767,875,878,881,1036,1212,1336,1339,1342,1491,1757,1760,1763,1872,1875,1878,2061,2268,2897 'entiti':394,425,471,518 'entity.enable':432 'entity.getname':436 'entity.getstatsfor':444,448,456 'entity.isenabled':429 'entity.ispaused':430 'entity.isremoved':431 'entity.pause':433 'entity.remove':434 'entity.setname':437 'entri':342 'error':149,222,2757,2767,2776,2777,2956,3038 'es5':361 'es6':364,3105 'everi':470,485,3017 'exact':1187,1286 'exampl':3168 'execut':242,348,355,2779,2988,3091,3102 'executeinparallel':358,2862,2960,3143 'executionresult':2975 'exp':1712 'expand':3034 'expect':1108,1706 'expectedspend':1010,1077 'expectedspend.tofixed':1109,1141 'expert':129 'explan':3087 'export':375,742,774,808,820,2603,3108 'exporttosheet':806,813 'extens':287,402 'extern':270,3141 'failur':3043 'fals':1320,1441 'faster':2669 'featur':329,791 'fetch':3127 'field':800 'filter':517 'final':2499 'find':160 'first':2998 'firstofmonth':1016,1044,1060 'flag':207,3016 'focus':187 'followredirect':1438 'fordaterang':505,560,608,633,1043,1343,2068,2836,2872,2898 'frequenc':2727 'full':64,71,670,3172 'function':368,689,832,970,1166,1311,1428,1457,1668,1829,2026,2109,2201,2473,2760,2828,2864,2880,2929,2965 'fundament':480 'gaql':15,47,111,112,225,292,656,3162 'generic':559,2681 'get':514,640,944,1046,1276,1350,2071,2842,2876,2902 'getactivesheet':785 'getamount':2107 'getcustomerid':2980 'getdat':1005 'geterror':2959,2979 'getfinalurl':1359 'getnam':1372,1375 'getreturnvalu':2949,2978 'getsheetbynam':1690,2579 'getstatus':2942,2977 'gettim':2796,2811 'getvalu':2637,2643,2648 'googl':2,5,26,33,39,55,126,140,231,249,256,265,293,299,659,671,777,1153,1422,1657,1659,1675,2463,2488,2607,2709,2773,3150 'google-ads-script':1 'grace':2790 'group':284,1700,1868 'h':1564,1569,1573 'handl':150,223,2758,2789 'hardcod':3059 'help':134 'hierarchi':395 'histori':1655,1665,1681 'hour':319,1512,1522,1525,1534,1539,1544,1554,1566,1568,1572,1593,1595,1597,1600,1601,1637,1638,2728,2738 'hourcvr':1607,1623 'hourlydata':1502,1515,1517,1520,1523,1532,1537,1542,1552,1555 'html':2703 'htmlbodi':2713 'http':311,1408 'httpstatus':1378,1411 'id':2624,2889 'ignor':527 'import':374,3107,3140 'impress':510,638,844,885,1322,1347,1349,1683,1713,1767,1834,1887,1920,1963,2218,2316,2586,2834,2870 'includ':203 'increment':3028 'individu':818 'insid':263 'instead':3057 'instruct':122 'integr':25,57,297,750,2609 'interfac':268 'iter':46,108,469,477,624,628,681,797,804 'iterator.hasnext':642,2807 'iterator.next':645 'j':1973,1975,1977,1983,1998,2012,2137,2139,2141,2145 'javascript':9,36,175,259,360,427,489,532,604,626,686,688,751,831,969,1165,1310,1456,1667,1828,2025,2200,2472,2613,2631,2653,2695,2759,2791,2827,2863 'json.parse':2946 'json.stringify':2924,2970 'key':200,273,277 'keyword':82,86,286,400,413,491,644,763,830,871,933,952,1163,1303,1701,1753,1821,2740 'keyword.getstatsfor':648 'keyword.gettext':653,3010 'keyword.pause':3012 'keywords.hasnext':946 'keywords.next':947 'label':421,989,1042,2046,2067,3052,3056 'labelnam':548,1038,2063 'land':1710,2469,2493,2509,2605 'landing_page_view.unexpanded':2498 'languag':296,359,662 'last':445,457,506,572,583,585,588,591,594,600,634,649,709,771,848,1181,1326,1463,1771,1838,2042,2479,2837,2873,2899,2911 'length':1815,2599,2689 'let':252,365 'level':406,411,416 'librari':3142 'lift':1843,1992,1999,2013 'like':666 'limit':243,349,618,2781,2818,2992,3088,3089,3104,3114,3124,3131 'liter':370 'log':94,214,304,1191,1652,1819,3005,3049 'logger.log':652,726,917,950,959,1104,1257,1300,1380,1582,1636,1818,2015,2171,2191,2602,2766,2815,2850,2950,2955,3008 'lookback':2205 'lookbackstart':2234,2251 'lookbackstr':2249 'lookbackstr.substring':2272,2275,2278 'loop':2674,3074 'loser':1845,1930,2018 'losers.length':2019 'losers.push':2007 'low':828 'low-perform':827 'm':2354,2356,2358,2362 'mailapp':309,3134 'mailapp.sendemail':1150,1419,2460,2698,2705,2771 'main':344,345,690,833,971,1167,1312,1458,1669,1830,2027,2202,2474,2761,2829,2865 'make':217,310 'manual':809 'match':530,540,1185,1284,1292,1702 'math.max':1627,2161 'math.min':1631,2164 'math.round':2167 'max':1173,1466,2037,2798,2813,2981,3120,3144 'mb':3129 'mcc':21,58,98,100,247,327,333,341,2788,2822,2825,3098 'method':426,453,488 'metric':565,2217,2360,2379,2393,2412,2413,2452 'metrics-bas':564 'metrics.clicks':697,760,865,1202,1220,1483,1748,1861,2259,2501,2518,2523 'metrics.conversions':700,761,869,1205,1224,1484,1751,1863,2262,2505,2506 'metrics.cost':698,714,867,1203,1214,1485,1749,1864,2260,2503 'metrics.ctr':866,1862 'metrics.impressions':696,759,864,883,1201,1482,1747,1765,1860,1885,2258,2502 'micro':699,715,868,1204,1215,1486,1750,1865,2221,2261,2321,2504 'min':245,836,840,843,1170,1176,1321,1470,1474,1682,1833,2034,2050,2482,2804,3094,3097 'mine':2741 'miner':87,1164 'minut':241,351,353,2783,2787,2991 'mm':1725,2334 'mode':2997 'model':13,43,381 'modifi':211,281,1468,1472,1614,1622,1626,1630,1634,1635,1641,1642,1644,3007,3018 'mon':581 'monitor':78,552,968,992 'month':322,599,601,974,2731 'morn':2750 'much':2668 'must':2926,2966 'mutehttpexcept':1436 'n':1128,1398,1399,1418,1583,2434,2435,2459 'nactual':1143 'name':435,439,534,538,1068,1274,1509,1516,1518,1521,1524,1533,1538,1543,1679,1692,2093,2303,2310,2312,2314,2492,2581,2658,2887,2915,3061 'nativ':1666 'nday':1131 'need':276 'negat':81,85,1162,1184,1237,1258,1302,2739 'negativekeyword':418 'negatives.length':1305 'negatives.push':1250 'new':438,995,999,1018,1720,2179,2225,2229,2235,2794,2809 'newbudget':2147,2160,2168,2187 'newbudget.tofixed':2180 'nexpect':1139 'nmonth':1135 'notif':2694,3039 'npm/modules':3138 'nproject':1147 'nstack':2769 'nstatus':1129 'number':621 'oauth':275 'object':12,42,380,384,452,687,740,799,2976 'ok':2943 'one':336,3032 'open':2610 'oper':393,519 'optim':139 'order':543,712,2521 'orderbi':509,611 'orderspec':612 'otherwis':191 'overspend':1094 'overview':72,194,248 'pace':75,77,967,1086,1122,1126,2735 'paceratio':1075,1088,1097 'page':1711,2470,2494,2510,2530,2597,2601,2606 'pages.length':2572,2596,2604 'pages.push':2554 'parallel':22,59,99,101,354,2859 'pattern':20,51,103,109,199,478,483,625,824 'paus':826,918,948,954,1318,3009 'pausecount':896,949,955 'per':2963,2984,3147 'per-account':2962 'perform':235,289,829,1827,2471,3119 'phrase':1294 'point':343 'practic':63,153,2994 'precis':167 'predefin':568 'predict':1738 'prefer':224 'preview':2996,3001 'principl':201 'prioriti':551 'process':23,60,102,816,2821,2851,2860 'processaccount':2878,2881 'processkeyword':2763 'programmat':254 'project':1114 'projectedspend':1079 'projectedspend.tofixed':1115,1148 'proper':148 'provid':143,166,180,192,663 'purpos':3083 'push':1915 'qi':1786 'qi.creativequalityscore':1796 'qi.postclickqualityscore':1797 'qi.qualityscore':1794 'qi.searchpredictedctr':1795 'qs':904,910,920,921,1664,1680 'qualiti':89,92,837,912,1649,1653,1704,1741,1745,2743 'qualityscor':545 'queri':16,48,113,229,288,295,661,678,692,720,736,855,894,1195,1235,1478,1500,1728,1777,1853,1896,2255,2294,2496,2528 'question':159 'rang':443,569,607,847,890,1180,1231,1325,1345,1462,1496,1837,1892,2041,2070,2083,2478,2516 'ratio':2399,2403,2406,2415,2424,2425 'ratio.tofixed':2455 'read':279,2629,3122 'read-on':3121 'readi':172 'ready-to-us':171 'receiv':2972 'recipients/day':3136 'recommend':2732 'refer':8,29,31,132,2855,3158,3163 'regexp':529,539 'relev':162,1709 'reli':3046 'remind':236 'report':739,753,790,798,1397,2432,2712,2714,3125 'report.exporttosheet':786 'request':312 'requir':441,562 'resourc':3149 'respons':1433,1881,3130 'response.getresponsecode':1443 'result':614,623,683,718,892,1233,1498,1775,1894,2292,2526,2886,2925,2931,2940,2947,2957 'result.campaigns.push':2914 'results.hasnext':722,899,1239,1504,1781,1900,2298,2532 'results.length':2937 'results.next':725,902,1242,1507,1784,1903,2301,2535 'retain':1663 'return':679,737,794,1442,1446,2112,2923,2927,2967 'roa':2024,2097,2134,2173,2548,2592 'roas.tofixed':2567 'rollout':3029 'root':387 'row':684,724,796,803,817,901,1241,1506,1779,1783,1813,1817,1902,2300,2534 'row.adgroup.name':940,1791,1907 'row.adgroupad.ad.id':1917 'row.adgroupad.adstrength':1919 'row.adgroupcriterion.keyword.matchtype':1793 'row.adgroupcriterion.keyword.text':919,937,1792 'row.adgroupcriterion.qualityinfo':1787 'row.adgroupcriterion.qualityinfo.qualityscore':905 'row.campaign.name':727,802,928,943,1254,1266,1275,1510,1790,1906,2304 'row.landingpageview.unexpandedfinalurl':2555 'row.metrics.clicks':1264,1536,1799,1923,2319,2542,2545,2556 'row.metrics.conversions':734,1541,1804,1927,2324,2544,2560 'row.metrics.conversionsvalue':2551 'row.metrics.conversionsvalue.tofixed':2561 'row.metrics.costmicros':729,1248,1546,1800,2322,2538 'row.metrics.ctr':908,1925 'row.metrics.impressions':1798,1921,2317,2557 'row.searchtermview.searchterm':1245 'row.segments.date':2307 'row.segments.hour':1513 'rows.length':1806,1812,1820 'rows.push':1788 'rsa':1826 'rule':2961 'run':206,261,315,852,931,958,961,1189,1269,2054,2184,2190,2193,3003,3015,3023 'runtim':2799,2814 's.getclicks':2918 's.getconversions':2922 's.getcost':2920 'sat':597 'schedul':318,2717,2733,3084 'scope':335 'score':90,93,758,838,861,913,1650,1654,1705,1736,1742,1746,2744 'script':4,7,19,28,35,50,79,115,121,128,142,209,251,260,328,334,486,658,676,823,2725,2775,2823,2826,3019,3054,3077,3117,3152,3167 'search':83,556,558,788,1160,1207,1882 'search_term_view.search':1197 'section':163 'segments.date':707,769,887,1228,1493,1769,1889,2263,2270,2513 'segments.hour':1481 'select':693,755,856,1196,1479,1729,1854,2256,2497 'selector':14,44,105,106,467,476,487 'selector-iter':475 'send':305,3040 'server':271 'set':2718,2722 'setamount':2186 'setvalu':1816,2600,2656,2690,3067 'sheet':24,56,96,232,300,749,778,780,783,787,807,1192,1658,1672,1676,1678,1686,2485,2489,2491,2575,2608,2623,2626,3064 'sheet.appendrow':1696,2583,2659 'sheet.clear':2582 'sheet.getdatarange':2642 'sheet.getlastrow':1694,1809 'sheet.getrange':1808,2593,2634,2646,2654,2683 'shop':542 'shoppingcampaign':419 'shortcut':407,412,417 'simpl':2696 'singl':324,331,391,2649,2784,3095 'single-account':323,330,390 'skill' 'skill-google-ads-scripts' 'sort':613 'source-cognyai' 'specif':182 'spend':1063,1066,1070,1071,1140,1144 'spike':2208,2418 'spreadsheet':2612 'spreadsheetapp.openbyid':2622 'spreadsheetapp.openbyurl':781,1687,2576,2616 'sql':665 'sql-like':664 'ss':2615,2621 'ss.getsheetbyname':2627 'start':524,3030 'starttim':2793,2812 'stat':440,451,455,647,1058,2080 'stats.getaveragecpc':463 'stats.getclicks':461,654 'stats.getconversionrate':466 'stats.getconversions':465,1073,2104 'stats.getconversionvalue':2089 'stats.getcost':464,1064,2086 'stats.getctr':462 'stats.getimpressions':460 'status':428,494,631,1035,1084,1093,1102,1117,1118,1120,1130,1157,1335,1363,1367,1379,1382,2060,2896 'stop':2819 'strength':1859,1918 'string':2928,2969 'subject':2700,2708 'sum':2364,2376,2388 'sun':577,596 'support':3115 'synchron':3101 'target':3053 'team@example.com':987,1317,1851,2216,2699,2707,2772 'templat':369 'term':84,1161,1198,1208,1244,1251,1252,1259,1288,1296,1299 'test':1825 'text':936,2702 'threshold':978,982,1091,1100,2209,2212,2405,2408,2417 'time':603,1452,2780,2817,3092 'timeout':3133 'today':570,578,582,994,1028,1718,1789,2224 'today.getdate':1008 'today.getfullyear':1001,1020 'today.getmonth':1002,1021 'today.gettime':2231,2237 'todaystr':1026,1045,1061 'tofix':731,925,1589,1646,1802,2565 'tool':2721 'topic':183 'topic-agent-skills' 'topic-ai-agents' 'topic-claude-code' 'topic-claude-skills' 'topic-cluade-mcp' 'topic-cursor' 'topic-geo' 'topic-growth-hacking' 'topic-llm' 'topic-marketing' 'topic-mcp' 'topic-seo' 'total':951,1301,2030 'totalclick':1560,1571,1577,1580,1945,1956,1968 'totalconv':1558,1567,1579 'totalimpr':1947,1960,1966,1969 'totalroa':2116,2131,2151,2154 'totalspend':1048,1065,1076,1080 'totalspend.tofixed':1112,1145 'track':2745 'tracker':91,1651 'tri':1431,2762 'true':853,1190,1437,1439,2055,3024 'try/catch':220 'type':795,859,1186,1285,1293,1703,1734,2414,2446 'underspend':1103 'url':784,1193,1308,1357,1361,1365,1376,1377,1383,1396,1417,1426,1430,1435,1673,1677,1689,2486,2490,2500,2571,2578,2584,2752,3126 'urlfetchapp':314 'urlfetchapp.fetch':1434 'usag':69 'use':130,174,219,258,291,473,3051,3066 'user':135,156,179,237 'utc':1023,1029,1722,2246,2252,2331 'utilities.formatdate':1017,1027,1719,2244,2250,2329 'valid':3036 'validatehttpscertif':1440 'valu':2507,2590,2633,2645 'var':454,490,627,643,646,691,717,723,752,779,834,854,891,895,900,903,906,932,972,993,997,1006,1009,1015,1025,1031,1047,1050,1054,1057,1062,1074,1078,1083,1123,1168,1194,1232,1236,1240,1243,1246,1270,1279,1313,1329,1331,1353,1356,1362,1393,1401,1432,1459,1477,1497,1501,1505,1508,1511,1549,1553,1557,1563,1575,1592,1598,1606,1613,1670,1685,1717,1727,1774,1778,1782,1785,1831,1852,1893,1897,1901,1904,1928,1932,1936,1944,1950,1964,1972,1978,2028,2056,2072,2076,2079,2084,2087,2115,2119,2136,2142,2146,2203,2223,2227,2233,2242,2248,2254,2291,2295,2299,2302,2305,2325,2327,2337,2341,2345,2353,2359,2363,2369,2386,2390,2398,2429,2437,2475,2495,2525,2529,2533,2536,2540,2547,2574,2614,2620,2625,2632,2640,2644,2675,2792,2797,2830,2845,2866,2882,2885,2892,2905,2908,2933,2944 'via':308,313,3055,3116 'videocampaign':420 'view':764,872,1209,1754,2511 'vs':326,789 'web':267 'week':321,576,580,584,593,595,2711,2730,2742,2754 'winner':1841,1929,2016 'winners.length':2017 'winners.push':1993 'withcondit':493,496,499,502,515,533,537,544,547,553,630,637,935,938,941,1034,1037,1273,1334,1337,1340,1346,2059,2062,2833,2869,2895 'within':38,675 'withlimit':512,617,2840 'work':18,53,145,371 'write':136,233,2651,2667,3065 'x':2457 'yes':357 'yesterday':571,2228,2245,2330 'yesterdayd':2328,2348,2375,2433 'yesterdaydata':2346,2350,2392 'yesterdaystr':2243 'yesterdaystr.substring':2282,2285,2288 'yyyi':1724,2333 'yyyy-mm-dd':1723,2332 'yyyymmdd':1024,1030,2247,2253","prices":[{"id":"0c56eb62-60b7-4c0b-8cfc-bdfa6eefd16f","listingId":"3136bea6-be3f-4269-ba9d-015054832338","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"cognyai","category":"claude-code-marketing-skills","install_from":"skills.sh"},"createdAt":"2026-04-18T22:23:35.125Z"}],"sources":[{"listingId":"3136bea6-be3f-4269-ba9d-015054832338","source":"github","sourceId":"cognyai/claude-code-marketing-skills/google-ads-scripts","sourceUrl":"https://github.com/cognyai/claude-code-marketing-skills/tree/main/skills/google-ads-scripts","isPrimary":false,"firstSeenAt":"2026-04-18T22:23:35.125Z","lastSeenAt":"2026-05-01T06:56:26.600Z"}],"details":{"listingId":"3136bea6-be3f-4269-ba9d-015054832338","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"cognyai","slug":"google-ads-scripts","github":{"repo":"cognyai/claude-code-marketing-skills","stars":42,"topics":["agent-skills","ai-agents","claude-code","claude-skills","cluade-mcp","cursor","geo","growth-hacking","llm","marketing","mcp","seo","vibe","windsurf"],"license":null,"html_url":"https://github.com/cognyai/claude-code-marketing-skills","pushed_at":"2026-04-29T13:43:50Z","description":"Marketing skills for Claude Code — SEO audits and implementation, ad analysis, ad optimization. Free skills need no account. $9/mo for live Search Console, Bing & LinkedIn data.","skill_md_sha":"53bb41e406b79494db3c5b7592a0a272791ace7c","skill_md_path":"skills/google-ads-scripts/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/cognyai/claude-code-marketing-skills/tree/main/skills/google-ads-scripts"},"layout":"multi","source":"github","category":"claude-code-marketing-skills","frontmatter":{"name":"google-ads-scripts","description":"Google Ads Scripts Reference — JavaScript automation, AdsApp object model, selectors, GAQL queries, 10+ working script patterns, MCC parallel processing, Sheets integration"},"skills_sh_url":"https://skills.sh/cognyai/claude-code-marketing-skills/google-ads-scripts"},"updatedAt":"2026-05-01T06:56:26.600Z"}}