How to create an Advanced AdWords Budget Script

Why use an AdWords Script to solve the problem with a budget?

So you’ve set up AdWords to gain more traffic to your website but you want to limit how much you spend on your campaigns per day/month/year, well this is exactly what we are going to show you today. We’ve heard a fair few horror stories where incorrect setup have caused businesses to pay high fees to Google AdWords simply because it wasn’t set up correctly. We are going to show you three case scenarios in which scripts can monitor exactly how much you spend and not exceed your budget.

The advanced budget script that we will be showing you today can be used to address several use cases:

1. Enforce Account Budgets

Firstly, budgets in AdWords are set at the campaign level. You can use a shared budget and assign all campaigns to it to get an account-level budget, but then you lose the ability to dedicate more money to higher performing campaigns.

We think shared budgets are useful because many advertisers don’t have time to manage things as granularly as they should. However, for those who want to get the very best results it’s better to manage the budget for each campaign separately.

So the first use case is simple, the script pauses all active campaigns when an account’s cost goes above a certain threshold for the month. You still set campaign level daily budgets but as soon as the total cost for the account gets too high, everything is paused.

2. Maintain Monthly Budgets

It could be argued that simply dividing a monthly budget evenly between all days is a fine way to turn the daily budgets AdWords uses into the monthly budgets that a typical business thinks about. But just like shared budgets are not great because you give up granularity, daily budgets that are the same every day don’t give you the control to spend money when it will lead to the best returns.

A great example of this in which we’ll use today is the month of December, since, with this particular month, we may not want to share the budget equally between every day of the month. Retailers know that the biggest opportunity for moving merchandise will come before Christmas. That being said, you might set higher daily budgets for Dec 1-24 than for Dec 25-31. That creates the risk that you could spend more than intended for the entire month. This script lets advertisers enforce a budget for a date range other than daily so that a campaign can automatically be paused when the cost starts to exceed the maximum allowed for the month, regardless of the daily budget.

The script can also be set to check budgets daily, weekly from Sunday through today, or weekly from Monday through today.

3. Apply Small Budgets To Tests

Let’s say you have a campaign that’s working really well and for which you’ve set a high budget, you might be worried that testing new ads or keywords could lead to accidentally spending a lot of money on something that performs worse than your expectations. Here’s a simple example that illustrates this issue. Let’s say you have a campaign with a £500 budget with all exact match keywords that usually produce a ROAS of about 10. Then you add a pretty generic broad match keyword and before you know it this new keyword has spent 90% of that campaign’s budget and is losing money. It stole the budget from what you knew was going to perform well and now you’re left with a loss for the day.

With this script, you can enforce small budgets down to the keyword or ad level. These experimental items can still go into your existing campaigns so you don’t need to maintain separate campaigns for testing.

The script can check budgets at the keyword, ad, ad group, campaign or account level.

The Script & Script Settings

Awesome, we’ve told you all about the script and the benefits and now you want to implement it right? Okay, great, first we are going to show you the code snippet to run on AdWords but make sure to check the script settings after the code script to ensure you have set it up correctly to your expectations!


DEBUG = 0;
 

function main() {

  Logger.log("");
  
//var SETTINGS = new Object();
  currentSetting = new Object();
  currentSetting.scope = "Account";	
  currentSetting.maxCost = getFloat("1000");
  currentSetting.budgetPeriod = "Monthly";
  currentSetting.labelName = "";
  currentSetting.labelToAdd = "stopped by budget script";
  currentSetting.email = "[email protected]";
  currentSetting.pauseItems = "yes";
  currentSetting.reEnableItems = "yes";
  
  currentSetting.logText = "";
  
  currentSetting.currencyCode = AdWordsApp.currentAccount().getCurrencyCode();
  
  switch(currentSetting.reEnableItems.toLowerCase()) {
    case "no":
      currentSetting.reEnableAtStartOfNewPeriod = 0;
      break;
    case "yes":
      currentSetting.reEnableAtStartOfNewPeriod = 1;
      break;
  }
  
  switch(currentSetting.pauseItems.toLowerCase()) {
    case "no":
      currentSetting.pauseWhenExceeds = 0;
      break;
    case "yes":
      currentSetting.pauseWhenExceeds = 1;
      break;
  }
  
  switch(currentSetting.budgetPeriod) {
	  case "Daily":
	  	//Logger.log("daily");
      currentSetting.dateRange = "TODAY";
	  	break;
	  case "Weekly Sun-Sat":
        //Logger.log("weekly sun");
        currentSetting.dateRange = "THIS_WEEK_SUN_TODAY";
	  	break;
      case "Weekly Mon-Sun":
        //Logger.log("weekly mon");
        currentSetting.dateRange = "THIS_WEEK_MON_TODAY";
	  	break;
	  case "Monthly":
        //Logger.log("monthly"); 
	  	currentSetting.dateRange = "THIS_MONTH";
        break;
  }
  
  
    
  var thenAction = "Pause"; // Alert
  var condition1 = "Cost > " + currentSetting.maxCost;
  var labelCondition = "Status = ENABLED";
  if(currentSetting.labelName) {
    labelCondition = "LabelNames CONTAINS_ANY ['" + currentSetting.labelName + "']";
  }
  if(DEBUG == 1) Logger.log("labelCondition: " + labelCondition);

  currentSetting.enabledCounter = 0;
  currentSetting.deletedCounter = 0;
  currentSetting.pausedCounter = 0;
  currentSetting.enabledList = new Array();
  currentSetting.pausedList = new Array();
  currentSetting.deletedList = new Array();
  
  
  // Set up labels
  var labelToAddText = currentSetting.labelToAdd + " (" + currentSetting.budgetPeriod + ")";
  Logger.log("labelToAddText: " + labelToAddText);
  currentSetting.labelToAdd = labelToAddText;
  if(currentSetting.labelToAdd) {
    createLabel(currentSetting.labelToAdd, "This label is used by an account automation. Its name should not be changed.");
  }
  
  
  if(currentSetting.reEnableAtStartOfNewPeriod) {
    // Check current date, day and time
    currentSetting.thisAccountTime = getTimeInThisAccount();
    var currentHour = currentSetting.thisAccountTime.HH;
    //Logger.log("currentHour: " + currentHour);
    var dayOfWeek = currentSetting.thisAccountTime.dayOfWeek;
    //Logger.log("dayOfWeek: " + dayOfWeek);
    var dd = currentSetting.thisAccountTime.dd;
    //Logger.log("dd: " + dd);
    
    //Re-activate paused items when start of a new period
    var pluralText = "";
    if(currentSetting.budgetPeriod.toLowerCase().indexOf("daily") != -1) {
      if(currentHour == 0) {
        reEnable();
        var emailType = "Notification";
        var body = 'Resetting daily budgets';
        if(currentSetting.enabledList.length > 0) {
          body += "<br/>";
          body += "Re-Enabled Because We Started a New Daily Budget Period:<br/><ul>";
          
          for(var itemCounter = 0; itemCounter < currentSetting.enabledList.length; itemCounter++) {
            var item = currentSetting.enabledList[itemCounter];
            body += "<li>" + item + "</li>";
          }
          body += "</ul>";
        }
        sendEmailNotifications(currentSetting.email, "Daily Budgets Reset", body, emailType );
        if(currentSetting.enabledList.length > 1) pluralText = "s";
        currentSetting.logText += currentSetting.enabledList.length + " " + currentSetting.scope + pluralText + " enabled. ";
      }
    } else if(currentSetting.budgetPeriod.toLowerCase().indexOf("weekly sun-mon") != -1) {
      if(dayOfWeek == 7 && currentHour == 0) {
        reEnable();
        var emailType = "Notification";
        var body = 'Resetting weekly budgets<br/>';
        if(currentSetting.enabledList.length > 0) {
          body += "<br/>";
          body += "Re-Enabled Because We Started a New Weekly Budget Period:<br/><ul>";
          
          for(var itemCounter = 0; itemCounter < currentSetting.enabledList.length; itemCounter++) {
            var item = currentSetting.enabledList[itemCounter];
            body += "<li>" + item + "</li>";;
          }
          body += "</ul>";
        }
        sendEmailNotifications(currentSetting.email, "Weekly Budgets Reset", body, emailType );
        if(currentSetting.enabledList.length > 1) pluralText = "s";
        currentSetting.logText += currentSetting.enabledList.length + " " + currentSetting.scope + pluralText + " enabled. ";
      }
    } else if(currentSetting.budgetPeriod.toLowerCase().indexOf("weekly mon-sun") != -1) {
      if(dayOfWeek == 1 && currentHour == 0) {
        reEnable();
        var emailType = "Notification";
        var body = 'Resetting weekly budgets<br/>';
        if(currentSetting.enabledList.length > 0) {
          body += "<br/>";
          body += "Re-Enabled Because We Started a New Weekly Budget Period:<br/><ul>";
          
          for(var itemCounter = 0; itemCounter < currentSetting.enabledList.length; itemCounter++) {
            var item = currentSetting.enabledList[itemCounter];
            body += "<li>" + item + "</li>";;
          }
          body += "</ul>";
        }
        sendEmailNotifications(currentSetting.email, "Weekly Budgets Reset", body, emailType );
        if(currentSetting.enabledList.length > 1) pluralText = "s";
        currentSetting.logText += currentSetting.enabledList.length + " " + currentSetting.scope + pluralText + " enabled. ";
      }
    } else if(currentSetting.budgetPeriod.toLowerCase().indexOf("monthly") != -1) {
      if(dd == 1 && currentHour == 0) {
        reEnable();
        var emailType = "Notification";
        var body = 'Resetting monthly budgets on the ' + dd + 'st of the month at ' + currentHour + "h<br/>";
        if(currentSetting.enabledList.length > 0) {
          body += "<br/>";
          body += "Re-Enabled Because We Started a New Monthly Budget Period:<br/><ul>";
          
          for(var itemCounter = 0; itemCounter < currentSetting.enabledList.length; itemCounter++) {
            var item = currentSetting.enabledList[itemCounter];
            body += "<li>" + item + "</li>";;
          }
          body += "</ul>";
        }
        sendEmailNotifications(currentSetting.email, "Monthly Budgets Reset", body, emailType );
        if(currentSetting.enabledList.length > 1) pluralText = "s";
        currentSetting.logText += currentSetting.enabledList.length + " " + currentSetting.scope + pluralText + " enabled. ";
      }
    }
  }
  
  // ------------------------------
  // CHECK IF BUDGETS HAVE EXCEEDED
  // ------------------------------
  
  // Account
  if(currentSetting.scope.toLowerCase().indexOf("account") != -1) {
    var fields = "Cost";
    var reportIterator = AdWordsApp.report('SELECT ' + fields +
      ' FROM ACCOUNT_PERFORMANCE_REPORT DURING ' + currentSetting.dateRange).rows();
    
    while(reportIterator.hasNext()) {
    var row = reportIterator.next();
    var cost = getFloat(row["Cost"]).toFixed(2);
      if(DEBUG == 1) Logger.log("Cost: " + cost + " currentSetting.maxCost: " + currentSetting.maxCost);
    }
    if(cost > currentSetting.maxCost) {
      // check if all campaigns are paused
      var campaignIterator = AdWordsApp.campaigns()
      .withCondition("Status = ENABLED")
      .get();
      
      var numActiveCampaigns = campaignIterator.totalNumEntities();
      
      if(numActiveCampaigns > 0) {
        var body = "The total cost for the account '" + AdWordsApp.currentAccount().getName() + "' (" + AdWordsApp.currentAccount().getCustomerId() + ") was " + currentSetting.currencyCode + " " + cost
                   + " as of the time of this email. The maximum allowed cost is " + currentSetting.currencyCode + " " + currentSetting.maxCost.toFixed(2) + " " + currentSetting.budgetPeriod + "." +
                     "<br/><br/>The account will continue to accrue more cost unless you take action like pausing all campaigns.";
        var emailType = "warning";
        sendEmailNotifications(currentSetting.email, "Account Budget Exceeded", body, emailType );
        //Logger.log("email sent");
        currentSetting.logText = "Account cost of " + currentSetting.currencyCode + " " + cost + " exceeds the maximum " + currentSetting.budgetPeriod + " cost of " + currentSetting.currencyCode + " " + currentSetting.maxCost.toFixed(2);
        
        if(currentSetting.pauseWhenExceeds) {
          while (campaignIterator.hasNext()) {
            var campaign = campaignIterator.next();
            var name = campaign.getName();
            campaign.pause();
            campaign.applyLabel(currentSetting.labelToAdd);
            currentSetting.pausedCounter++;
            currentSetting.pausedList.push(name);
          }
        }
      }
    } else {
      Logger.log("Account cost is currently " + currentSetting.currencyCode + cost + " and this does not exceed the allowed budget for the account.");
    }
  }
  
  // Campaigns
  else if(currentSetting.scope.toLowerCase().indexOf("campaign") != -1) {
    
    if(currentSetting.labelName != "") {
      var isLabelUsed = checkIfLabelIsUsed(currentSetting.scope, currentSetting.labelName);
    } 
    
    
    if(DEBUG == 1) Logger.log(condition1);
    if(DEBUG == 1) Logger.log(labelCondition);
    if(DEBUG == 1) Logger.log(currentSetting.dateRange);
    
    // SEARCH AND DISPLAY CAMPAIGNS
    var iterator = AdWordsApp.campaigns()
    .withCondition(condition1)
    .withCondition("Status = ENABLED")
    .withCondition(labelCondition)
    .forDateRange(currentSetting.dateRange)
    .get();
    
    while(iterator.hasNext()){
      var item = iterator.next();
      var name = item.getName();
      var cost = item.getStatsFor(currentSetting.dateRange).getCost();
      item.applyLabel(currentSetting.labelToAdd);
      Logger.log("Match found: '" + name + "' cost: " + currentSetting.currencyCode + " " + cost);
      if(currentSetting.pauseWhenExceeds) item.pause();
      currentSetting.pausedCounter++;
      currentSetting.pausedList.push(name + " cost: " + currentSetting.currencyCode + " " + cost);
    } 
    
    
    
    if(currentSetting.pausedCounter == 0) {
      Logger.log("No campaigns exceeded their allowed budgets for the budget period.");
    } else {
      //Logger.log("something else");
    }
    
    
  } else
  
    
  // Ad Groups
  if(currentSetting.scope.toLowerCase().indexOf("ad group") != -1) {
    
    if(currentSetting.labelName != "") {
      var isLabelUsed = checkIfLabelIsUsed(currentSetting.scope, currentSetting.labelName);
    } 
    
    // SEARCH AND DISPLAY CAMPAIGNS
    var iterator = AdWordsApp.adGroups()
     .withCondition(condition1)
     .withCondition("Status = ENABLED")
     .withCondition(labelCondition)
     .forDateRange(currentSetting.dateRange)
     .get();
    
    while(iterator.hasNext()){
      var item = iterator.next();
      var name = item.getName();
      var cost = item.getStatsFor(currentSetting.dateRange).getCost();
      var campaignName = item.getCampaign().getName();
      Logger.log("Match found: " + "campaign: '" + campaignName + "' ad group: '" + name + "' cost: " + currentSetting.currencyCode + " " + cost);
      item.applyLabel(currentSetting.labelToAdd);
      if(currentSetting.pauseWhenExceeds) item.pause();
      currentSetting.pausedCounter++;
      currentSetting.pausedList.push("campaign: '" + campaignName + "' ad group: '" + name + "'" + " cost: " + currentSetting.currencyCode + " " + cost);
    } 
    
    
    
    if(currentSetting.pausedCounter == 0) {
      Logger.log("No ad groups exceeded their allowed budgets for the budget period.");
    }
    
    
    // Ads
  } else if(currentSetting.scope.toLowerCase().indexOf("ad text") != -1) {
    
    if(currentSetting.labelName != "") {
      var isLabelUsed = checkIfLabelIsUsed(currentSetting.scope, currentSetting.labelName);
    } 
    
    var adIterator = AdWordsApp.ads()
     .withCondition(condition1)
     .withCondition("Status = ENABLED")
     .withCondition(labelCondition)
     .forDateRange(currentSetting.dateRange)
     .get();
    
    
    while(adIterator.hasNext()) {
      
      var ad = adIterator.next();
      var adHeadline = ad.getHeadline();
      var description1 = ad.getDescription1();
      var description2 = ad.getDescription2();
      var displayUrl = ad.getDisplayUrl();
      var cost = ad.getStatsFor(currentSetting.dateRange).getCost();
      ad.applyLabel(currentSetting.labelToAdd);
      
      Logger.log("Match found: " + adHeadline + " " + description1 + " " + description2 + " " + displayUrl  + " -- cost: " + currentSetting.currencyCode + " " + cost);
      var fullAdText = adHeadline + " " + description1 + " " + description2 + " " + displayUrl;
      if(currentSetting.pauseWhenExceeds) ad.pause();
      currentSetting.pausedCounter++;
      currentSetting.pausedList.push(fullAdText + " -- cost: " + currentSetting.currencyCode + " " + cost);
    } // while(adIterator.hasNext())
    
    if(currentSetting.pausedCounter == 0) {
      Logger.log("No ads exceeded their allowed budgets for the budget period.");
    }
    
    
    // Keywords
  } else if(currentSetting.scope.toLowerCase().indexOf("keyword") != -1) {
    
    if(currentSetting.labelName != "") {
      var isLabelUsed = checkIfLabelIsUsed(currentSetting.scope, currentSetting.labelName);
    } 
    
    var iterator = AdWordsApp.keywords()
     .withCondition(condition1)
     .withCondition("Status = ENABLED")
     .withCondition(labelCondition)
     .forDateRange(currentSetting.dateRange)
     .get();
    
    while(iterator.hasNext()){
      var item = iterator.next();
      var name = item.getText();
      var cost = item.getStatsFor(currentSetting.dateRange).getCost();
      var campaignName = item.getCampaign().getName();
      var adGroupName = item.getAdGroup().getName();
      Logger.log("Match found: " + "campaign: '" + campaignName + "' ad group: '" + adGroupName + "' kw: '" + name + "' cost: " + currentSetting.currencyCode + " " + cost);
      item.applyLabel(currentSetting.labelToAdd);
      if(currentSetting.pauseWhenExceeds) item.pause();
      currentSetting.pausedList.push("campaign: '" + campaignName + "' ad group: '" + adGroupName + "' kw: '" + name + "'" + " cost: " + currentSetting.currencyCode + " " + cost);
      currentSetting.pausedCounter++;
    } // while iterator hasnext
    
    if(currentSetting.pausedCounter == 0) {
      Logger.log("No keywords exceeded their allowed budgets for the budget period.");
    }
    
    
  } // if scope == keyword
  
  
  currentSetting.pausedList.sort();
  if(currentSetting.pausedCounter > 1) {
    var pluralText = "s";
  } else {
    var pluralText = "";
  }
  var body = currentSetting.pausedCounter + " " + currentSetting.scope + pluralText + " exceeded the " + currentSetting.budgetPeriod + " maximum cost of " + currentSetting.currencyCode + " " + currentSetting.maxCost.toFixed(2) + ". ";
  if(currentSetting.pauseWhenExceeds) body += "They were paused by the script.";
  body += "<br/><ul>";
  
  if(DEBUG == 1) Logger.log("currentSetting.email: " + currentSetting.email + " currentSetting.pausedCounter: " + currentSetting.pausedCounter);
  if(currentSetting.email && currentSetting.pausedCounter > 0) {
    
    var changesMadeOrSuggestedText = "suggested";
    if(currentSetting.pauseWhenExceeds) changesMadeOrSuggestedText = "made";
    
    var pausedOrSuggestedText = "exceeded budget";
    if(currentSetting.pauseWhenExceeds) pausedOrSuggestedText = "paused";
    
    var subject = "Automated Rules for " + AdWordsApp.currentAccount().getName() + ": " + currentSetting.pausedCounter + " change" + pluralText + " " + changesMadeOrSuggestedText;
    currentSetting.logText += currentSetting.pausedCounter + " " + currentSetting.scope + pluralText + " " + pausedOrSuggestedText;
    for(var itemCounter = 0; itemCounter < currentSetting.pausedList.length; itemCounter++) {
      var item = currentSetting.pausedList[itemCounter];
      body += "<li>" + item + "</li>";
    }
    body += "</ul><br/>";
    body += "These items were labeled '" + currentSetting.labelToAdd + "' for easy identification.<br/><br/>";
    body += "Thanks for using a free Optmyzr.com script. Try our Enhanced Scripts for AdWords which have several benefits:<ul><li>automatically updated when AdWords changes</li><li>works with MCC or individual accounts</li><li>change settings without touching a single line of code</li></ul>";
    body += "Get a 2 week free trial at <a href='https://www.optmyzr.com?utm_campaign=free_scripts'>optmyzr.com</a>";
    
    
    var emailType = "notification";
    if(DEBUG == 1) Logger.log("sending email...");
    sendEmailNotifications(currentSetting.email, subject, body, emailType )
  }
}

  

  
  function reEnable(){
  // Campaigns or Account
  if(currentSetting.scope.toLowerCase().indexOf("campaign") != -1 || currentSetting.scope.toLowerCase().indexOf("account") != -1) {
    var iterator = AdWordsApp.campaigns()
     .withCondition("LabelNames CONTAINS_ANY ['" + currentSetting.labelToAdd + "']")
     .get();
    
    while(iterator.hasNext()){
      var item = iterator.next();
      var name = item.getName();
      item.removeLabel(currentSetting.labelToAdd)
      Logger.log("Enabling campaign: " + name);
      item.enable();
      currentSetting.enabledCounter++;
      currentSetting.enabledList.push(name); 
    } 
    
    // SHOPPING CAMPAIGNS
    var iterator = AdWordsApp.shoppingCampaigns()
     .withCondition("LabelNames CONTAINS_ANY ['" + currentSetting.labelToAdd + "']")
     .get();
    
    while(iterator.hasNext()){
      var item = iterator.next();
      var name = item.getName();
      item.removeLabel(currentSetting.labelToAdd)
      Logger.log("Enabling campaign: " + name);
      item.enable();
      currentSetting.enabledCounter++;
      currentSetting.enabledList.push(name); 
    } 
    
  } else
  
    
  // Ad Groups
  if(currentSetting.scope.toLowerCase().indexOf("ad group") != -1) {

    var iterator = AdWordsApp.adGroups()
    .withCondition("LabelNames CONTAINS_ANY ['" + currentSetting.labelToAdd + "']")
    .get();
    
    while(iterator.hasNext()){
      var item = iterator.next();
      var name = item.getName();
      var campaignName = item.getCampaign().getName();
      Logger.log("Enabling campaign: '" + campaignName + "' ad group: '" + name + "'");
      item.removeLabel(currentSetting.labelToAdd);
      
      item.enable();
      currentSetting.enabledCounter++;
      currentSetting.enabledList.push("campaign: '" + campaignName + "' ad group: '" + name + "'");
    }
    
    // SHOPPING AD GROUPS
    var iterator = AdWordsApp.shoppingAdGroups()
    .withCondition("LabelNames CONTAINS_ANY ['" + currentSetting.labelToAdd + "']")
    .get();
    
    while(iterator.hasNext()){
      var item = iterator.next();
      var name = item.getName();
      var campaignName = item.getCampaign().getName();
      Logger.log("Enabling campaign: '" + campaignName + "' ad group: '" + name + "'");
      item.removeLabel(currentSetting.labelToAdd);
      
      item.enable();
      currentSetting.enabledCounter++;
      currentSetting.enabledList.push("campaign: '" + campaignName + "' ad group: '" + name + "'");
    }
    
    
    
    
    // Ads
  } else if(currentSetting.scope.toLowerCase().indexOf("ad text") != -1) {
    
    var adIterator = AdWordsApp.ads()
    .withCondition("LabelNames CONTAINS_ANY ['" + currentSetting.labelToAdd + "']")
    .get();
    
    
    while(adIterator.hasNext()) {
      
      var ad = adIterator.next();
      var adHeadline = ad.getHeadline();
      var description1 = ad.getDescription1();
      var description2 = ad.getDescription2();
      var displayUrl = ad.getDisplayUrl();
      ad.removeLabel(currentSetting.labelToAdd);
      
      Logger.log("Enabling ad: " + adHeadline + " " + description1 + " " + description2 + " " + displayUrl);
      var fullAdText = adHeadline + " " + description1 + " " + description2 + " " + displayUrl;
      
      
      ad.enable();
      currentSetting.enabledCounter++;
      currentSetting.enabledList.push(fullAdText);
      
    } // while(adIterator.hasNext())
    
    
    
    // Keywords
  } else if(currentSetting.scope.toLowerCase().indexOf("keyword") != -1) {
    
    var iterator = AdWordsApp.keywords()
    .withCondition("LabelNames CONTAINS_ANY ['" + currentSetting.labelToAdd + "']")
    .get();
     
    
    while(iterator.hasNext()){
      var item = iterator.next();
      var name = item.getText();
      var campaignName = item.getCampaign().getName();
      var adGroupName = item.getAdGroup().getName();
      Logger.log("Enabling campaign: '" + campaignName + "' ad group: '" + adGroupName + "' kw: '" + name + "'");
      item.removeLabel(currentSetting.labelToAdd);
      
      
      item.enable();
      currentSetting.enabledCounter++;
      currentSetting.enabledList.push("campaign: '" + campaignName + "' ad group: '" + adGroupName + "' kw: '" + name + "'");
      
    } // while iterator hasnext
  } // if scope == keyword
}

/* getTimeInThisAccount
// ----------------------
// Deals with getting the current time and date in this account
// using the timezone settings of the account.

// returns all values in currentSetting.thisAccountTime object
*/
function getTimeInThisAccount() {
  var weekday = new Array(7);
  weekday[0]=  "Sunday";
  weekday[1] = "Monday";
  weekday[2] = "Tuesday";
  weekday[3] = "Wednesday";
  weekday[4] = "Thursday";
  weekday[5] = "Friday";
  weekday[6] = "Saturday";
  
  var timeZone = AdWordsApp.currentAccount().getTimeZone();
  //Logger.log("time zone: " + timeZone);
  var date = new Date();
  
  var thisAccountTime = new Object();
  thisAccountTime.dayOfWeek = parseInt(Utilities.formatDate(date, timeZone, "uu"));
  thisAccountTime.dd =parseInt(Utilities.formatDate(date, timeZone, "dd"));
  thisAccountTime.weekday = weekday[thisAccountTime.dayOfWeek];
  thisAccountTime.HH = parseInt(Utilities.formatDate(date, timeZone, "HH"));
  thisAccountTime.timeZone = timeZone;
  
  
  return(thisAccountTime);
}

/* createLabel(name, description, backgroundColor)
// ------------
// Makes sure the label doesn't already exists before creating it
// 
*/
function createLabel(name, description, backgroundColor) {
    
    var labelIterator = AdWordsApp.labels()
     .withCondition("Name CONTAINS '" + name + "'")
     .get();
    
    if(labelIterator.hasNext()) {
      Logger.log("Label already exists");
    } else {
      Logger.log("Label needs to be created: " + name + " desc: " + description + " color: " + backgroundColor);
      if(description && backgroundColor) {
        AdWordsApp.createLabel(name, description, backgroundColor);
        Logger.log("Label created");
      } else if (description) {
        AdWordsApp.createLabel(name, description);
        Logger.log("Label created");
      } else {
        AdWordsApp.createLabel(name);
        Logger.log("Label created");
      }
    }
  }	  
  /* checkIfLabelIsUsed(scope, labelName)
// --------------------------------------
// Check is a label that will be used to search for entities is actually used by at
// least 1 of those entities.
//
// This prevents weird cases where the script fails without error due to a missing label
// 
*/
  function checkIfLabelIsUsed(scope, labelName) {
    var entitiesWithLabel = 0;
    var labelIterator = AdWordsApp.labels()
    .withCondition('Name = "' + labelName + '"')
    .get();
    if (labelIterator.hasNext()) {
      var label = labelIterator.next();
      if(scope.toLowerCase().indexOf("campaign") != -1) entitiesWithLabel = label.campaigns().get().totalNumEntities();
      if(scope.toLowerCase().indexOf("ad group") != -1) entitiesWithLabel = label.adGroups().get().totalNumEntities();
      if(scope.toLowerCase().indexOf("ad text") != -1) entitiesWithLabel = label.ads().get().totalNumEntities();
      if(scope.toLowerCase().indexOf("keyword") != -1) entitiesWithLabel = label.keywords().get().totalNumEntities();
      return(entitiesWithLabel);
    }
    
    if(!entitiesWithLabel) {
      Logger.log("No campaigns use the label '" + currentSetting.labelName + "' so this script won't do anything. Update your settings on optmyzr.com with the name of a label that is used for at least 1 campaign.");
    } 
  }
  
  /*
  // emailType can be: notification or warning
  */
  function sendEmailNotifications(emailAddresses, subject, body, emailType ) {
	
    if(emailType.toLowerCase().indexOf("warning") != -1) {
      var finalSubject = "[Warning] " + subject + " - " + AdWordsApp.currentAccount().getName() + " (" + AdWordsApp.currentAccount().getCustomerId() + ")"
    } else if(emailType.toLowerCase().indexOf("notification") != -1) {
      var finalSubject = "[Notification] " + subject + " - " + AdWordsApp.currentAccount().getName() + " (" + AdWordsApp.currentAccount().getCustomerId() + ")"
    }
    
    if(AdWordsApp.getExecutionInfo().isPreview()) {
      var finalBody = "<b>This script ran in preview mode. No changes were made to your account.</b><br/>" + body;
    } else {
      var finalBody = body;
    }
    
	MailApp.sendEmail({
        to:emailAddresses, 
        subject:  finalSubject,
        htmlBody: finalBody
      });
    
    if(DEBUG == 1) Logger.log("email sent to " + emailAddresses + ": " + finalSubject);

  }
  
  function getFloat (input) {
    if(!input || input == "" || typeof(input) === 'undefined') var input = "0.0";
    input = input.toString();
    var output = parseFloat(input.replace(/,/g, ""));
    return output;
  }

Wooooah, that’s a crazy long script, right? Fortunately there are only a few parameters at the top in which you have to tailor to your specific needs.

Okay, so first things first, install the above code to an individual AdWords account (NOT an MCC account).

Here are the lines you’ll need to update in the script to make it work:


<strong>currentSetting.scope = "Account";</strong>

Enter a value of either: Account, Campaign, Ad Group, Keyword, or Ad
This is the level at which the maximum budget will be enforced.


<strong>currentSetting.maxCost = parseFloat("1");</strong>

Enter a decimal value that represents the maximum cost each item is allowed to have.


currentSetting.budgetPeriod = "Daily";

Enter a value of either: Daily, Monthly, Weekly Sun-Sat, Weekly Mon-Sun
This is the time frame for the budget, i.e. the period during which the maximum cost can be accrued.


currentSetting.labelName = "Label name to check";

Enter the name of the label that you’ve added to the items you want the script to check. If you want to check all items of your selected scope (e.g. all keywords), then leave this blank.


currentSetting.labelToAdd = "stopped by budget script";

Enter the name of the label you want the script to add to all items that exceed the allowed budget. This will make it easy for you to find these items in an account, and it is also needed for the script to know what should be re-enabled at the start of a new period.


currentSetting.email = "[email protected]";

Enter the email address of the person to notify whenever a budget has been exceeded or whenever the script makes any changes to the account.


currentSetting.pauseItems = "yes";

Enter a value of either: yes, no
This says if the script should pause items that exceeded the budget (yes) or not (no).


currentSetting.reEnableItems = "yes";

Enter a value of either: yes, no
This says if the script should re-enable any items that were paused by the script when a new budget period commences. The script must be set to run hourly for this to work.

Conculusion

This is a fairly simple script, to be honest, but hopefully, it will help you overcome some of the issues related to how AdWords treats budgets. We recently started using Google AdWords and have used the above script to combat our daily/monthly and yearly budgets in which we spent on Google AdWords, we’ve tested it for quite a while now and it seems pretty solid!

As always, we love to help others… and we hope this helps you too!

Let us know in the comments if this has helped you… If you have any pointers for further improvements… awesome! let us know and we’ll update this blog post to help future users.

 

Nathan da Silva - Profile

Posted by: Nathan da Silva

Nathan is the Founder of Silva Web Designs. He is passionate about web development and web site design. His expertise is WordPress & Magento as well as many other frameworks. Would you like to work with Nathan? Send him an email on [email protected]

It’s good to share

How Using an Email Validator Increases the Success of Your Email Marketing Campaigns

Your email marketing campaign is an investment, and you’re probably putting a lot of thought and time into its planning and creation. A big part of protecting your return of investment is using an email validator. 

What is an email validator?

It’s a system that removes old and risky email addresses from your database, thus enhancing your marketing and business communication.

You want to send out your newsletters with confidence that the message will be read. With plenty of personal and business emails being sent all the time, you’ve got a lot of competition – even if your email ends up in the inbox. By using an email validator, you’re saving yourself the frustration of so many of your emails not even having a chance.

An email validator boosts your sender reputation

Do you know how some businesses start to get a bad rep? This principle plays out in email land, too.

Think of a bad email sender reputation like a local restaurant receiving a terrible grade from the health inspector. Bulk email senders have something called “email sender reputation.” Internet Service Providers (ISPs) assign these scores based on a number of factors.

Are a lot of the emails you send bouncing? Maybe you have a lot of addresses on your list that aren’t valid anymore.

Do you constantly get spam complaints? That means you’ve got abuse emails in your database. These are people who may habitually sign up for email lists and then, for reasons of forgetting or simply being careless, will mark emails as spam. You don’t want these on your list, because they cause your emails to land in the spam folder or in Google’s Promotions tab.

What an email validator does is rejuvenate that valuable database you have. It removes the invalid, abuse, and other undesirable addresses from your list. Some call it “cleaning” or even “scrubbing” your list.

“Quality over quantity” applies to email marketing as well

You may ask, “but won’t cleaning decrease the number of people on my list?” Having a high quantity of addresses isn’t what’s important – it’s having email addresses of high quality.

Going back to the restaurant analogy, the health inspector isn’t concerned with how much food you have in your refrigerator. If you have a lot of mouldy, rotten food, then you’ve got a problem!  

Using an email validator increases your sender reputation exponentially and gives you a higher return on investment. You’ve put a lot of hard work, time, and money into your campaign. By allowing more receptive candidates who want to receive your messages, you’re decreasing costs while increasing revenue.

How to validate your email list

There are two ways in which you can keep your email list in tip-top shape:

  • by cleaning it in bulk. This means you’ll upload it on an email validation platform and get it back clean within a few hours – or even a few minutes.
  • by using an email validation API. This is a piece of code you can copy and paste on your website. Once activated, the API will check every new signup in real-time.

Bonus tips to help you reach the inbox

People juggle their busy day-to-day, all while being exposed to a constant deluge of advertisements on billboards, social media, television and other media outlets. Email marketing is one of the most valuable ways to reach your target audience and keep you in their thoughts.

For a product or service to sell consistently, increasing your customer’s awareness is of paramount importance. The old adage “out of sight, out of mind” can definitely be true when you consider this age of constant distraction we live in.

Most businesses prioritise their exposure and their reputation, and for good reason. Of course, the quality and consistency of your emails are crucial, but just imagine if these masterpieces of marketing you’ve worked so hard on are never even seen.

To avoid that and achieve better results, here are a few tips:

  • once you ensure your email list is up-to-date, send your emails regularly, like on the same day of the week. It’ll help you build familiarity and trust.
  • write your subject lines carefully and avoid using any spammy words, such as “money” or “for free.”
  • make your content informative and engaging. The more people interact with it, the better your sender reputation will become.
  • prune inactive subscribers regularly. Your email service provider should isolate them from your list, so you can remove if they don’t click your messages for more than six months.

Conclusion

Email marketing has the greatest ROI of any other way of reaching a mass audience. If you are like most people, you check your email before you check social media. Not everyone is on all of the big social media channels: Facebook, Instagram, Twitter, etc. However, pretty much everyone, young or old, uses email. 

Using an email validator ensures your email newsletters and promotions end up in the inbox and not in the spam folder. 

Remember your company’s reputation is on the line. It’s easy to do and quick to implement and protects this very valuable asset and investment – your email list and the efforts and expenses associated with it. Make sure to treat it that way.

Author bio: Paul Leslie is a freelance writer, researcher and interviewer. He has recorded more than 700 interviews distributed via radio and podcasts. He likes watching and rewatching movies and is always down to try a new restaurant.

 

It’s good to share

The Best Instagram Tools To Drive Traffic And Sales To Your Website in 2020

Website traffic is the lifeblood of any business, and Instagram tools can help. High-quality website traffic is important because it opens up opportunities for you to increase your brand awareness, nurture leads, make sales, and grow your business in a variety of ways.

You can get website traffic from multiple locations. SEO is a big help when it comes to making your website rank in search results. Backlinks, or links to your website from other sites, can also help. But there’s one important source of website traffic you may not have considered in the past: Instagram! You can use Instagram tools to help your website get more traffic and more sales. Here are the top three tools that can increase your web traffic from the ‘gram.

Kicksta for Follower Growth

Kicksta is an Instagram growth service that helps companies get real Instagram followers, and it’s a great Instagram tool that you can use to drive web traffic. Having genuine followers is hugely important for your business. You might have thousands of followers, but if they’re bots instead of real people, your account could be marked as spam. Plus, fake followers won’t engage with your content — which means they won’t visit your website or make a purchase either.

Kicksta recognises how important this is and helps you find genuine followers that care about what you have to offer. When you sign up with Kicksta, you provide some information about your target audience. Then Kicksta will use your account to begin liking posts from users who fall into your ideal demographic. By targeting accounts that are similar to yours, Kicksta finds people who are truly interested in your brand — and many of those users will come to check out your account and then follow along.

By using Kicksta to grow your account, you can drive web traffic and sales too. The more followers you have, the more your brand awareness grows — and more people will see your posts advertising your products. Kicksta boosts your sales by getting more eyes on the marketing content you post.

Before registering, make sure your Instagram business profile is set up and ready to go. Include the link to your website in your bio so the new users who are exposed to your account can easily visit your site. You can even use Bitly to include multiple links. Bitly links let you track clicks, gaining valuable insight about who’s visiting your website and when they’re doing it.

Bottom line? Kicksta can take your Instagram account and help it see major growth. Register for this Instagram tool for just $49/month at the standard plan.

Crowdfire for Social Media Management

Crowdfire is a social media management tool trusted by over 19 million users around the world. You can use Crowdfire to schedule posts in advance, resulting in more website traffic and sales. Having a consistent scheduling plan attracts more followers — people want to follow accounts that put out regular high-quality content, and posting often helps you stay at the top of your followers’ minds.

Crowdfire is also helpful because you can layout your grid before posting. Your Instagram theme should be carefully curated, with on-brand images and graphics. And by mapping things out ahead of time, you can make sure your grid has a cohesive look.

What are some of Crowdfire’s features? Besides basic scheduling capabilities, this platform recommends hashtags for you and supports video posts. Crowdfire notifies you of Twitter and Facebook mentions so you don’t miss anything. This Instagram tool even provides advanced post and competitor analytics.

You can use Crowdfire to publish content from your own blogs and websites by using the Chrome extension or an RSS feed connected. Schedule posts that promote your website and then include a CTA in the caption (“For more, click the link in our bio!”). This is a great way to drive more traffic to your website from your Instagram account.

Crowdfire also helps you manage all of your social media accounts in one place. No more hopping back and forth from tab to tab, closing Twitter and opening Instagram — instead, you can see everything in one convenient dashboard. All paid Crowdfire users can link Twitter, Instagram, Pinterest, Facebook, and LinkedIn.

If you’re on the fence about Crowdfire, take advantage of their free trial. After that, monthly payment plans range from $7.48 for solopreneurs to $74.98 for agencies.

Iconosquare for Analytics

Instagram tools like Iconosquare can help you keep track of your Instagram analytics — and if you don’t keep up with your analytics, you’re just wasting your time. Tracking your analytics is important because it gives you insight into what’s working with your marketing strategy and what isn’t. When you know that, you can optimise your future posts and see better results. Instagram analytics is a truly valuable tool as you work to drive more website traffic.

With Iconosquare, you can create an analytics report to keep track of how your posts are performing. Hashtags, Instagram stories, conversations, and more are all included in Iconosquare’s analytics. You can schedule a free monthly, weekly, or daily report to receive via email.

Iconosquare can also help you figure out which posts are driving the most clicks to the link in your bio. By analysing those posts, you can come up with ways to improve posts that aren’t performing as well, such as tweaking your captions or CTAs. Then run some tests to find out what works best.

What other features does this Instagram tool provide? Iconosquare helps you track your hashtag performance; you can use hashtags to grow your account and eventually make more sales. Iconosquare will also identify your top followers, track follower growth, and notify you of anyone who unfollows you. The platform also offers location-based post tracking and has data filters.

Finally, Iconosquare offers one feature that other analytics platforms might not: it tracks not only your posting habits but your competitors’ too. Keep an eye on what your competitors are sharing and what hashtags they’re using. This can help you tweak your own strategy and get a leg up.

Iconosquare has multiple payment plans that start at $29 per month for small businesses. It’s an affordable method of tracking your analytics and understanding what you need to do to get more web traffic.

Social media platforms such as Instagram are an effective (and easy) way to drive traffic and sales. By using Instagram tools like Kicksta, Crowdfire, and Iconosquare, you can make the process even easier. Use these tools to help your website traffic grow in 2020.

On the subject of Instagram, there are also various ways to schedule Instagram posts using automation. You can read more about this here.

 

Nathan da Silva - Profile

Posted by: Nathan da Silva

Nathan is the Founder of Silva Web Designs. He is passionate about web development and web site design. His expertise is WordPress & Magento as well as many other frameworks. Would you like to work with Nathan? Send him an email on [email protected]

It’s good to share

Easy-to-follow Digital Marketing Strategies for Business Owners

In the modern world, every company should be on the Internet. Being on the Internet is the fastest way to grow your business and have an explosion of sales. It is also an easy way to continually sell without having any salespeople around. A big factor in success on the Internet is your knowledge of digital marketing. You must understand what it takes to get customers online through various marketing channels. There are many considerations that need to be made, but here are some strategies that will keep you ahead of the competition when you are marketing online.

Know Your Desired Outcome

The first step to success is knowing your desired outcome. Your digital marketing campaign should always have a goal. Everything you do needs to be a means to that end. For example, if you want to start a Facebook campaign then you need to understand how this will fit into your overall goals. Without this fundamental understanding, you will be rolling around in a sea of nothing with no goals. So the first step is to set your goals.

Find Your Potential Customers

A business is nothing without customers. Before you find customers you have to know who they are. You have to be able to understand fundamental questions about your business such as the people you are targeting. You have to understand them at a deeper level so you know their behaviours and dreams. There are many hangout spots on the Internet, and you should be able to understand where your potential customers are hanging out. By getting this information you are less likely to waste money on advertising to people who aren’t your customer base.

Related Article: 5 Tools To Boost Your Conversion Rates

See the Competition

Once you have figured out your customer base, then is all about knowing your competition. Your competition will play a large role in whether you are successful or not. After all, they are fighting you for the same customers and your success depends on your ability to out match them. Winning on the Internet means researching your competition and seeing what their strengths and weaknesses are and how you can take advantage of those weaknesses.

Delegate to Your Team

Utilising your team is also essential in the digital marketing realm. Making sure that everyone on your team knows their role will help things move smoothly, and the team will be like a good world machine. Making sure that the people assigned to certain roles have expertise will also help things go along faster.

Use Search Engine Optimisation and Marketing

Search engines are often the best ways to get traffic to your website. Most people start their search online with a search engine. There are multiple ways to utilise search engines to find customers. You have SEO and SEM. You can either by advertising spots on various keywords or you can optimise your website to rank near the top of a search engine. Both methods have potential downsides, but also an amazing upside. For example, you can get traffic instantaneously by using SEM or you can build long-term traffic for free with SEO. Both strategies should be a critical part of your marketing endeavour.

Be on Social Media Sites

Most people hang out on social media sites. Having a strategy for marketing to people on social media sites will take your digital marketing efforts far. You have to understand the platform and know what to do and what not to do. For example, sending inspirational images on Instagram could help boost your traffic immensely. On other social media sites, this would not work. Being able to match the strategy with the social media site is a critical part of your success.

Related Article: How to Boost your Social Media Using WordPress

Have Great Content

Content is how people interact with your website. You need to have great content that resonates with the reader in order to keep them engaged. If they don’t finish your content, then you won’t get them to stick around long enough to be a customer. Having content that will keep them around should be your top priority. Hiring dedicated content marketing staff can also be helpful.

Don’t Forget Their Mobile Devices

Most people browse the Internet on their mobile devices. Your website and other digital marketing campaigns must be mobile-friendly. This means they should load quickly and display correctly when being browsed from a mobile device.

Send Them an Email

Email marketing should be a fundamental strategy for your business. Having their emails allows you to reach out to them and build trust and a long-term relationship. This will help you when you go to ask for the order later. Email marketing is one of the few ways to truly have your own customers online.

Use Analytics

Understanding analytics will help you get very far in Internet marketing. Analytics is the data collected from user information. This can tell you trends and how certain visitors behave on your websites. Using this information, you can optimise your websites to improve conversion rates and your ROI.

Digital marketing is difficult. However, by following these easy to understand instructions you can build your own empire online. As a business owner, crushing the competition will become easier and you will be able to outperform them no matter the channel.

 

Nathan da Silva - Profile

Posted by: Nathan da Silva

Nathan is the Founder of Silva Web Designs. He is passionate about web development and web site design. His expertise is WordPress & Magento as well as many other frameworks. Would you like to work with Nathan? Send him an email on [email protected]

It’s good to share

4 Instagram Growth Strategies That Get Followers & Clicks

When it comes to growth, Instagram is the gift that keeps on giving. It’s popular, fast-growing, and with a daily active user base of over a billion, there’s plenty of scope for growth.

For businesses, Instagram can increase sales, boost lead generation, and drive engagement again and again. But before you can feel those benefits, you first need a solid follower base to market to. Read on to discover how to source exactly that.

Want to take your Instagram further? Look at tools to plug the platform into your Shopify store as an extra sales channel. Learn how Shopify apps work here.

Consistency and regularity is key

Whether you’re a solopreneur running a venture out of your spare bedroom or a big brand operating a number of enterprises, your social strategy requires time and effort.

If you really want to increase your Instagram follower count, you need to bring consistency and regularity to your posting schedule.

As well as giving your followers more of what they want, this is also good for your Instagram ranking too. The platform looks favourably on those accounts that frequently post fresh content, so it’s worth pursuing.

If you want to get your content right, first conduct some research into the best time to post. This will vary depending on your industry, target audience’s country of residence, and so on, so make sure your research is on point.

Top tip: you can make your Instagram strategy that bit easier but using a tool like Sendible. This lets you plan and schedule your posts ahead of time, and they will post automatically without you needing to do it manually.

Look to your followers for engaging user-generated content

Instagram is a treasure trove of photos, videos, and images. More than 95 million photos are posted to the platform every day — so why not mine this for your own benefit?

In terms of Instagram growth, user-generated content (UGC) is cost-effective and delivers results. It’s genuine, produced by real people for brands. As a result, sharing UGC on your Instagram provides valuable social proof for your business.

The quickest and easiest way to source this is with a UGC competition. Simply offer followers the opportunity to win a prize following submission of their own content, centred around a specific theme. For instance, you might ask people to submit snaps of their view from their place of work.

Encourage users to submit their content by tagging you or mentioning you in the caption. This boosts the visibility of your brand to their followers, expanding your reach into the bargain.

Take note though: sourcing UGC from tags and mentions is only available for brands with business accounts. If you haven’t made the switch yet, do so. Not only can you collect UGC easier, but you can also access a whole host of other features to help you grow your Instagram.

Embrace Stories for a dynamic Instagram profile

Instagram has a dazzling array of features to help you connect with your followers and expand your audience. But perhaps the most engaging of these is Stories.

Stories are ephemeral content that stays on your profile for 24 hours — once it’s gone, it’s gone. They also offer great scope for flexing your creative muscles and driving followers and clicks at the same time.

Each Story can be customised with a variety of different Stickers: polls, gifs, countdowns, questions, and so on. Use these to encourage follower engagement, sourcing their opinion and sharing it across your Story.

Similarly, you can use Stories to host contests and giveaways, with friend referrals required to enter. These drive engagement and add diversity to your existing Instagram strategy too.

As with the rest of your Instagram feed, you should post regularly on your Story to get it seen. A fresh Story that is regularly updated gives your Instagram account dynamism, compelling people to check in often.

Plus, when you share on your Story, you automatically push it to the front of your followers’ feeds. This lets you circumvent the feed algorithm and get your Stories seen by your followers as soon as they open the app.

Give the people what they want

The true driver of growth on Instagram is high-quality content. Beautiful, engaging content is what the platform is all about, and it’s what gives your followers a reason to visit your account — so why not give them what they want?

In order to get this right, you need to get inside the minds of your followers. What do they regularly engage with? What kind of content sees the most likes or comments? Know this, and you’ll know what to post to keep your followers hooked (and increase new follows).

It’s worth looking at your analytics here. Instagram offers built-in analytics that let you identify which posts perform best. Analyse your top-performing posts, identify why they worked so well, and use this to inform your next round of Instagram content ideation.

If you’re stuck for time and need some quick inspiration, you could also check out your competitors.

Trawl other brands in your industry and see what kind of content they post and what does (or doesn’t) work. At the same time, check out what hashtags your competition uses too. This helps you spot the most popular, which you can in turn use for your own content.

Instagram might just be one part of your overall growth strategy, but it’s an important one. With such an array of benefits on offer from the platform, it’s worth doing all you can to increase your follower base.

Follow the tips above and start growing your Instagram followers slowly but surely in 2020.

 

Nathan da Silva - Profile

Posted by: Nathan da Silva

Nathan is the Founder of Silva Web Designs. He is passionate about web development and web site design. His expertise is WordPress & Magento as well as many other frameworks. Would you like to work with Nathan? Send him an email on [email protected]

It’s good to share