Rapport sur les performances des mots clés – Script Google Ads

Un petit groupe de mots clés de votre compte est susceptible de faire la part du lion du travail de votre compte. Désormais, grâce à notre script pratique de performance des mots-clés, vous pouvez identifier exactement les mots-clés qui contribuent le plus à votre succès et ceux qui coûtent le plus cher.

Le principe 80/20

Si vous avez déjà lu un livre sur la gestion du temps ou même sur le développement personnel, vous avez peut-être rencontré quelque chose appelé le principe 80/20.

Parfois appelé le principe de Pareto, le concept tire son nom de l’économiste italien Vilfredo Pareto qui a remarqué ses effets au début du 19e siècle.

L’idée est simple: 80% des sorties proviennent de 20% des entrées.

Par exemple:

  • 20% des cosses produisent 80% des pois
  • 20% des criminels commettent 80% des crimes
  • 20% de vos clients représentent 80% de vos ventes

Il est important de se rappeler qu’il s’agit d’une observation et non d’une loi naturelle. Les chiffres ne seront pas toujours une répartition 80/20, mais ils sont généralement assez proches.

Malgré cela, de nombreux phénomènes naturels suivent un schéma similaire. Tout, de la fréquence des mots (même dans les langues que nous ne pouvons pas encore déchiffrer) au diamètre des cratères sur la lune, correspond à une distribution de Pareto.

Le plus étrange, c’est que personne ne sait pourquoi.

Aujourd’hui, près de 200 ans plus tard, nous constatons à nouveau ces effets dans le monde de la publicité PPC.

Si vous examinez votre compte Google Ads, vous constaterez probablement qu’environ 20% de vos mots clés génèrent environ 80% de vos conversions.

Si vous souhaitez savoir ce que représentent ces 20%, nous avons un script pour vous aider à faire exactement cela et plus encore.

Notre script de performance des mots clés inspiré du principe de Pareto

Tout ce que vous avez à faire est d’installer et d’exécuter ce script Google Ads et il vous donnera un rapport de feuille de calcul avec tous les détails dont vous avez besoin.

Si vous n’avez jamais exécuté de script auparavant, Google propose un guide rapide pour vous montrer comment configurer votre premier.

La feuille de calcul décomposera exactement le pourcentage de votre coût représenté par chaque mot clé, ainsi que le pourcentage de vos conversions.

Si vous utilisez un compte de commerce électronique qui extrait votre valeur de conversion dans Google Ads, il vous donnera également la répartition des mots clés pour vos revenus.

/*******************************************************************************
*
* [KPR] Keyword Performance Report
* Author: Nathan Ifill (@nathanifill), Impression
*
* This script gives you the distribution of cost, conversions and revenue
* (if you are pulling conversion value into your account) amongst your keywords.
*
* v1.1
*
* Any suggestions? Email nathan.ifill@impression.co.uk
*
* Change History:
* v1.1
* - NI added option to select or exclude campaigns by name
*
*******************************************************************************/
// Want your report emailed to you every month? Enter your email address here.
// e.g. var yourEmailAddress = "james.jameson@impression.co.uk";

var yourEmailAddress = "";

var campaignNameContains = "";
// Use this if you only want to look at some campaigns such as campaigns with
// names containing 'Brand' or 'Remarketing'. Leave it as "" if you don't need
// it.

// e.g. var campaignNameContains = "Brand";

var campaignNameDoesNotContain = "";
// Use this if you want to exclude some campaigns such as campaigns with names
// containing 'Brand' or 'Remarketing'. Leave it as "" if you don't need it.

// e.g. var campaignNameDoesNotContain = "Remarketing";

/*******************************************************************************
//********* ONLY CERTIFIED NERDS SHOULD CHANGE THINGS BELOW THIS LINE **********
//*****************************************************************************/
var fullCleanLog = ""; // initialise fullCleanLog

var whereStatements = "";

if (campaignNameDoesNotContain != "") {
  whereStatements += " AND CampaignName DOES_NOT_CONTAIN_IGNORE_CASE '"
  + campaignNameDoesNotContain + "' ";
}

var query = "SELECT Criteria, AdGroupName, CampaignName, KeywordMatchType,"
+ " Impressions, Clicks, Ctr, AverageCpc, Cost, Conversions, ConversionValue,"
+ " ConversionRate FROM KEYWORDS_PERFORMANCE_REPORT WHERE Cost > 0"
+ " AND CampaignName CONTAINS_IGNORE_CASE '" + campaignNameContains + "'"
+ whereStatements
+ " DURING LAST_MONTH"

var report = AdsApp.report(query);

function main() {
  var rows = report.rows();
  var keywordSubtotal = 0;
  var costSubtotal = 0;
  var convSubtotal = 0;
  var revSubtotal = 0;
  var keywordArray = [];
  var costData = [];
  var convData = [];
  var revData = [];
  var skipRev = false; // flag to say whether to add revenue data or not

  Logger.log("Keyword Performance Report - www.impression.co.uk");
  Logger.log("-------------------------------------------------");
  cleanLog("");

  while (rows.hasNext()) {

    var row = rows.next();

    var criteria = row["Criteria"];
    var adGroupName = row["AdGroupName"];
    var campaignName = row["CampaignName"];
    var keywordMatchType = row["KeywordMatchType"];
    var impressions = +row["Impressions"];
    var clicks = +row["Clicks"];
    var ctr = row["Ctr"];
    var averageCpc = +row["AverageCpc"];
    var cost = +row["Cost"];
    var conversions = +row["Conversions"];
    var conversionValue = +row["ConversionValue"];
    var conversionRate = row["ConversionRate"];

    // Sets conversion value to 0 if NaN
    if (isNaN(conversionValue)) {
      conversionValue = 0;
    }

    // Sets ROAS if cost > 0
    if (cost > 0) {
      var roas = conversionValue / cost;
    } else {
      var roas = 0;
    }

    if (conversions > 0) {
      var costPerConversion = (cost / conversions).toFixed(2);
    } else {
      var costPerConversion = 0;
    }

    keywordArray.push({
      "criteria": criteria,
      "ad group name": adGroupName,
      "campaign name": campaignName,
      "keyword match type": keywordMatchType,
      "impressions": impressions,
      "clicks": clicks,
      "ctr": ctr,
      "average cpc": averageCpc,
      "cost": cost,
      "conversions": conversions,
      "cost per conversion": costPerConversion,
      "conversion value": conversionValue,
      "conversion rate": conversionRate,
      "roas": roas
    });

    keywordSubtotal++;
    costSubtotal += cost;
    convSubtotal += conversions;
    revSubtotal += conversionValue;
  }

  if (revSubtotal == 0) {
    skipRev = true;
  }

  var costArray = keywordArray.sort(function (a,b) {

    if ((b["cost"] - a["cost"]) < 0) {
      return -1;
    } else if ((b["cost"] - a["cost"]) > 0) {
      return 1;
    } else {
      return 0;
    }
  });

  var cumulativeCost = 0;
  var maxCostDiff = 0;
  var keywordPercentage = 0;
  var costPercentage = 0;
  var convPercentage = 0;
  var revPercentage = 0;

  costArray.forEach(function(item, index) {
    cumulativeCost += item.cost;

    item["keyword percentage"] = (100 * ((index + 1) / keywordSubtotal)).toFixed(2);
    item["cost percentage"] = (100 * (item["cost"] / costSubtotal)).toFixed(2);
    item["cum cost percentage"] = (100 * (cumulativeCost / costSubtotal)).toFixed(2);
    item["cost diff"] = (item["cum cost percentage"] - item["keyword percentage"]).toFixed(2);
    // sets maxCostDiff to item["cost diff"] if it's greater

    if (+item["cost diff"] > +maxCostDiff) {
      maxCostDiff = item["cost diff"];
      keywordPercentage = item["keyword percentage"];
      costPercentage = item["cum cost percentage"];
    };

    costData.push([item["criteria"],
    item["ad group name"],
    item["campaign name"],
    item["keyword match type"],
    item["cost percentage"] / 100,
    item["cum cost percentage"] / 100,
    item["impressions"],
    item["clicks"],
    item["ctr"],
    item["average cpc"],
    item["cost"],
    item["conversions"],
    item["cost per conversion"],
    item["conversion value"],
    item["conversion rate"],
    item["roas"]]);
  });

  cleanLog(keywordPercentage + "% of your keywords are spending " + costPercentage + "% of your media spend.");
  var expensiveKeywords = costArray.filter(function(item) { return +item["keyword percentage"] <= +keywordPercentage && +item["cost percentage"] <= +costPercentage});

  var convArray = keywordArray.sort(function (a,b) {
    if ((b["conversions"] - a["conversions"]) < 0) {
      return -1;
    } else if ((b["conversions"] - a["conversions"]) > 0) {
      return 1;
    }  else {
      return 0;
    }
  });

  var cumulativeConv = 0;
  var maxConvDiff = 0;
  keywordPercentage = 0;
  convPercentage = 0;

  convArray.forEach(function(item, index) {
    cumulativeConv += item.conversions;

    item["keyword percentage"] = (100 * ((index + 1) / keywordSubtotal)).toFixed(2);
    item["conv percentage"] = (100 * (item["conversions"] / convSubtotal)).toFixed(2);
    item["cum conv percentage"] = (100 * (cumulativeConv / convSubtotal)).toFixed(2);
    item["conv diff"] = (item["cum conv percentage"] - item["keyword percentage"]).toFixed(2);
    // sets maxCostDiff to item["cost diff"] if it's greater

    if (+item["conv diff"] > +maxConvDiff) {
      maxConvDiff = item["conv diff"];
      keywordPercentage = item["keyword percentage"];
      convPercentage = item["cum conv percentage"];
    };

    convData.push([item["criteria"],
    item["ad group name"],
    item["campaign name"],
    item["keyword match type"],
    item["conv percentage"] / 100,
    item["cum conv percentage"] / 100,
    item["impressions"],
    item["clicks"],
    item["ctr"],
    item["average cpc"],
    item["cost"],
    item["conversions"],
    item["cost per conversion"],
    item["conversion value"],
    item["conversion rate"],
    item["roas"]]);
  });

  cleanLog(keywordPercentage + "% of your keywords are driving " + convPercentage + "% of your conversions.");
  var highConvertingKeywords = convArray.filter(
    function(item) {
      return +item["keyword percentage"] <= +keywordPercentage && +item["conv percentage"] <= +convPercentage
    });

    if (!skipRev) {
      var revArray = keywordArray.sort(function (a,b) {
        if ((b["conversion value"] - a["conversion value"]) < 0) {
          return -1;
        } else if ((b["conversion value"] - a["conversion value"]) > 0) {
          return 1;
        }  else {
          return 0;
        }
      });

      // REVENUE BITS

      var cumulativeRev = 0;
      var maxRevDiff = 0;
      keywordPercentage = 0;
      revPercentage = 0;

      revArray.forEach(function(item, index) {
        cumulativeRev += item["conversion value"];

        item["keyword percentage"] = (100 * ((index + 1) / keywordSubtotal)).toFixed(2);
        item["rev percentage"] = (100 * (item["conversion value"] / revSubtotal)).toFixed(2);
        item["cum rev percentage"] = (100 * (cumulativeRev / revSubtotal)).toFixed(2);
        item["rev diff"] = (item["cum rev percentage"] - item["keyword percentage"]).toFixed(2);
        // sets maxCostDiff to item["cost diff"] if it's greater

        if (+item["rev diff"] > +maxRevDiff) {
          maxRevDiff = item["rev diff"];
          keywordPercentage = item["keyword percentage"];
          revPercentage = item["cum rev percentage"];
        };
        // If rev percentage isn't a num, set it to '--'
        isNaN(item["rev percentage"])? item["rev percentage"] = '--' : false;
        isNaN(item["cum rev percentage"])? item["cum rev percentage"] = '--' : false;

        revData.push([item["criteria"],
        item["ad group name"],
        item["campaign name"],
        item["keyword match type"],
        item["rev percentage"] / 100,
        item["cum rev percentage"] / 100,
        item["impressions"],
        item["clicks"],
        item["ctr"],
        item["average cpc"],
        item["cost"],
        item["conversions"],
        item["cost per conversion"],
        item["conversion value"],
        item["conversion rate"],
        item["roas"]]);
      });

      cleanLog(keywordPercentage + "% of your keywords are driving " + revPercentage + "% of your revenue.");
      var highRevKeywords = revArray.filter(
        function(item) {
          return +item["keyword percentage"] <= +keywordPercentage && +item["rev percentage"] <= +revPercentage
        });
      }
      // SPREADSHEET BITS

      // Headers for sheets
      var costReportHeaders = [ "Keyword", "Ad Group", "Campaign",
      "Match type", "% of Cost", "Cum. % of Cost", "Impr.", "Clicks", "CTR",
      "Avg. CPC", "Cost", "Conv.", "Cost / Conv.", "Conv. value", "Conv. rate",
      "Conv. value / cost"];

      var convReportHeaders = [ "Keyword", "Ad Group", "Campaign",
      "Match type", "% of Conversions", "Cum. % of Conversions", "Impr.", "Clicks",
      "CTR",   "Avg. CPC", "Cost", "Conv.", "Cost / Conv.", "Conv. value",
      "Conv. rate", "Conv. value / cost"];

      if (!skipRev) {
        var revReportHeaders = [ "Keyword", "Ad Group", "Campaign",
        "Match type", "% of Revenue", "Cum. % of Revenue", "Impr.", "Clicks", "CTR",
        "Avg. CPC", "Cost", "Conv.", "Cost / Conv.", "Conv. value", "Conv. rate",
        "Conv. value / cost"];
      }

      var title = "Keyword Performance Report: Cost, Conversions & Revenue";

      if (skipRev) {
        title = "Keyword Performance Report: Cost & Conversions";
      }

      var timeZone = AdsApp.currentAccount().getTimeZone();
      var today = new Date(); // initialises a date object to today
      var d = new Date(today.setMonth(today.getMonth()-1));
      // sets the date to a month ago
      var lastMonth = Utilities.formatDate(d, timeZone, 'MMMM'); // December
      var shortMonth = Utilities.formatDate(d, timeZone, 'MMM'); // Dec
      var theYear = parseFloat(Utilities.formatDate(d, timeZone, 'yyyy'));

      title += " - " + lastMonth + " " + theYear + " (www.impression.co.uk)";
      // Appends the date to the title name

      title = AdsApp.currentAccount().getName() + " " + title;

      // Make the spreadsheet
      var ss = SpreadsheetApp.create(title);

      // Give URL for spreadsheet in logs
      cleanLog("");
      cleanLog("The full report is available here: " + ss.getUrl());
      Logger.log("");

      if (yourEmailAddress != '') {
        try {
          var subject = "[KPR] " + AdsApp.currentAccount().getName()
          + " Keyword Performance Report "
          + " - " + shortMonth + " " + theYear;
          var body = title + fullCleanLog;
          MailApp.sendEmail(yourEmailAddress, subject, body);
          Logger.log("Email sent to " + yourEmailAddress + ".");
        } catch (e) {

          Logger.log("Unable to send keyword report email. Please check the email "
          + "address provided is valid.");
        }
      }

      // Name the sheets
      var costSheet = ss.getSheets()[0].setName("Cost Distribution");
      var convSheet = ss.insertSheet("Conversions Distribution");

      if (!skipRev) {var revSheet = ss.insertSheet("Revenue Distribution");}

      // Cost sheet bits
      costSheet.deleteColumns(2, costSheet.getMaxColumns() - 1);
      costSheet.deleteRows(2, costSheet.getMaxRows() - 1);
      costSheet.getRange(1,1,1,costReportHeaders.length).setValues([costReportHeaders]);

      // Conv sheet bits
      convSheet.deleteColumns(2, convSheet.getMaxColumns() - 1);
      convSheet.deleteRows(2, convSheet.getMaxRows() - 1);
      convSheet.getRange(1,1,1,convReportHeaders.length).setValues([convReportHeaders]);

      if (!skipRev) {
        // Rev sheet bits
        revSheet.deleteColumns(2, revSheet.getMaxColumns() - 1);
        revSheet.deleteRows(2, revSheet.getMaxRows() - 1);
        revSheet.getRange(1,1,1,revReportHeaders.length).setValues([revReportHeaders]);
      }

      // Freeze keyword column:
      costSheet.setFrozenColumns(1);
      convSheet.setFrozenColumns(1);
      if (!skipRev) {
        revSheet.setFrozenColumns(1);
      }

      // Put the data in
      costSheet.getRange(2,1,costData.length,costData[0].length).setValues(costData);
      convSheet.getRange(2,1,convData.length,convData[0].length).setValues(convData);

      if (!skipRev) {
        revSheet.getRange(2,1,revData.length,revData[0].length).setValues(revData);
      }

      // Set formats for columns
      var costRangeList = costSheet.getRangeList(['J2:P']);
      var costPrcRangeList = costSheet.getRangeList(['E2:F', 'O2:O']);
      costRangeList.setNumberFormat('0.00');
      costPrcRangeList.setNumberFormat('0.00%');

      var convRangeList = convSheet.getRangeList(['J2:P']);
      var convPrcRangeList = convSheet.getRangeList(['E2:F', 'O2:O']);
      convRangeList.setNumberFormat('0.00');
      convPrcRangeList.setNumberFormat('0.00%');

      if (!skipRev) {
        var revRangeList = revSheet.getRangeList(['J2:P']);
        var revPrcRangeList = revSheet.getRangeList(['E2:F', 'O2:O']);
        revRangeList.setNumberFormat('0.00');
        revPrcRangeList.setNumberFormat('0.00%');
      }

      // Set column widths
      costSheet.setColumnWidths(2, costReportHeaders.length - 1, 175);
      convSheet.setColumnWidths(2, convReportHeaders.length - 1, 175);

      if (!skipRev) {
        revSheet.setColumnWidths(2, revReportHeaders.length - 1, 175);
      }

      // Set first column to be auto sized
      costSheet.autoResizeColumn(1);
      convSheet.autoResizeColumn(1);

      if (!skipRev) {
        revSheet.autoResizeColumn(1);
      }

      // Add filter
      costSheet.getDataRange().createFilter();
      convSheet.getDataRange().createFilter();
      if (!skipRev) {
        revSheet.getDataRange().createFilter();
      }
      Logger.log(" ");
      Logger.log("2 multiplied by 10 plus 1. Romeo done.");
      Logger.log("https://youtu.be/g7VhofoV3qs?t=236");
    }

    function cleanLog(input) {
      Logger.log(input);
      fullCleanLog += "n" + input;
    }

Le script est configuré pour vous donner des statistiques pour le mois dernier, quel que soit le mois. Planifiez-le pour qu’il s’exécute le premier du mois et il créera automatiquement un nouveau rapport pour vous afin que vous puissiez voir vos performances au fil du temps.

En fonction de la taille de votre compte, il devrait durer jusqu’à une minute avant de vous fournir vos données.

Si vous ajoutez votre adresse e-mail au script, il vous enverra un lien vers la feuille de calcul une fois le script exécuté.

Vous pouvez également choisir de n’inclure que les campagnes avec un certain mot dans leur nom (par exemple, «Marque» ou «Royaume-Uni») dans le rapport pour afficher des statistiques spécifiques pour un sous-ensemble de vos campagnes.

Sinon, s’il y a certaines campagnes que vous souhaitez exclure d’après les chiffres, vous pouvez le faire aussi.

À quoi s’attendre

Vous pourriez être surpris de découvrir à quel point votre compte est biaisé!

Les journaux de script vous donneront certaines des statistiques en tant que faits saillants, mais vous les recevrez également si vous avez entré votre adresse e-mail.

Un exemple de ce à quoi les journaux pourraient ressembler sur votre compte.

Le rapport lui-même est assez simple avec des colonnes affichant une variété de mesures de performance pour chaque mot clé, y compris les impressions, les clics, le CTR, la moyenne. CPC, coût, coût par conversion et taux de conversion.

En plus de cela, vous obtiendrez également une ventilation du pourcentage de coût, de conversions et de revenus dont chaque mot clé est responsable, ainsi que du pourcentage cumulé.

Le script analyse uniquement les mots-clés qui ont dépensé de l’argent le mois dernier, donc si vous vous attendiez à plus, cela peut être la raison de l’écart.

Chaque feuille est triée par coût, conversions et revenus par ordre décroissant, en fonction de la métrique analysée. Cela signifie que les mots clés qui contribuent le plus à votre compte ou qui dépensent le plus de votre budget seront toujours en tête.

Essayez-le sur votre compte dès aujourd’hui pour voir les performances de vos mots clés.

Augmentez l’efficacité de vos dépenses

Ainsi, une petite poignée de mots clés est responsable d’une grande partie de vos conversions et de vos frais publicitaires.

Maintenant, que faites-vous de ces informations? C’est là que nous intervenons.

Même si les choses se passent bien dans votre compte pour le moment, il y a toujours place à l’amélioration. Avec quelques ajustements rapides à l’arrière de ces données, vous pouvez augmenter l’efficacité des dépenses dans votre compte et obtenir encore de meilleurs résultats avec votre budget actuel.

Bien que des performances disproportionnées soient à prévoir, cela peut également indiquer qu’il est temps de restructurer votre compte, d’ajuster vos stratégies d’enchères et d’appliquer le budget en priorité.

Si vous souhaitez en savoir plus sur la façon dont vous pouvez tirer le meilleur parti de votre compte, contactez-nous dès aujourd’hui et nous pourrons vous guider.