Operation Quality Score 10: A Silkstone Story

It all started with an innocent question in my Facebook group ‘the best way to track if quality score starts to dip’:

how-to track quality score changes
thanks to Kris for starting the conversation

There are marketers who believe in Quality Score (QS) and that they should monitor it, and there are others who simply ignore it.

Is Quality Score really that important?

Quality score is an indicator of account health and is associated with higher CTR (Click-through Rate). It also positively correlates with cheaper CPC (Cost Per Click).

My personal take? Well, I did do a video on ‘hacking’ quality score a few years back.

Things have changed a little since then. We learn, improve and evolve our approach.

That said, I’ve always used QS as a diagnostic tool, not a performance metric. In other words, if something isn’t going my way I’ll look for and review any quality score issues.

So then, how do we reliably track QS changes when a keyword drops from a high of 10?

There’s no way to do it in the Google Ads interface, unless you login and physically check each keyword – every day!

And so, the magic happened:

quality score script

A new Google Ads script was born.

What does this quality score script do exactly?

This script will label all your 10/10 QS keywords. If they drop below 10 it’ll email you and mark them with a label to get your attention.

Simple instructions to use it:

  • Copy the code below
  • Paste it in to your Google Ads scripts
  • Change the email address
  • Schedule it to run daily

Not sure how to setup a script? Here’s a quick tutorial.

// -- Operation QS10 0 Keyword Propper -- When 10s fall from grace, we should know about it.
// This script will alert us to any keywords that fall from their 10/10 position by labelling them accordingly
// and emailing off any droppers to the team for action.
// Author: Doug - withSeismic.com
// 16.11.2019

// Version 1.01 - Fixed a monumental bug, and a couple of crappy errors.

var emailList = [ 'you@emailadress.com' ]

var labels = [
name: 'qualityScoreTen',
labelString: 'QS10 Champion',
description: 'A Keyword with a Quality Score of 10',
colour: '#0055FF'
name: 'warningLabel',
labelString: 'QS < 10',
description: 'A keyword that needs attention - QS < 10',
colour: '#FF0000' // RED MEANS DANGER, RIGHT?

function main() {
// Laying some groundwork - Firstly, let's check that the right labels exist to do the job.
// If they don't - say, if you're running this script for the first time - then let's create them using details from the labels array above

for (var i = 0; i < labels.length; i++) {
var label = labels[i]
if (!AdsApp.labels().withCondition('Name = "' + label.labelString + '"').get().hasNext()) {
AdsApp.createLabel(label.labelString, label.description, label.colour)
Logger.log('Created %s Label: %s', label.name, label.labelString)

var flagBucket = [] // Where we'll dump naughty keywords.

// Step One. Checking existing 10's for changes.. If this is your first run, it won't do much unless you've already labelled your keywords. No need to, as we'll do that anyway in step two.
// ..Step Two. Tagging our currently live keywords up with the QS10 Label so this script can process them next time it's run.

var adGroupList = AdsApp.adGroups()
.withCondition('CampaignStatus = "ENABLED"')
.withCondition('Status = "ENABLED"')
Logger.log('Processing %s adGroups', adGroupList.totalNumEntities())

while (adGroupList.hasNext()) {
var adGroup = adGroupList.next()

var keywordList = adGroup
.withCondition('Status = "ENABLED"')
.withCondition('LabelNames CONTAINS_ANY ["' + labels[0].labelString + '"]')

while (keywordList.hasNext()) {
var keyword = keywordList.next()
if (keyword.getQualityScore() < 10) { Logger.log('Keyword Dropped!') flagBucket.push(keyword) keyword.removeLabel(labels[0].labelString) keyword.applyLabel(labels[1].labelString) } } var keywordList = adGroup.keywords().withCondition('QualityScore = 10').get() while (keywordList.hasNext()) { var keyword = keywordList.next() if (!keyword.labels().withCondition('Name CONTAINS "' + labels[0].labelString + '"').get().hasNext()) { keyword.applyLabel(labels[0].labelString) } } } // Here, we'll make the emails fly - Did you know there's a daily quota of how many emails you can send per day? // If you're super script heavy, you might just cap it! var droppedKeywordsNo = flagBucket.length if (droppedKeywordsNo > 0) {
var responsibleEmailerQuota = MailApp.getRemainingDailyQuota()
if (responsibleEmailerQuota < 1) {
Logger.log('Your daily email quota is out! No emails sent.')

var htmlString = []
for (var i = 0; i < flagBucket.length; i++) {
var keyword = flagBucket[i]

' +
keyword.getCampaign().getName() +
'' +
keyword.getAdGroup().getName() +
'' +
keyword.getText() +
'' +
keyword.getQualityScore() +


Logger.log('%s have dropped below QS10 - See email for details.', droppedKeywordsNo)
to: emailList.toString(),
subject: 'QS10 Checker: ' + droppedKeywordsNo + ' Keywords have dropped from QS10!',

' +
htmlString.toString().replace(/,/g, '') +

<table style="width: 100%; text-align: left;">






<th>New Quality Score</th>




} else {
Logger.log('No keyword drops! Congrats')

Logger.log('Script Completed')

If the above isn’t displaying properly for you, here’s a link to the script for backup.

A big shout-out to Dougie for creating this script for the community. If you’d like to hire Dougie to build you some scripts, you can find him on LinkedIn.