AdWords Quality Score Tracker Version 2.0 – Now with Labels

Google just made some very helpful additions to AdWords Scripts. One of them is that you can now use labels within scripts. To demonstrate how powerful this feature is, I’ve rewritten the Quality Score Tracker.

The first version required a spreadsheet with a list of keywords to track. It also required you to get the spreadsheet first. This new version relies on labels to mark keywords for tracking and the setup is much easier.

Newer Version Available
There’s a newer and much better version of this script that

  1. has more features (including a dashboard with charts!),
  2. is much faster and
  3. is much easier to use.

Check out the newest Quality Score Tracker.

What the Script does

Just like the first version, this script tracks keyword quality scores over time. When run, the script goes through all keywords you’ve marked with a certain label and checks their quality scores. Changes are logged and can be emailed to you. The script also writes all values into a spreadsheet, so that over time you’ll get a complete history.

The recommended use is to run the script regularly for your top keywords to see changes over time. You can use it for a quick diagnosis, but you can also use the history in the spreadsheet for an in depth analysis later.

The Setup

As I mentioned, this one is much easier than before. Again, three steps are needed:

  1. Get the script
  2. Run the script once to create the label
  3. Mark all keywords you want tracked with the label

The Script

Go to your AdWords account and click Bulk operations (formerly Automation) and then Scripts on the left hand side of the AdWords interface. Next, click Create script. Copy and paste the following code:

var email_address = "YOUR_EMAIL_HERE"; // Change this to be notified of changes
var label_name = "Track QS";

function main() {
  var labelIterator = AdWordsApp.labels().withCondition("Name = '" + label_name + "'").get();
  // If there is no label, this is the first time this script is running
    // Create the spreadsheet
    var spreadsheet = SpreadsheetApp.create("AdWords Quality Score Tracker");
    Logger.log("Spreadsheet for QS history created: " + spreadsheet.getUrl());
    var sheet = spreadsheet.getActiveSheet();
    sheet.setName("QS history");
    // Put in the table headings
    sheet.getRange(1, 1, 1, 6).setValues([["Date", "Campaign", "AdGroup", "Keyword", "Quality Score", "Change"]]);
    sheet.getRange(1, 1, 1, 6).setFontWeight("bold");
    // Create the label and save the spreadsheet's URL in the description
    AdWordsApp.createLabel(label_name, "Marks Keywords for QS tracking. Results are here: " + spreadsheet.getUrl() + " (keep URL in this description)", "#339999");
    Logger.log("Label '" + label_name + "' has been created. Apply this label to all keywords want to track. Then run the script again.");
    for(i = 1; i <= 10; i++){ AdWordsApp.createLabel("QS: " + i, "Used for QS comparison.", "#ffffff"); } Logger.log("Ten additional labels ('QS: 1' to 'QS: 10') have been created. Those are needed by the script to compare old and new Quality Scores later. You can just ignore those."); return; } // There is a label so get it and get the spreadsheet's URL from its description label =; var matches = new RegExp(' (http(s?)://[^ ]+) ').exec(label.getDescription()); if (!matches || !matches[1]) { throw "Couldn't get spreadsheet URL from label description: " + label.getDescription(); } var spreadsheetUrl = matches[1]; var spreadsheet = SpreadsheetApp.openByUrl(spreadsheetUrl); var alert_text = new Array(); var history = new Array(); var currentTime = new Date(); var today = (currentTime.getMonth() + 1) + "/" + currentTime.getDate() + "/" + currentTime.getFullYear(); var keywordIterator = label.keywords().get(); var line_counter = 0; while (keywordIterator.hasNext()) { var keyword =; line_counter++; var current_quality_score = keyword.getQualityScore(); keywordLabelsIterator = keyword.labels().withCondition("Name STARTS_WITH 'QS: '").get(); if(keywordLabelsIterator.hasNext()){ keyword_label =; var matches = new RegExp('QS: ([0-9]+)$').exec(keyword_label.getName()); old_quality_score = matches[1]; }else{ old_quality_score = 0; } // For the history also note the change or whether this keyword is new if(old_quality_score > 0) var change = current_quality_score - old_quality_score;
    else var change = "NEW";
    var row = [today, keyword.getCampaign().getName(), keyword.getAdGroup().getName(), keyword.getText(), current_quality_score, change];
    // If there is a previously tracked quality score and it's different from the current one...
    if(old_quality_score > 0 && current_quality_score != old_quality_score){
      // Make a note of this to log it and possibly send it via email later
      alert_text.push(current_quality_score + "\t" + old_quality_score + "\t" + change + "\t" + keyword.getText());
      // Remove the old label
    // Store the current QS for the next time by using a label
    keyword.applyLabel("QS: " + current_quality_score);
  if(line_counter == 0){
    Logger.log("Couldn't find any keywords marked for quality score tracking. To mark keywords for tracking, apply the label '" + label_name + "' to those keywords.");
  Logger.log("Tracked " + line_counter + " keyword quality scores. To select different keywords for tracking, apply the label '" + label_name + "' to those keywords.");
  // Store history
  var history_sheet = spreadsheet.getSheetByName('QS history');
  history_sheet.getRange(history_sheet.getLastRow()+1, 1, history.length, 6).setValues(history);  
  // If there are notes for alerts then prepare a message to log and possibly send via email
    var message = "The following quality score changes were discovered:\nNew\tOld\tChange\tKeyword\n";
    for(i = 0; i < alert_text.length; i++){
      message += alert_text[i] + "\n";
    // Also include a link to the spreadsheet
    message += "\n"
      + "The complete history is available at "
      + spreadsheet.getUrl();
    // If there is an email address send out a notification
    if(email_address && email_address != "YOUR_EMAIL_HERE"){
      MailApp.sendEmail(email_address, "Quality Score Tracker: Changes detected", message);

If you want to receive notifications, replace YOUR_EMAIL_HERE with your email address. If you don’t want any notifications, just ignore this.

Name the script “Quality Score Tracker v2” and click Save.

The First Run

Click the button Run script now. AdWords will ask you to authorize the script before it can be executed. The first run takes about ten seconds.

The script will now create the spreadsheet where the history will be logged. The spreadsheet’s URL can be found in the logs, after the first run. The script will also create some labels. The label Track QS will be needed for the next step. The other ten labels (QS: 1 to QS: 10) are needed by the script and should be ignored.

Apply the label Track QS to select keywords for quality score trackingThe Keywords

Go back to your campaigns and hit F5 to reload the AdWords interface (otherwise the newly created labels won’t be available). Go to the keyword column and select the keywords you want tracked. Apply the label Track QS to those keywords.

You can always come back to this and remove the label or apply it to new keywords at any time.

Using the Script

To run the script, click the button Run script now. You can also schedule the script to run regularly.

Execution takes a while, especially if it’s the first time you track new keywords. You don’t have to wait around for the script to finish. If changes are found, the script sends an email notification and also logs the notification (to be found under View details in the script logs). The first time the script runs it naturally won’t be able to find any changed quality scores.

If at any point you want to change the set of tracked keywords, simply use the label Track QS to change it.

To analyze the quality score history of your keywords, use the spreadsheet. Its URL can be found in the logs after the script has been run, but it will also be emailed to you (if you provided your address). You can also find the URL in the label’s description.

There’s nothing you can break in the spreadsheet, but I’d recommend to copy the contents into a new sheet if you want to play with the data. To focus on changes over time, filter out the zeros in the Change column.

Limitations and Warnings

Scripts have gotten a lot faster and it wouldn’t be a problem to track hundreds, maybe thousands of keywords with it. You could also run the script hourly. If you do it, you might go crazy.

We’ve done a lot of testing and tracking with keyword quality scores and found that they change quite frequently. Most changes don’t mean anything and you won’t be able to see any difference in performance. Chances are that too much tracking will give you a lot to worry about.

So my advice is still to just track the ten or twenty most important keywords and do it once a week or once a month.

For Scripts Developers: A new Feature

If you’ve written scripts before you might have noticed that this one does something that previously wasn’t possible with AdWords Scripts: It stores data in the account.

This is especially helpful for the spreadsheet’s URL. Previously there was no way for a script to “remember” information like this between runs: If you wanted to use the same spreadsheet twice you had to put its URL into the script’s code. That’s why you had to get the spreadsheet and save its URL in the first version.

In this version the script just checks if there a label called Track QS. If not, it creates the spreadsheet and then the label, storing the spreadsheet’s URL in the label’s description. Afterwards, the script just gets the label and with it the spreadsheet.

This way of storing information opens up new possibilities for AdWords Scripts. You could use labels to store information directly (as done with the ten QS labels), or you could do it indirectly, by storing URL’s in label descriptions. Of course, usually there won’t be a reason not to write a URL directly into the script, but it’s a nice trick if you want to share a script and make the setup as easy as possible.

It would also be possible to use label descriptions to store some other pieces of data, like the last time a script was executed. However, with room for only 200 characters, the storage capabilities of label descriptions are very limited.

Minor Update 7 / 7 / 2014

Changed the way the spreadsheet is opened from byId to byUrl. If the script works fine, there’s no need to upgrade.