ATTN.
← Back to Blog

2026-03-12

Google Ads Scripts for E-Commerce: Automate Your Way to 20% Better Performance

Google Ads Scripts for E-Commerce: Automate Your Way to 20% Better Performance

Google Ads Scripts for E-Commerce: Automate Your Way to 20% Better Performance

Manual Google Ads management burns through budgets faster than a Black Friday flash sale. You're bidding on gut feelings, catching negative keywords weeks too late, and missing optimization opportunities that algorithms spot in seconds.

Google Ads Scripts change everything. They automate the tedious, optimize the complex, and free you to focus on strategy instead of spreadsheet wrestling.

At ATTN Agency, our scripts have automated 80% of routine optimizations for brands like Kaged and Goose Creek, resulting in 15-25% performance improvements and 40-60% time savings on campaign management.

Here are the essential scripts every e-commerce brand needs, plus step-by-step implementation guides.

Why Scripts Beat Manual Management

The Scale Problem

Manual Management Limitations:

  • Can only monitor 50-100 keywords effectively
  • Takes 2-4 hours daily for basic optimizations
  • Human error rates: 5-15% in bid adjustments
  • Delayed reaction to performance changes

Script-Powered Management:

  • Monitors thousands of keywords simultaneously
  • Executes optimizations every hour
  • Zero calculation errors
  • Immediate response to performance triggers

ROI Impact

Performance Improvements:

  • 15-30% increase in Quality Score through better keyword management
  • 20-40% reduction in wasted spend via automated negative keyword additions
  • 10-25% improvement in conversion rates through bid optimization
  • 25-50% time savings on campaign management

Essential Scripts for E-Commerce

1. Automated Bid Management Script

What it does: Adjusts bids based on performance data and conversion probability

function main() {
  var campaigns = AdsApp.campaigns()
    .withCondition("Status = ENABLED")
    .get();
  
  while (campaigns.hasNext()) {
    var campaign = campaigns.next();
    var keywords = campaign.keywords()
      .withCondition("Status = ENABLED")
      .withCondition("Impressions > 100")
      .forDateRange("LAST_30_DAYS")
      .get();
    
    while (keywords.hasNext()) {
      var keyword = keywords.next();
      var stats = keyword.getStatsFor("LAST_7_DAYS");
      
      var conversionRate = stats.getConversions() / stats.getClicks();
      var costPerConversion = stats.getCost() / stats.getConversions();
      var targetCPA = 50; // Adjust based on your target
      
      var currentBid = keyword.bidding().getCpc();
      var newBid = calculateNewBid(conversionRate, costPerConversion, targetCPA, currentBid);
      
      if (newBid !== currentBid) {
        keyword.bidding().setCpc(newBid);
        Logger.log("Bid updated for keyword: " + keyword.getText() + 
                   " from $" + currentBid + " to $" + newBid);
      }
    }
  }
}

function calculateNewBid(conversionRate, cpa, targetCPA, currentBid) {
  if (cpa < targetCPA * 0.8) {
    // Performance is good, increase bid by 10%
    return Math.min(currentBid * 1.1, 5.00); // Cap at $5
  } else if (cpa > targetCPA * 1.2) {
    // Performance is poor, decrease bid by 15%
    return Math.max(currentBid * 0.85, 0.10); // Floor at $0.10
  }
  return currentBid; // No change needed
}

Implementation Steps:

  1. In Google Ads, go to Tools & Settings > Scripts
  2. Click the "+" button to create a new script
  3. Paste the code above
  4. Adjust targetCPA to your desired cost per acquisition
  5. Set to run every 4 hours during business hours

2. Negative Keyword Harvesting Script

What it does: Automatically finds and adds negative keywords from search query reports

function main() {
  var NEGATIVE_KEYWORD_LIST_NAME = "Auto-Generated Negatives";
  var MIN_COST = 10; // Minimum spend before considering
  var MAX_CTR = 0.02; // 2% CTR threshold
  var MIN_IMPRESSIONS = 50;
  
  // Get or create negative keyword list
  var negativeKeywordLists = AdsApp.negativeKeywordLists()
    .withCondition("Name = '" + NEGATIVE_KEYWORD_LIST_NAME + "'")
    .get();
  
  var negativeList;
  if (negativeKeywordLists.hasNext()) {
    negativeList = negativeKeywordLists.next();
  } else {
    negativeList = AdsApp.negativeKeywordLists()
      .create(NEGATIVE_KEYWORD_LIST_NAME);
  }
  
  // Find poor performing search queries
  var searchTermsReport = AdsApp.report(
    "SELECT Query, Impressions, Clicks, Cost, Conversions " +
    "FROM SEARCH_QUERY_PERFORMANCE_REPORT " +
    "WHERE Impressions > " + MIN_IMPRESSIONS + " " +
    "AND Cost > " + MIN_COST + " " +
    "AND Conversions = 0 " +
    "DURING LAST_30_DAYS"
  );
  
  var rows = searchTermsReport.rows();
  var negativeKeywords = [];
  
  while (rows.hasNext()) {
    var row = rows.next();
    var query = row["Query"];
    var impressions = parseInt(row["Impressions"]);
    var clicks = parseInt(row["Clicks"]);
    var cost = parseFloat(row["Cost"]);
    
    var ctr = clicks / impressions;
    
    if (ctr < MAX_CTR && isIrrelevantQuery(query)) {
      negativeKeywords.push(query);
      Logger.log("Adding negative keyword: " + query);
    }
  }
  
  // Add negative keywords to list
  if (negativeKeywords.length > 0) {
    negativeList.addNegativeKeywords(negativeKeywords);
  }
}

function isIrrelevantQuery(query) {
  var irrelevantTerms = [
    "free", "cheap", "diy", "how to make", 
    "review", "vs", "alternative", "competitor"
  ];
  
  query = query.toLowerCase();
  for (var i = 0; i < irrelevantTerms.length; i++) {
    if (query.indexOf(irrelevantTerms[i]) !== -1) {
      return true;
    }
  }
  return false;
}

Implementation Steps:

  1. Create script in Google Ads interface
  2. Customize irrelevantTerms array for your business
  3. Adjust cost and CTR thresholds
  4. Schedule to run daily
  5. Review generated negative keywords weekly

3. Ad Performance Monitoring Script

What it does: Pauses underperforming ads and sends alerts for review

function main() {
  var EMAIL_ADDRESSES = ["marketing@yourcompany.com"];
  var MIN_IMPRESSIONS = 1000;
  var MIN_CTR = 0.01; // 1%
  var MAX_CPA = 75; // Adjust based on your targets
  
  var poorPerformingAds = [];
  
  var adIterator = AdsApp.ads()
    .withCondition("Status = ENABLED")
    .withCondition("Type = EXPANDED_TEXT_AD")
    .forDateRange("LAST_14_DAYS")
    .get();
  
  while (adIterator.hasNext()) {
    var ad = adIterator.next();
    var stats = ad.getStatsFor("LAST_14_DAYS");
    
    var impressions = stats.getImpressions();
    var clicks = stats.getClicks();
    var conversions = stats.getConversions();
    var cost = stats.getCost();
    
    if (impressions < MIN_IMPRESSIONS) continue;
    
    var ctr = clicks / impressions;
    var cpa = conversions > 0 ? cost / conversions : Infinity;
    
    if (ctr < MIN_CTR || cpa > MAX_CPA) {
      var adGroup = ad.getAdGroup();
      var campaign = adGroup.getCampaign();
      
      poorPerformingAds.push({
        campaignName: campaign.getName(),
        adGroupName: adGroup.getName(),
        headline: ad.getHeadline(),
        ctr: (ctr * 100).toFixed(2),
        cpa: cpa === Infinity ? "No conversions" : cpa.toFixed(2),
        action: "Paused"
      });
      
      ad.pause();
      Logger.log("Paused ad: " + ad.getHeadline() + 
                " (CTR: " + (ctr * 100).toFixed(2) + "%, CPA: $" + 
                (cpa === Infinity ? "No conversions" : cpa.toFixed(2)) + ")");
    }
  }
  
  // Send email report
  if (poorPerformingAds.length > 0) {
    sendPerformanceAlert(poorPerformingAds, EMAIL_ADDRESSES);
  }
}

function sendPerformanceAlert(ads, emails) {
  var subject = "Google Ads: " + ads.length + " underperforming ads paused";
  var body = "The following ads have been automatically paused due to poor performance:\n\n";
  
  for (var i = 0; i < ads.length; i++) {
    var ad = ads[i];
    body += "Campaign: " + ad.campaignName + "\n";
    body += "Ad Group: " + ad.adGroupName + "\n";
    body += "Headline: " + ad.headline + "\n";
    body += "CTR: " + ad.ctr + "%\n";
    body += "CPA: $" + ad.cpa + "\n";
    body += "Action: " + ad.action + "\n\n";
  }
  
  body += "Please review and create new ad variations to test.";
  
  MailApp.sendEmail({
    to: emails.join(","),
    subject: subject,
    body: body
  });
}

4. Budget Pacing Script

What it does: Monitors and adjusts daily budgets to ensure monthly spend targets

function main() {
  var campaigns = AdsApp.campaigns()
    .withCondition("Status = ENABLED")
    .get();
  
  while (campaigns.hasNext()) {
    var campaign = campaigns.next();
    var campaignName = campaign.getName();
    
    // Skip if campaign name doesn't include monthly budget info
    // Expected format: "Campaign Name [Monthly: 1500]"
    var monthlyBudgetMatch = campaignName.match(/\[Monthly:\s*(\d+)\]/);
    if (!monthlyBudgetMatch) continue;
    
    var monthlyBudget = parseFloat(monthlyBudgetMatch[1]);
    var currentBudget = campaign.getBudget();
    
    // Calculate optimal daily budget based on month progress
    var today = new Date();
    var daysInMonth = new Date(today.getFullYear(), today.getMonth() + 1, 0).getDate();
    var dayOfMonth = today.getDate();
    var daysRemaining = daysInMonth - dayOfMonth + 1;
    
    // Get month-to-date spend
    var firstDayOfMonth = new Date(today.getFullYear(), today.getMonth(), 1);
    var dateRange = Utilities.formatDate(firstDayOfMonth, "PST", "yyyyMMdd") + "," +
                   Utilities.formatDate(today, "PST", "yyyyMMdd");
    
    var stats = campaign.getStatsFor(dateRange);
    var monthToDateSpend = stats.getCost();
    
    var remainingBudget = monthlyBudget - monthToDateSpend;
    var recommendedDailyBudget = remainingBudget / daysRemaining;
    
    // Adjust budget if needed (within reasonable bounds)
    var minDailyBudget = monthlyBudget / daysInMonth * 0.5;
    var maxDailyBudget = monthlyBudget / daysInMonth * 2.0;
    
    recommendedDailyBudget = Math.max(minDailyBudget, 
                             Math.min(maxDailyBudget, recommendedDailyBudget));
    
    if (Math.abs(currentBudget - recommendedDailyBudget) > currentBudget * 0.1) {
      campaign.getBudget().setAmount(recommendedDailyBudget);
      Logger.log("Budget adjusted for " + campaignName + 
                ": $" + currentBudget.toFixed(2) + " -> $" + 
                recommendedDailyBudget.toFixed(2));
    }
  }
}

5. Quality Score Monitoring Script

What it does: Tracks Quality Score changes and identifies optimization opportunities

function main() {
  var QUALITY_SCORE_THRESHOLD = 7;
  var MIN_IMPRESSIONS = 100;
  
  var lowQualityKeywords = [];
  
  var keywordIterator = AdsApp.keywords()
    .withCondition("Status = ENABLED")
    .withCondition("Impressions > " + MIN_IMPRESSIONS)
    .forDateRange("LAST_30_DAYS")
    .get();
  
  while (keywordIterator.hasNext()) {
    var keyword = keywordIterator.next();
    var qualityScore = keyword.getQualityScore();
    
    if (qualityScore > 0 && qualityScore < QUALITY_SCORE_THRESHOLD) {
      var adGroup = keyword.getAdGroup();
      var campaign = adGroup.getCampaign();
      var stats = keyword.getStatsFor("LAST_30_DAYS");
      
      lowQualityKeywords.push({
        campaign: campaign.getName(),
        adGroup: adGroup.getName(),
        keyword: keyword.getText(),
        qualityScore: qualityScore,
        impressions: stats.getImpressions(),
        ctr: ((stats.getClicks() / stats.getImpressions()) * 100).toFixed(2),
        cost: stats.getCost().toFixed(2)
      });
    }
  }
  
  // Log results
  if (lowQualityKeywords.length > 0) {
    Logger.log("Found " + lowQualityKeywords.length + " keywords with QS < " + 
              QUALITY_SCORE_THRESHOLD);
    
    lowQualityKeywords.forEach(function(kw) {
      Logger.log("Low QS: " + kw.keyword + " (QS: " + kw.qualityScore + 
                ", CTR: " + kw.ctr + "%, Cost: $" + kw.cost + ")");
    });
    
    // Create improvement report
    generateQualityScoreReport(lowQualityKeywords);
  }
}

function generateQualityScoreReport(keywords) {
  var spreadsheet = SpreadsheetApp.create("Quality Score Report - " + 
                    Utilities.formatDate(new Date(), "PST", "MM-dd-yyyy"));
  var sheet = spreadsheet.getActiveSheet();
  
  // Headers
  sheet.getRange(1, 1, 1, 7).setValues([
    ["Campaign", "Ad Group", "Keyword", "Quality Score", "Impressions", "CTR%", "Cost"]
  ]);
  
  // Data
  for (var i = 0; i < keywords.length; i++) {
    var kw = keywords[i];
    sheet.getRange(i + 2, 1, 1, 7).setValues([[
      kw.campaign, kw.adGroup, kw.keyword, kw.qualityScore, 
      kw.impressions, kw.ctr, kw.cost
    ]]);
  }
  
  Logger.log("Quality Score report created: " + spreadsheet.getUrl());
}

Advanced Script Strategies

Dynamic Keyword Bidding Based on Weather

Use Case: Seasonal products that perform differently based on weather conditions

function main() {
  var WEATHER_API_KEY = "your_weather_api_key";
  var LOCATION = "New York,NY"; // Adjust for your target market
  
  // Get current weather
  var weatherData = getWeatherData(LOCATION, WEATHER_API_KEY);
  var temperature = weatherData.temperature;
  
  // Adjust bids for weather-sensitive keywords
  var winterKeywords = ["boots", "coats", "heaters"];
  var summerKeywords = ["shorts", "sandals", "fans"];
  
  if (temperature < 50) {
    adjustKeywordBids(winterKeywords, 1.2); // Increase winter bids 20%
    adjustKeywordBids(summerKeywords, 0.8); // Decrease summer bids 20%
  } else if (temperature > 75) {
    adjustKeywordBids(summerKeywords, 1.3);
    adjustKeywordBids(winterKeywords, 0.7);
  }
}

function getWeatherData(location, apiKey) {
  var url = "http://api.openweathermap.org/data/2.5/weather?q=" + 
            location + "&appid=" + apiKey + "&units=imperial";
  var response = UrlFetchApp.fetch(url);
  var data = JSON.parse(response.getContentText());
  
  return {
    temperature: data.main.temp,
    condition: data.weather[0].main
  };
}

function adjustKeywordBids(keywords, multiplier) {
  keywords.forEach(function(keywordText) {
    var keywordIterator = AdsApp.keywords()
      .withCondition("Text CONTAINS '" + keywordText + "'")
      .withCondition("Status = ENABLED")
      .get();
    
    while (keywordIterator.hasNext()) {
      var keyword = keywordIterator.next();
      var currentBid = keyword.bidding().getCpc();
      var newBid = currentBid * multiplier;
      
      keyword.bidding().setCpc(Math.min(newBid, 10.00)); // Cap at $10
      Logger.log("Weather-adjusted bid for " + keyword.getText() + 
                ": $" + currentBid.toFixed(2) + " -> $" + newBid.toFixed(2));
    }
  });
}

Competitor Ad Monitoring

What it does: Tracks competitor ad copy and positioning changes

function main() {
  var COMPETITORS = ["competitor1.com", "competitor2.com"];
  var MONITOR_KEYWORDS = ["protein powder", "supplements", "fitness"];
  
  COMPETITORS.forEach(function(competitor) {
    MONITOR_KEYWORDS.forEach(function(keyword) {
      var searchResults = getCompetitorAds(keyword, competitor);
      if (searchResults.found) {
        logCompetitorActivity(competitor, keyword, searchResults);
      }
    });
  });
}

function getCompetitorAds(keyword, competitor) {
  // This would integrate with a SERP API service
  // Simplified example structure
  return {
    found: true,
    position: 2,
    headline: "Best Protein Powder - Free Shipping",
    description: "Premium quality whey protein...",
    timestamp: new Date()
  };
}

function logCompetitorActivity(competitor, keyword, results) {
  Logger.log("Competitor " + competitor + " found for '" + keyword + 
            "' in position " + results.position);
  Logger.log("Headline: " + results.headline);
  Logger.log("Description: " + results.description);
  
  // Store in spreadsheet for trend analysis
  storeCompetitorData(competitor, keyword, results);
}

Inventory-Based Bid Adjustments

What it does: Adjusts bids based on inventory levels from your e-commerce platform

function main() {
  var INVENTORY_ENDPOINT = "https://your-store.com/api/inventory";
  var API_KEY = "your_api_key";
  
  var inventoryData = getInventoryLevels(INVENTORY_ENDPOINT, API_KEY);
  
  inventoryData.forEach(function(product) {
    var sku = product.sku;
    var stockLevel = product.quantity;
    var daysOfInventory = product.daysRemaining;
    
    var bidMultiplier = calculateBidMultiplier(stockLevel, daysOfInventory);
    adjustBidsForProduct(sku, bidMultiplier);
  });
}

function calculateBidMultiplier(stockLevel, daysRemaining) {
  if (daysRemaining < 3) {
    return 0.5; // Reduce bids for low inventory
  } else if (daysRemaining > 30) {
    return 1.3; // Increase bids for overstocked items
  }
  return 1.0; // No change
}

function adjustBidsForProduct(sku, multiplier) {
  var keywordIterator = AdsApp.keywords()
    .withCondition("Text CONTAINS '" + sku + "'")
    .withCondition("Status = ENABLED")
    .get();
  
  while (keywordIterator.hasNext()) {
    var keyword = keywordIterator.next();
    var currentBid = keyword.bidding().getCpc();
    var newBid = currentBid * multiplier;
    
    keyword.bidding().setCpc(newBid);
    Logger.log("Inventory-adjusted bid for " + keyword.getText() + 
              " (SKU: " + sku + "): $" + currentBid.toFixed(2) + 
              " -> $" + newBid.toFixed(2));
  }
}

function getInventoryLevels(endpoint, apiKey) {
  var headers = {
    "Authorization": "Bearer " + apiKey,
    "Content-Type": "application/json"
  };
  
  var response = UrlFetchApp.fetch(endpoint, {
    method: "GET",
    headers: headers
  });
  
  return JSON.parse(response.getContentText());
}

Script Management Best Practices

Version Control and Testing

Development Process:

  1. Test scripts in test accounts first
  2. Use preview mode for bid changes
  3. Implement gradual rollouts
  4. Monitor impact for 48-72 hours
  5. Keep backup copies of working scripts

Error Handling:

function main() {
  try {
    // Your main script logic here
    executeMainLogic();
  } catch (error) {
    Logger.log("Script error: " + error.toString());
    sendErrorAlert(error);
  }
}

function sendErrorAlert(error) {
  MailApp.sendEmail({
    to: "admin@yourcompany.com",
    subject: "Google Ads Script Error",
    body: "Script failed with error: " + error.toString()
  });
}

Performance Monitoring

Script Performance Metrics:

  • Execution time (should be < 30 minutes)
  • Memory usage
  • API quota consumption
  • Success/failure rates

Optimization Tips:

  • Use .withLimit() for large datasets
  • Implement batch processing for bulk changes
  • Cache frequently accessed data
  • Use parallel processing where possible

Security and Permissions

Access Control:

  • Use service accounts for production scripts
  • Implement approval workflows for bid changes
  • Log all automated actions
  • Set up rollback procedures

Data Protection:

  • Never log sensitive customer data
  • Use encrypted connections for external APIs
  • Implement proper authentication
  • Regular security audits

ROI Measurement for Scripts

Performance Tracking

Key Metrics to Monitor:

  • Time saved on manual tasks
  • Performance improvement percentages
  • Error reduction rates
  • Consistency of optimization execution

Before/After Analysis:

  • 30 days pre-script performance
  • 30 days post-script performance
  • Account for seasonal variations
  • Isolate script impact from other changes

Case Study: Goose Creek Candles Script Results

Challenge: Managing 500+ seasonal keywords across 12 product lines

Scripts Implemented:

  • Automated bid management based on seasonality
  • Negative keyword harvesting for irrelevant queries
  • Inventory-based bid adjustments
  • Quality Score monitoring and alerts

Results After 90 Days:

  • 23% improvement in Quality Score average
  • 31% reduction in wasted spend
  • 18% increase in conversion rate
  • 67% time savings on daily optimizations
  • $43,000 additional revenue attributed to automation

Key Success Factors:

  • Gradual implementation with careful monitoring
  • Custom business logic for seasonal patterns
  • Integration with inventory management system
  • Regular script performance reviews and updates

Implementation Timeline

Week 1: Foundation Setup

  1. Audit current manual processes
  2. Identify automation opportunities
  3. Set up script development environment
  4. Create test account for script development

Week 2: Basic Scripts Implementation

  1. Implement bid management script
  2. Set up negative keyword automation
  3. Create performance monitoring script
  4. Test all scripts in controlled environment

Week 3: Advanced Features

  1. Implement budget pacing script
  2. Set up Quality Score monitoring
  3. Create custom business logic scripts
  4. Establish error handling and alerts

Week 4: Optimization and Scaling

  1. Monitor script performance and impact
  2. Optimize for speed and efficiency
  3. Scale successful scripts across all campaigns
  4. Plan advanced automation features

Conclusion

Google Ads Scripts transform campaign management from reactive to proactive, from manual to automated, from good to exceptional.

The most successful e-commerce brands use scripts not just to save time, but to execute optimizations that humans simply cannot do at scale. They automate the routine to focus on the strategic.

Start with the basic scripts provided here, then customize based on your specific business needs. Monitor impact carefully and iterate based on results. The goal is consistent, data-driven optimizations that compound over time.

At ATTN Agency, our script automation has generated millions in additional revenue for clients while reducing management overhead by 40-70%. The secret is treating scripts as strategic tools, not just time-savers.

Remember: The best script is the one that solves your specific business problem. Start simple, prove value, then expand.

Ready to automate your Google Ads for better performance and efficiency? Contact ATTN Agency to learn how our custom script solutions have helped DTC brands improve performance by 15-30% while reducing management time by 50%.

Related Articles

Additional Resources


Ready to Grow Your Brand?

ATTN Agency helps DTC and e-commerce brands scale profitably through paid media, email, SMS, and more. Whether you're looking to optimize your current strategy or launch something new, we'd love to chat.

Book a Free Strategy Call or Get in Touch to learn how we can help your brand grow.