2026-03-12
Advanced Google Ads Scripts for DTC Automation and Optimization
Advanced Google Ads Scripts for DTC Automation and Optimization
Google Ads Scripts provide powerful automation capabilities that can transform how DTC brands manage their advertising campaigns. From automated bid adjustments to performance monitoring and budget optimization, scripts enable sophisticated campaign management that would be impossible to execute manually at scale.
This comprehensive guide covers advanced Google Ads Scripts specifically designed for DTC brands, including custom automation strategies, performance optimization techniques, and scalable management solutions.
Essential Automation Scripts for DTC Brands
1. Automated Bid Management Script
function automatedBidManagement() {
// Configuration
const CONFIG = {
TARGET_ROAS: 4.0,
BID_ADJUSTMENT_THRESHOLD: 0.15,
MIN_CONVERSIONS: 5,
LOOKBACK_DAYS: 14,
MAX_BID_INCREASE: 0.5,
MAX_BID_DECREASE: 0.3
};
// Get campaigns with Smart Bidding
const campaigns = AdsApp.campaigns()
.withCondition('Status = ENABLED')
.withCondition('BiddingStrategyType = TARGET_ROAS')
.get();
while (campaigns.hasNext()) {
const campaign = campaigns.next();
optimizeCampaignBids(campaign, CONFIG);
}
}
function optimizeCampaignBids(campaign, config) {
const stats = campaign.getStatsFor('LAST_' + config.LOOKBACK_DAYS + '_DAYS');
if (stats.getConversions() < config.MIN_CONVERSIONS) {
Logger.log(`Campaign ${campaign.getName()} - Insufficient data for optimization`);
return;
}
const currentRoas = stats.getConversionValue() / stats.getCost();
const targetRoas = config.TARGET_ROAS;
const roasGap = (currentRoas - targetRoas) / targetRoas;
if (Math.abs(roasGap) < config.BID_ADJUSTMENT_THRESHOLD) {
Logger.log(`Campaign ${campaign.getName()} - ROAS within acceptable range`);
return;
}
// Adjust bids for keywords
adjustKeywordBids(campaign, roasGap, config);
// Log optimization action
Logger.log(`Campaign ${campaign.getName()} - ROAS Gap: ${roasGap.toFixed(2)}, Adjustments made`);
}
function adjustKeywordBids(campaign, roasGap, config) {
const keywords = campaign.keywords()
.withCondition('Status = ENABLED')
.withCondition('Conversions > 0')
.get();
while (keywords.hasNext()) {
const keyword = keywords.next();
const stats = keyword.getStatsFor('LAST_' + config.LOOKBACK_DAYS + '_DAYS');
const keywordRoas = stats.getConversionValue() / stats.getCost();
if (keywordRoas > config.TARGET_ROAS * 1.2) {
// Increase bid for high-performing keywords
const currentBid = keyword.bidding().getCpc();
const newBid = currentBid * (1 + Math.min(config.MAX_BID_INCREASE, roasGap));
keyword.bidding().setCpc(newBid);
} else if (keywordRoas < config.TARGET_ROAS * 0.8) {
// Decrease bid for poor-performing keywords
const currentBid = keyword.bidding().getCpc();
const newBid = currentBid * (1 - Math.min(config.MAX_BID_DECREASE, Math.abs(roasGap)));
keyword.bidding().setCpc(newBid);
}
}
}
2. Dynamic Budget Allocation Script
function dynamicBudgetAllocation() {
const CONFIG = {
TOTAL_DAILY_BUDGET: 5000,
MIN_BUDGET_PER_CAMPAIGN: 50,
PERFORMANCE_LOOKBACK_DAYS: 7,
BUDGET_ADJUSTMENT_FREQUENCY: 'DAILY'
};
const campaignPerformance = analyzeCampaignPerformance(CONFIG);
const budgetAllocations = calculateOptimalBudgetAllocation(campaignPerformance, CONFIG);
implementBudgetChanges(budgetAllocations);
}
function analyzeCampaignPerformance(config) {
const campaigns = AdsApp.campaigns()
.withCondition('Status = ENABLED')
.get();
const performanceData = [];
while (campaigns.hasNext()) {
const campaign = campaigns.next();
const stats = campaign.getStatsFor('LAST_' + config.PERFORMANCE_LOOKBACK_DAYS + '_DAYS');
const performance = {
campaign: campaign,
name: campaign.getName(),
spend: stats.getCost(),
conversions: stats.getConversions(),
conversionValue: stats.getConversionValue(),
roas: stats.getConversionValue() / stats.getCost(),
cpa: stats.getCost() / stats.getConversions(),
impressionShare: campaign.getSearchImpressionShare(),
currentBudget: campaign.getBudget().getAmount()
};
performanceData.push(performance);
}
return performanceData;
}
function calculateOptimalBudgetAllocation(performanceData, config) {
// Calculate efficiency scores
performanceData.forEach(campaign => {
campaign.efficiencyScore = calculateEfficiencyScore(campaign);
});
// Sort by efficiency score
performanceData.sort((a, b) => b.efficiencyScore - a.efficiencyScore);
// Allocate budget based on performance and opportunity
let remainingBudget = config.TOTAL_DAILY_BUDGET;
const allocations = [];
performanceData.forEach(campaign => {
let allocatedBudget = Math.max(
config.MIN_BUDGET_PER_CAMPAIGN,
remainingBudget * (campaign.efficiencyScore / getTotalEfficiencyScore(performanceData))
);
// Consider impression share opportunity
if (campaign.impressionShare < 0.8 && campaign.roas > 3.0) {
allocatedBudget *= 1.2; // 20% boost for high-opportunity campaigns
}
allocations.push({
campaign: campaign.campaign,
currentBudget: campaign.currentBudget,
newBudget: Math.min(allocatedBudget, remainingBudget),
change: allocatedBudget - campaign.currentBudget,
reason: `Efficiency Score: ${campaign.efficiencyScore.toFixed(2)}`
});
remainingBudget -= allocatedBudget;
});
return allocations;
}
function calculateEfficiencyScore(campaign) {
// Multi-factor efficiency calculation
const roasScore = Math.min(campaign.roas / 4.0, 2.0); // Cap at 2x for ROAS > 8
const volumeScore = Math.log(campaign.conversions + 1) / 10;
const opportunityScore = (1 - campaign.impressionShare) * 2;
return roasScore * 0.6 + volumeScore * 0.2 + opportunityScore * 0.2;
}
3. Automated Keyword Harvesting Script
function automatedKeywordHarvesting() {
const CONFIG = {
MIN_IMPRESSIONS: 100,
MIN_CLICKS: 5,
MAX_CPC_MULTIPLIER: 1.5,
SEARCH_TERMS_LOOKBACK: 30,
AUTO_ADD_THRESHOLD: {
conversions: 2,
ctr: 0.05,
conversionRate: 0.02
}
};
const campaigns = AdsApp.campaigns()
.withCondition('Status = ENABLED')
.get();
while (campaigns.hasNext()) {
const campaign = campaigns.next();
harvestKeywordsFromCampaign(campaign, CONFIG);
}
}
function harvestKeywordsFromCampaign(campaign, config) {
const searchTermsQuery = `
SELECT SearchTermView.search_term,
metrics.impressions,
metrics.clicks,
metrics.conversions,
metrics.cost_micros,
metrics.ctr
FROM search_term_view
WHERE campaign.id = ${campaign.getId()}
AND metrics.impressions >= ${config.MIN_IMPRESSIONS}
AND metrics.clicks >= ${config.MIN_CLICKS}
AND segments.date DURING LAST_${config.SEARCH_TERMS_LOOKBACK}_DAYS
`;
const searchTermsReport = AdsApp.report(searchTermsQuery);
const rows = searchTermsReport.rows();
const keywordsToAdd = [];
const negativeKeywordsToAdd = [];
while (rows.hasNext()) {
const row = rows.next();
const searchTerm = row['SearchTermView.search_term'];
const impressions = parseInt(row['metrics.impressions']);
const clicks = parseInt(row['metrics.clicks']);
const conversions = parseInt(row['metrics.conversions']);
const cost = parseInt(row['metrics.cost_micros']) / 1000000;
const ctr = parseFloat(row['metrics.ctr']);
// Check if search term is already a keyword
if (isExistingKeyword(campaign, searchTerm)) {
continue;
}
// Evaluate for keyword addition
if (shouldAddAsKeyword(conversions, ctr, config)) {
const suggestedBid = calculateSuggestedBid(cost, clicks, config);
keywordsToAdd.push({
text: searchTerm,
matchType: 'EXACT',
finalUrl: campaign.getUrls().getFinalUrl(),
maxCpc: suggestedBid
});
} else if (shouldAddAsNegative(conversions, ctr, cost, config)) {
negativeKeywordsToAdd.push({
text: searchTerm,
matchType: 'PHRASE'
});
}
}
// Add positive keywords
if (keywordsToAdd.length > 0) {
const keywordOperation = campaign.newKeywordBuilder()
.withText(keywordsToAdd[0].text)
.withCpc(keywordsToAdd[0].maxCpc)
.build();
Logger.log(`Added keyword "${keywordsToAdd[0].text}" to campaign ${campaign.getName()}`);
}
// Add negative keywords
if (negativeKeywordsToAdd.length > 0) {
campaign.createNegativeKeyword(negativeKeywordsToAdd[0].text);
Logger.log(`Added negative keyword "${negativeKeywordsToAdd[0].text}" to campaign ${campaign.getName()}`);
}
}
function shouldAddAsKeyword(conversions, ctr, config) {
return conversions >= config.AUTO_ADD_THRESHOLD.conversions &&
ctr >= config.AUTO_ADD_THRESHOLD.ctr;
}
function shouldAddAsNegative(conversions, ctr, cost, config) {
return conversions === 0 && cost > 50 && ctr < 0.01;
}
4. Performance Monitoring and Alerting Script
function performanceMonitoringAndAlerting() {
const CONFIG = {
ALERT_THRESHOLDS: {
roasDecline: 0.2, // 20% decline
costIncrease: 0.3, // 30% increase
conversionDrop: 0.25, // 25% drop
cpaIncrease: 0.4 // 40% increase
},
COMPARISON_PERIOD: 7, // Compare last 7 days vs previous 7
EMAIL_RECIPIENTS: ['marketing@company.com'],
SLACK_WEBHOOK_URL: 'your_slack_webhook_url'
};
const performanceAlerts = checkPerformanceAlerts(CONFIG);
if (performanceAlerts.length > 0) {
sendAlertNotifications(performanceAlerts, CONFIG);
}
}
function checkPerformanceAlerts(config) {
const alerts = [];
const campaigns = AdsApp.campaigns()
.withCondition('Status = ENABLED')
.get();
while (campaigns.hasNext()) {
const campaign = campaigns.next();
const currentStats = campaign.getStatsFor(`LAST_${config.COMPARISON_PERIOD}_DAYS`);
const previousStats = campaign.getStatsFor(
`LAST_${config.COMPARISON_PERIOD * 2}_DAYS`,
`LAST_${config.COMPARISON_PERIOD + 1}_DAYS`
);
// Check ROAS decline
const currentRoas = currentStats.getConversionValue() / currentStats.getCost();
const previousRoas = previousStats.getConversionValue() / previousStats.getCost();
if (previousRoas > 0 && (currentRoas - previousRoas) / previousRoas < -config.ALERT_THRESHOLDS.roasDecline) {
alerts.push({
type: 'ROAS_DECLINE',
campaign: campaign.getName(),
current: currentRoas,
previous: previousRoas,
change: ((currentRoas - previousRoas) / previousRoas * 100).toFixed(1) + '%',
severity: 'HIGH'
});
}
// Check cost increase
const costChange = (currentStats.getCost() - previousStats.getCost()) / previousStats.getCost();
if (costChange > config.ALERT_THRESHOLDS.costIncrease) {
alerts.push({
type: 'COST_INCREASE',
campaign: campaign.getName(),
current: currentStats.getCost(),
previous: previousStats.getCost(),
change: (costChange * 100).toFixed(1) + '%',
severity: 'MEDIUM'
});
}
// Check conversion drop
const conversionChange = (currentStats.getConversions() - previousStats.getConversions()) / previousStats.getConversions();
if (conversionChange < -config.ALERT_THRESHOLDS.conversionDrop) {
alerts.push({
type: 'CONVERSION_DROP',
campaign: campaign.getName(),
current: currentStats.getConversions(),
previous: previousStats.getConversions(),
change: (conversionChange * 100).toFixed(1) + '%',
severity: 'HIGH'
});
}
}
return alerts;
}
function sendAlertNotifications(alerts, config) {
// Email alert
const emailBody = generateEmailAlert(alerts);
MailApp.sendEmail({
to: config.EMAIL_RECIPIENTS.join(','),
subject: `Google Ads Performance Alert - ${alerts.length} issues detected`,
htmlBody: emailBody
});
// Slack notification
if (config.SLACK_WEBHOOK_URL) {
sendSlackNotification(alerts, config.SLACK_WEBHOOK_URL);
}
Logger.log(`Sent ${alerts.length} performance alerts`);
}
5. Competitor Analysis Script
function competitorAnalysisScript() {
const CONFIG = {
COMPETITOR_DOMAINS: [
'competitor1.com',
'competitor2.com',
'competitor3.com'
],
ANALYSIS_PERIOD_DAYS: 30,
IMPRESSION_SHARE_THRESHOLD: 0.1
};
const competitorInsights = analyzeCompetitorMetrics(CONFIG);
const opportunityReport = generateOpportunityReport(competitorInsights);
logCompetitorInsights(opportunityReport);
}
function analyzeCompetitorMetrics(config) {
const auctionInsightsQuery = `
SELECT
auction_insight_search_impression_share_report.domain,
metrics.impression_share,
metrics.overlap_rate,
metrics.position_above_rate,
metrics.position_below_rate,
metrics.top_of_page_rate,
metrics.absolute_top_impression_share
FROM auction_insight_search_impression_share_report
WHERE segments.date DURING LAST_${config.ANALYSIS_PERIOD_DAYS}_DAYS
AND metrics.impression_share >= ${config.IMPRESSION_SHARE_THRESHOLD}
`;
const report = AdsApp.report(auctionInsightsQuery);
const rows = report.rows();
const competitorData = {};
while (rows.hasNext()) {
const row = rows.next();
const domain = row['auction_insight_search_impression_share_report.domain'];
if (config.COMPETITOR_DOMAINS.includes(domain)) {
competitorData[domain] = {
impressionShare: parseFloat(row['metrics.impression_share']),
overlapRate: parseFloat(row['metrics.overlap_rate']),
positionAboveRate: parseFloat(row['metrics.position_above_rate']),
positionBelowRate: parseFloat(row['metrics.position_below_rate']),
topOfPageRate: parseFloat(row['metrics.top_of_page_rate']),
absoluteTopShare: parseFloat(row['metrics.absolute_top_impression_share'])
};
}
}
return competitorData;
}
function generateOpportunityReport(competitorData) {
const opportunities = [];
Object.keys(competitorData).forEach(domain => {
const competitor = competitorData[domain];
// High overlap, low position above rate = opportunity to outbid
if (competitor.overlapRate > 0.3 && competitor.positionAboveRate < 0.5) {
opportunities.push({
type: 'BIDDING_OPPORTUNITY',
competitor: domain,
description: `High overlap (${(competitor.overlapRate * 100).toFixed(1)}%) but appearing below competitor ${(competitor.positionAboveRate * 100).toFixed(1)}% of the time`,
recommendation: 'Consider increasing bids for overlapping keywords'
});
}
// Low impression share competitor with high top-of-page rate
if (competitor.impressionShare < 0.15 && competitor.topOfPageRate > 0.7) {
opportunities.push({
type: 'MARKET_EXPANSION',
competitor: domain,
description: `Competitor has low impression share but high top-of-page rate`,
recommendation: 'Opportunity to capture additional market share'
});
}
});
return opportunities;
}
Advanced Optimization Scripts
Shopping Campaign Optimization
function shoppingCampaignOptimization() {
const CONFIG = {
MIN_SPEND_THRESHOLD: 100,
TARGET_ROAS: 4.0,
NEGATIVE_KEYWORD_COST_THRESHOLD: 50,
PRODUCT_GROUP_SPLIT_THRESHOLD: 20
};
const shoppingCampaigns = AdsApp.shoppingCampaigns()
.withCondition('Status = ENABLED')
.get();
while (shoppingCampaigns.hasNext()) {
const campaign = shoppingCampaigns.next();
optimizeShoppingCampaign(campaign, CONFIG);
}
}
function optimizeShoppingCampaign(campaign, config) {
const stats = campaign.getStatsFor('LAST_30_DAYS');
if (stats.getCost() < config.MIN_SPEND_THRESHOLD) {
Logger.log(`Campaign ${campaign.getName()} - Insufficient spend for optimization`);
return;
}
// Analyze product group performance
const productGroups = campaign.productGroups().get();
const performanceData = [];
while (productGroups.hasNext()) {
const productGroup = productGroups.next();
const pgStats = productGroup.getStatsFor('LAST_30_DAYS');
if (pgStats.getCost() > 0) {
performanceData.push({
productGroup: productGroup,
roas: pgStats.getConversionValue() / pgStats.getCost(),
cost: pgStats.getCost(),
conversions: pgStats.getConversions()
});
}
}
// Optimize high-performing product groups
performanceData
.filter(pg => pg.roas > config.TARGET_ROAS * 1.2)
.forEach(pg => {
// Increase bid for high performers
const currentBid = pg.productGroup.bidding().getCpc();
const newBid = currentBid * 1.1;
pg.productGroup.bidding().setCpc(newBid);
});
// Reduce bids for poor performers
performanceData
.filter(pg => pg.roas < config.TARGET_ROAS * 0.8 && pg.cost > config.NEGATIVE_KEYWORD_COST_THRESHOLD)
.forEach(pg => {
const currentBid = pg.productGroup.bidding().getCpc();
const newBid = currentBid * 0.9;
pg.productGroup.bidding().setCpc(newBid);
});
}
Quality Score Optimization
function qualityScoreOptimization() {
const CONFIG = {
LOW_QS_THRESHOLD: 6,
HIGH_COST_THRESHOLD: 100,
MIN_IMPRESSIONS: 1000
};
const campaigns = AdsApp.campaigns()
.withCondition('Status = ENABLED')
.get();
const lowQualityKeywords = [];
while (campaigns.hasNext()) {
const campaign = campaigns.next();
const keywords = campaign.keywords()
.withCondition('Status = ENABLED')
.withCondition(`Impressions >= ${CONFIG.MIN_IMPRESSIONS}`)
.get();
while (keywords.hasNext()) {
const keyword = keywords.next();
const qualityScore = keyword.getQualityScore();
const stats = keyword.getStatsFor('LAST_30_DAYS');
if (qualityScore <= CONFIG.LOW_QS_THRESHOLD && stats.getCost() >= CONFIG.HIGH_COST_THRESHOLD) {
lowQualityKeywords.push({
keyword: keyword,
campaign: campaign.getName(),
qualityScore: qualityScore,
cost: stats.getCost(),
ctr: stats.getCtr(),
conversions: stats.getConversions()
});
}
}
}
// Generate optimization recommendations
generateQualityScoreReport(lowQualityKeywords);
}
function generateQualityScoreReport(lowQualityKeywords) {
lowQualityKeywords.forEach(item => {
const recommendations = [];
if (item.ctr < 0.02) {
recommendations.push('Improve ad copy relevance and call-to-action');
}
if (item.qualityScore <= 4) {
recommendations.push('Consider pausing and recreating with exact match');
}
if (item.conversions === 0 && item.cost > 200) {
recommendations.push('Consider adding as negative keyword');
}
Logger.log(`
Keyword: ${item.keyword.getText()}
Campaign: ${item.campaign}
Quality Score: ${item.qualityScore}
Cost: $${item.cost.toFixed(2)}
CTR: ${(item.ctr * 100).toFixed(2)}%
Recommendations: ${recommendations.join('; ')}
`);
});
}
Script Management and Deployment
Script Scheduling Best Practices
// Daily optimization script - run every morning at 6 AM
function dailyOptimizationRoutine() {
Logger.log('Starting daily optimization routine...');
try {
automatedBidManagement();
performanceMonitoringAndAlerting();
Logger.log('Daily optimization completed successfully');
} catch (error) {
Logger.log('Error in daily optimization: ' + error.toString());
// Send error notification
sendErrorAlert(error);
}
}
// Weekly deep analysis - run every Monday at 8 AM
function weeklyAnalysisRoutine() {
Logger.log('Starting weekly analysis routine...');
try {
dynamicBudgetAllocation();
competitorAnalysisScript();
qualityScoreOptimization();
Logger.log('Weekly analysis completed successfully');
} catch (error) {
Logger.log('Error in weekly analysis: ' + error.toString());
sendErrorAlert(error);
}
}
// Monthly comprehensive review - run first Monday of each month
function monthlyReviewRoutine() {
Logger.log('Starting monthly review routine...');
try {
automatedKeywordHarvesting();
shoppingCampaignOptimization();
generateMonthlyPerformanceReport();
Logger.log('Monthly review completed successfully');
} catch (error) {
Logger.log('Error in monthly review: ' + error.toString());
sendErrorAlert(error);
}
}
function sendErrorAlert(error) {
MailApp.sendEmail({
to: 'admin@company.com',
subject: 'Google Ads Script Error Alert',
body: `
An error occurred in Google Ads Script execution:
Error: ${error.toString()}
Time: ${new Date().toString()}
Please investigate and resolve the issue.
`
});
}
Implementation Strategy
Phase 1: Foundation Scripts (Week 1-2)
- Deploy basic performance monitoring
- Implement automated bid management
- Set up error handling and alerting
Phase 2: Optimization Scripts (Week 3-4)
- Add keyword harvesting automation
- Implement dynamic budget allocation
- Deploy quality score optimization
Phase 3: Advanced Analytics (Week 5-6)
- Add competitor analysis capabilities
- Implement shopping campaign optimization
- Create comprehensive reporting dashboards
Phase 4: Custom Solutions (Week 7-8)
- Develop brand-specific optimization logic
- Implement advanced attribution tracking
- Create predictive optimization models
Conclusion
Google Ads Scripts provide powerful automation capabilities that can transform DTC advertising performance and efficiency. By implementing these advanced scripts, brands can achieve superior results while reducing manual management overhead.
Success requires starting with foundational monitoring and optimization scripts, then gradually implementing more sophisticated automation based on specific business needs and performance goals.
Ready to implement advanced Google Ads automation for your DTC brand? ATTN Agency specializes in custom Google Ads Scripts and automation strategies. Contact us to discuss your automation opportunities.
Related Articles
- Advanced Google Ads Scripts for DTC Automation: Performance Optimization and Campaign Management at Scale 2026
- Google Ads Performance Max: Advanced Optimization Strategies for DTC Brands in 2026
- Advanced Facebook Messenger Automated Sequences for Conversion Optimization in 2026
- Google Ads Audience Signals: Advanced Targeting Strategies for Smart Bidding Success
- Google Ads Scripts for E-Commerce: Automate Your Way to 20% Better Performance
Additional Resources
- Google Ads Smart Bidding
- Google Ads Resource Center
- Meta Campaign Budget Optimization
- Moz SEO Guide
- Think with Google Marketing Insights
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.