An AdWords Script to Track Quality Scores

This script is outdated, but there’s a new one that is much better and much easier to use. Check out the newest Quality Score Tracker.[/su_box]Last month I wrote about AdWords Scripts and came to the conclusion that there is basically just one really exciting thing about them: You can share them! Besides Google, I haven’t found anyone doing that, so I’d like to make the first step.

The script I’m presenting here tracks selected keyword quality scores over time. If a quality score changes, it can notify you via email. The script also uses a Google spreadsheet to track the quality score of each keyword. Over time it logs the complete quality score history of your keywords in a simple, easy to use spreadsheet.

Newer Version Available
This script is outdated, but there’s a new one that is much better and much easier to use. Check out the newest Quality Score Tracker.

You can use this script to track quality scores regularly and see changes over time. You can also use it to diagnose a set of keywords when needed. And, of course, you can use the history for your own analysis later.


Even though scripts are easy to share, a quick setup is necessary here. There are three steps:

  1. Get the spreadsheet
  2. Get the script
  3. Put the keywords you want to track into the spreadsheet (up to about 100, preferably no more than 80)

The Spreadsheet

Log into your AdWords account and open this mostly empty spreadsheet. You might have to sign in there, too (top right corner). Click File and then Make a copy (choose whatever name you like). The spreadsheet is now in your account and no longer publicly accessible. Keep the browser tab open.

The Script

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

var spreadsheet_url = "INSERT_SPREADSHEET_URL_HERE";
var email_address = "YOUR_EMAIL_HERE";

function main() {
  var matches = new RegExp('key=([^&#]*)').exec(spreadsheet_url);
  if (!matches || !matches[1]) throw 'Invalid spreadsheet URL: ' + spreadsheetUrl;
  var spreadsheetId = matches[1];
  var spreadsheet = SpreadsheetApp.openById(spreadsheetId);
  var sheet = spreadsheet.getSheetByName('Input Keywords');
  var sheet_values = sheet.getDataRange().getValues();
  var result_range = new Array(); // holds the results to write back
  var alert_text = new Array();
  var history = new Array();
  var currentTime = new Date();
  var today = (currentTime.getMonth() + 1) + "/" + currentTime.getDate() + "/" + currentTime.getFullYear();
  for(i = 1; i < sheet_values.length; i++){ // make sure there is actually some data here if(sheet_values[i][0] == "") continue; result_range[i] = [today, 0]; var campaign_name = sheet_values[i][0]; var adgroup_name = sheet_values[i][1]; // remove single quotes at the beginning of the keyword (Excel sometimes adds them in front of modified broad matches, like: '+keyword) var keyword_text = sheet_values[i][2].replace(/^[']+/g, ""); var latest_check = sheet_values[i][3]; var old_quality_score = sheet_values[i][4]; var keywordIterator = AdWordsApp.keywords() .withCondition("CampaignName = '" + campaign_name + "'") .withCondition("AdGroupName = '" + adgroup_name + "'") // this won't let us filter for phrase or exact matches so we have to remove brackets and quotation marks (broad match modifiers are fine) .withCondition("Text = \"" + keyword_text.replace(/[\[\]\"]/g, "") + "\"") .get(); while(keywordIterator.hasNext()){ var keyword =; // since we couldn't filter phrase or exact matches directly, we have to make sure that this is the right keyword if(keyword.getText() == keyword_text){ var current_quality_score = keyword.getQualityScore(); // save quality score for results result_range[i][1] = current_quality_score; // for the history we 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, campaign_name, adgroup_name, keyword_text, current_quality_score, change];
        // if we have a previously tracked quality score and it's different from the current one, we make a note to log it and send it via email later
        if(old_quality_score > 0 && current_quality_score != old_quality_score){
          alert_text.push(current_quality_score + "\t" + old_quality_score + "\t" + change + "\t" + latest_check + "\t" + keyword_text);
        // we've found the keyword we were looking for so we look no further
  // write results to spreadsheet
  sheet.getRange(2, 4, result_range.length, 2).setValues(result_range);
  // write history to spreadsheet
  var history_sheet = spreadsheet.getSheetByName('QS history');
  history_sheet.getRange(history_sheet.getLastRow()+1, 1, history.length, 6).setValues(history);
  // if we've made notes for alerts then we send them via email
    var message = "The following quality score changes were discovered:\nNew\tOld\tChange\tPreviously checked\tKeyword\n";
    for(i = 0; i < alert_text.length; i++) message += alert_text[i] + "\n";
    // also include a link to the spreadsheet
    message += "\n" + "Settings and complete history are available at " + spreadsheet_url;    
    // if we have an email address we send out a notification
    if(email_address && email_address != "YOUR_EMAIL_HERE"){
      MailApp.sendEmail(email_address, "AdWords quality score changes detected", message);
    // log the message

Go back to your spreadsheet and copy the URL from your browser’s address bar. At the beginning of the script, replace INSERT_SPREADSHEET_URL_HERE with this URL.

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” and click Save.

The Keywords

Go to the spreadsheet again and make sure to select the sheet Input Keywords. Now list the keywords you want to track, along with their campaigns and adgroups. Leave the columns for Latest Check and Latest Quality Score empty.

You can always come back to this and add or remove keywords later.

Using the Script

To run the script, click the button Run script now. Before the first run, AdWords asks you to authorize the script.

Since AdWords Scripts aren’t exactly fast, execution takes a while, but you don’t have to wait around for the script to finish. If the script finds any changes, it sends an email notification at the end. Notifications are also logged and can be found under the View details link in the script logs. In its first run the script naturally won’t find any changed quality scores.

Whenever there’s a new keyword you want to track, or an old keyword you no longer want tracked, you can always change the set of keywords in the spreadsheet. Simply add keywords to the first sheet (Input Keywords).

If you ever want to analyze the quality score history of your keywords, check the second sheet (QS history). You can’t break anything there, but it’s probably best if you copy the history so far into a new spreadsheet and then play with the data there. Filter out the zeros from the Change column to only look at changed quality scores over time.


Like all AdWords Scripts at the time of this writing, this one has a big, frustrating limitation: execution time. Since AdWords Scripts are slow and not supposed to run more than five minutes, the number of quality scores you can track is also limited.

To make matters worse, performance fluctuates, making it impossible to determine this number. With the same set of keywords, on some days the script was able to check only 50, on others 150 quality scores. The system seems to be getting faster though. Based on the last three weeks it seems that with 80 keywords or less you’re on the safe side.

Words of Warning

With this script you can track quality scores of a limited, but still big number of keywords. And you can run this script as often as you like. However, overdoing this might drive you crazy.

From my experience with this script and similar ones, I can tell that there are more quality score fluctuations than you might think. In most instances you won’t be able to find a reason for a change. Sometimes a quality score changes over night and then returns to the original value the day after. Noticing the first change, especially if it’s a drop, can make you worry and take unnecessary steps. For some people it even causes distress.

If you don’t want to go crazy over this, my advice would be to track only the ten or twenty most important keywords and not to react too quickly to changes, unless there is a really good reason.

Is this useful?

The purpose of writing this script was to try out AdWords Scripts and to see whether they are in fact something that’s easily shared. Due to the necessary setup I’m not so sure about the easy sharing with this one.

So what do you think? Is this script helpful? Do you have another idea for a useful script? I’d love to hear your feedback.


  • To accommodate keywords with apostrophes, line 32 of the script has be updated.
  • In September 2012 Google announced that AdWords Scripts can now run up to 30 minutes, so you could track six times as many keywords. Now you can also schedule scripts to run daily, weekly, or monthly. My advice is to track the most important keywords weekly or monthly.
  • As of Jan 26, a new version of this script is available here: AdWords Quality Score Tracker Version 2.0

About Martin Roettgerding
Martin Roettgerding is the head of SEM at SEO/SEM agency Bloofusion Germany. On Twitter he goes by the name @bloomarty, and you can find him regularly on #ppcchat.

  • Well done. This cannot have been easy to implement. I think you are right about not analyzing too often (same goes for most other AdWords data..).

  • Martin Roettgerding

    Thanks! The keyword report tutorial from Google was very helpful for figuring out the spreadsheet parts…

  • Hi Marty, Thanks for pushing the limits of PPC once again.
    My first thought was, could you graph this quality score data over time against CTR. Would this then allow us to get a better understanding of the true percentage of quality score that is made up of CTR? If so could this conceivably work for other quality score metrics too and give us a complete picture of all the constituents and their true influence on quality score?

  • Martin Roettgerding

    Hey Shaun,
    You’re right, comparing CTR and quality scores over time could be an interesting study. Sid Shah once published an article about the statistics of the relationship between quality score and CTR:

    However, looking at this over time should be much more interesting. The problem I see is weeding out the other influences on CTR, especially ad position. You would at least need stable bids, but keywords that gather enough data are usually the ones with the most bid changes.

    There is one approach that could be promising and it’s in these quality score fluctuations. If a quality score goes down and then back up the next day, it would be interesting to look at what happenend in the meantime. I actually started gathering data to look at this phenomenon last week. Might lead to another blog post… or just some tweets, depending on what I find πŸ˜‰

  • Hi Martin,

    thank you for that very interesting food for thought. It shows me that even if I marked some AdWords features (e.g.: scripts) as unnecessary someone else (e.g.: you ;)) is able to turn them into something very usefull and interesting for me.

    Currently I had some issues with a group of keywords and their quality score and now I cannot only check the score more easily but also have a very powerfull testing tool.

    I’m very curious – why does the quality score changes over night – even if there were no changes at all? What influences the quality score besides my own AdWords activities? And as Shaun said: comparing the quality score over time with other data like CTR will be very interesting.
    Thank you! πŸ™‚

  • Hi Martin,

    again a great post. I am experimenting a lot to get an accumulated and weighted Quality Score on Adgroup und Campaign Level. I’ve tested the wordstream Quality Score Toolkit an Tenscores for it. But the toolkit is a bit complicated to handle regularly and I don’t want to give tenscores all of our clients data. It would be interesting, if this is possible with the Adwords Scripts.

    Best from hannover

  • Sebastian

    Hi Martin,

    thank you for this great post.
    When I use your script I have some keywords with quality score 0 (in the account they have at least a QS of 7), do you have an explanation for this?


  • Martin Roettgerding

    Thank you gentlemen πŸ™‚

    David – I’m curious about those over night changes, too. I’m looking into this, but I don’t expect to find much. Those changes could be more or less random… personally, I don’t think that keyword quality scores are very reliable.

    Olaf – In theory, AdWords Scripts would be great to calculate adgroup quality scores. The problem would be performance – you might not be able to go through an entire account before the five minutes are up. But it’s worth a try… here’s how I would go about it:
    1) select all keywords with impressions >= 1 for some date range (zero impressions aren’t weighted anyway and this saves some time)
    2) make two arrays: imp_qs and imp
    3) go through keywords.
    3.1) In the array imp_qs[campaign name][adgroup name] add up impressions times quality scores from each keyword.
    3.2) In the array imp[campaign name][adgroup name] add up impressions from each keyword.
    4) Go through one of those arrays and divide imp_qs by imp to arrive at weighted quality scores
    I’m tempted to try this out…

  • Martin Roettgerding

    Sebastian – in this case the script probably wasn’t able to find the keyword in the account. It could be an issue with the match type and the spreadsheet. Do those keywords start with a plus (modified broad) or quotation marks from a phrase match? It could also be the other way around: are those characters missing from a modified broad or phrase match?

    When you download a keyword report to Excel, AdWords adds a single quotation mark before modified broad matches, so Excel doesn’t confuse the keyword with a formula. The script was designed to handle these cases, but maybe it’s something in that area that I missed…

    In any case, if you find something, feedback is greatly appreciated!

    • Sebastian

      Hi Martin,

      Thanks for your reply
      In the end it was pretty easy…I missed to put in the characters for phrase match. So it was my fault. πŸ™‚


      • Martin Roettgerding

        Ah okay, that’s good to know. Thanks for the feedback!

  • Bob Schmidt

    Very cool script! Thanks for posting.

  • LUIS

    Hi Martin,

    thank you for this fantastic post. Probably one of the most usefull post i have recently read about adwords.
    I have a problem anyway. the error comes with keywords like “d’alembert”. what do we have to do with the ‘ character?

    • Martin Roettgerding

      Hi Luis,

      Ouch, I did not test it with apostrophes – sorry!

      I’ve fixed it in the code above. The error was in line 32, so there’s the only change.
      To update your script, you can either copy the entire code or just replace line 32 with this:

      .withCondition(“Text = \”” + keyword_text.replace(/[\[\]\”]/g, “”) + “\””)

      Again, sorry for the avoidable mistake…

      • Luis

        thank you very much

  • Martin,

    Great stuff here.. Obviously you mention that the number of words tracked is the problem. With large accounts you have countless words and only tracking a few is kind a like trying to drain a swimming pool with a cotton ball. I think with some interesting modifications this could be a great script.

    I’m curious though why you don’t just ad another field to your table for the “Last time Keyword was Checked”. Then each time yous start the script pickup where you left off. You could schedule the script to run every 10 minutes or something like that and do 80 to 100 each pass thru the script… You could even do something like have the worksheet name be the Campaign and have the script build the keywords list for you. Maybe have a field to flag that you don’t want to track that word and the script would skip over to the next keyword.

    Anyway, this is great stuff.. I’m wondering how we can all work together to come up with some better solutions.


  • Martin Roettgerding

    Hey Glenn,

    Thanks for your ideas! You’re right, we could just use a spreadsheet to store how far we’ve gotten and then pick up where we left off. However, there are several reasons I didn’t do it that way.

    The main reason is that you can’t schedule a script in that way. At the time of writing, you weren’t able to schedule scripts at all. So in order to do this, you would’ve had to wait five minutes, restart the script manually, and repeat until you’re done.

    Now you can schedule a script to run daily at a specified hour. In theory, you could just copy the script and schedule one for 1 am, the next for 2 am and so on. Still, this seems excessive and the number of scripts you can schedule is limited (15?).

    Anyway, with the length of scripts now extended to 30 minutes ( it can track six times the number of keywords.

    However, I don’t think it should be done. The script is intended for tracking your most important keywords. Tracking hundreds of keywords won’t result in any actionable insights.

    Just try it: Track 500 keywords for a week and have the script email you the changes. You will probably get 100+ changed quality scores every day. It will drive you crazy πŸ˜‰

  • WOW that is probably the BEST article I have ever read about quality score. Keep up the great work.

  • hi, thanks for your post, i guess it will help me a lot. im going to test it right now πŸ™‚

  • It should be easy to do a pivot table with the dates across the top, but it doesn’t work for me for some reason.

    I am using this script with clients instead of something like TenScores as there is a bit more control and less of the privacy issue with client accounts.

  • Ed

    Not being able to track QS was one of my big problem and you solved it thank you very much.

    However, I having a problem with copy and pasting “phrase match” in google doc. When I copy all my phrase match keywords in google docs the ” disappear.

    Have you ever experience that. If I copy them one by one its fine the ” will remain but it is really time consuming.

    Thanks in advance,

  • Hey Martin

    Thanks for this awesome script. Only my AdWords sees a problem in line 57.

    The coordinates or dimensions of the range are invalid. (line 57)

    Do you know how I can solve this problem?
    I copy and paste the code and ony changed the destination url

  • I just found your script and of course I have added it in my account. BIG BIG Time saver!

  • Martin,

    I am getting this error at line 57: The coordinates or dimensions of the range are invalid. (line 57)

    What’s wrong?

    • n0er

      You need to input data into the shaded portion of the first worksheet

  • Martin Roettgerding

    Hey Bart and Dimitris,
    I’m not sure what’s wrong, it worked for months. This error would occur if the script didn’t find any keywords to track. Is it possible that this is what happend?

    I’ll do some tests, but I doubt I can get around to it today…

    Anyway, AdWords gave us some great new scripts features this Wednesday. Among other things it is now possible to use labels. I’ve rewritten the QS tracking script to track all keywords marked with a certain label. I’ll have to test it a little more, but if all goes well it’ll be out this weekend.

  • Hi Martin,

    My mistake… you script works 100%. I thought that it was pulling the keywords automatically. I pasted some data in the first sheet and it worked!

    Thank you very much mate for the good work!

  • Hi Martin
    Thanks for this – so useful for getting a top-line view of important KW performance. I would like to adjust the script so that I can track the average QS of each campaign in an account in a Google spreadsheet as that way it will be a handy way to check for issues.
    I cannot work out how to do this as i am not used to scripts – how should i adjust the script to achieve this?

  • Martin Roettgerding

    Hi Joel,
    You’d need a new script for that – this script here is build around looking up keywords. What you need is something to go through all keywords and add up quality scores, possibly weighted by impressions or something else. Then this would have to be stored in a spreadsheet.

    It’s not that hard, but it still requires some work. However, depending on the number of keywords in your account this might not be possible in the 30 minutes you have.

    Part of the process (for adgroups) is outlined in a comment above:
    I’d also look at Google’s tutorial “Keyword Performance Report”, which might be helpful for this project:
    The part about saving QS history could be adapted from my script here.


  • Hi Martin
    Thanks for the pointers – You have been very helpful but I am perhaps more of a beginner than you realise; I have never seen an Adwords script before last week and I am not sure what arrays are?
    I will start from the beginning with the tutorials and hopefully will be able to piece something together in a few months.

  • Great post, clear code and good ideas. It’s what I was looking for πŸ™‚

  • leuphis

    Very cool script!

  • Agnes

    Hi Martin! I used the script above and upon running it, it returned the following error message:
    Could you please help? Thank you!

  • Franziska Buchrlde

    Hi there, I almost love this but just can’t seem to find why it tells me

    “ReferenceError: “spreadsheetUrl” is not defined. (line 6)” ? Surely something simple to resolve but do help me out here anyone…

  • Mike

    Same for me “ReferenceError: “spreadsheetUrl” is not defined. (line 6)”

    • Marta Krawczyk

      * spreadsheetUrl – should be “spreadsheet_url” in line 6
      * If you later get “Invalid spreadsheet URL” – line 5 of the script should look like this “var matches = new RegExp(‘/d/([^&#]*)’).exec(spreadsheet_url);”. The regex expression seems to be wrong. Probably the spreadsheet address is in different format now.