Google Ads Library

Flowboost Labelizer Google Ads script

Well, as many of our Library members, I used the solution of Product Hero before (shoutout to their idea). Only I found it not the best segmentation and it was very prone to errors. Those errors are mainly because of the coming of pMax and high fluctuations due to display and YouTube traffic.

Since august 2022 I am using my first version of the Labelizer. It’s a Google Ads scripts that gets all the performance data from Google Shopping and segments based on good and bad performance.

The good and bad performance can be set in the config part of the script using nothing more than the target ROAS and the actual conversion rate. This conversion rate must be checked every week and updated accordingly (free version).

After a while I created a stronger version which has become quite complex. One in every 3 users need a lot over support, that’s why it’s a premium one now. BUT you don’t need the premium one when you know what you’re doing, have ROAS targets between 4-6 and do not have capitalized item IDs.

So why I created the Flowboost Labelizer?

Well, as many of our Library members, I used the solution of Product Hero before (shoutout to their idea). Only I found it not the best segmentation and it was very prone to errors. Those errors are mainly because of the coming of pMax and high fluctuations due to display and YouTube traffic.

Let’s dive in the settings and differences between the Product Hero- and the Flowboost Labelizer.

Well first of all it’s how we name the products.

Product Hero has: Heroes, Sidekicks, Villains and Zombies. Where my segmentation has: over-index, index, near-index, under-index and no-index.

Later in this blogpost I will explain when a product will be matched as one of them.

The PH solution only uses clicks and tROAS where the Flowboost Labelizer uses:

  • Actual and live conversion rates
  • The possibility to add a ‘Superhero’ bucket
  • A more realistic way of grouping the products

Moreover, in the premium version there is an advanced dashboard where you can see the change in products over time.

Let’s dive deeper into the differences. And please, feel free to contact me using the form (or e-mail me) if you have any questions about this script.

The actual conversion rates

Well sometimes you’ll get a ton of clicks due to display traffic. In the Product Hero solution most of your products turn into Heroes or Villains instead of possible Sidekicks when the conversion rates drop.

This happened to me quite some time and shook up the entire algorithm. I was bidding top dollar on products that suddenly got pushed on display with only one conversion. For over a week (until the regrouping took place), I spend too much on a product who had one lucky conversion.

In my opinion only one conversion does not say anything. That’s why the no-index group can contain an occasional conversion when there are not many item clicks and the ROAS in under the index.

Also, in bid management or anything else than product analysis I never take one conversion into account. I believe it can be a lucky shot.

The Superhero bucket

When I use standard shopping and I have a ton of products I don’t want to go to pMax with all of them. Because it will likely not to boost the profit.

No, I only want to go with my best performers.

Sometimes those are the index products. But when that group is too big, I use the over-index products to advertise in my pMax layer.

The Superhero bucket can be set inside the config to ‘true’, if it’s set to ‘false’ the products will be added to the index bucket.

A realistic grouping

Now it’s time to explain how the segmentation works. Here it gets a bit technical because I am using parts of the IF statement to explain it better.

Over-index condition

This condition checks whether a product is performing significantly above the set target and got enough clicks according to the click multiplier you can set yourself.

Basically ‘3x’ means on average 3 conversions in the timespan.

  • config.addToOverIndex: This is a setting which, if true, allows products to be classified as ‘over-index’.
  • productData.clicks > ((100 * config.clickMultiplier) / averageConversionRate): This part checks if the product has enough clicks. It uses a special calculation that takes into account the average conversion rate and a multiplier.
  • convValuePerCost >= config.targetRoas + (config.percentageDifferenceTarget / 100 * config.targetRoas): This checks if the return on ad spend (ROAS) is above the target ROAS by a certain percentage. If these conditions are all met, the product is performing much better than the target and is labeled as ‘over-index’.

Index condition

This condition checks whether a product is performing at or slightly above the target and has reached enough clicks for at least one conversion.

  • productData.clicks >= (100 / averageConversionRate): This part checks if the product has enough clicks, this time using the average conversion rate.
  • convValuePerCost >= config.targetRoas: This checks if the ROAS is at least as high as the target. If these conditions are met, the product is performing well and is labeled as ‘index’.

Near-index condition

This condition checks whether a product is performing slightly below the target.

  • convValuePerCost >= (config.targetRoas -(config.percentageDifferenceTarget / 100 * config.targetRoas)): This checks if the ROAS is at least as high as the target, minus a certain percentage. If this condition is met, the product is performing slightly below target but still close, and is labeled as ‘near-index’.

Under-index condition

This condition checks whether a product is performing below the target.

  • productData.conversions > 1 || productData.clicks > (100 / averageConversionRate): This checks if the product has more than one conversion or if it has enough clicks. If one of these conditions is met, but the product’s ROAS is not meeting the conditions of the above labels, the product is not performing well and is labeled as ‘under-index’.

This can be a rather small group if your clicks suddenly increase due to more display traffic.

No-index condition

If a product doesn’t meet any of the conditions for the above labels, it is labeled as ‘no-index’. This usually means it has very low conversions and clicks. But it can have a high number of items.

Here is it important to state that if your conversion rate drops you’ll have a lot of underperforming, but potential ‘no-index’ products in your feed.

💡 – Flowboost tip for the no-index condition

It’s best to create a separate campaign for the no-index products and give it sufficient budget to test with. This is the group of items that need to be activated and thus you don’t want it to be on a tROAS strategy.

How to install the script and how to fetch them with merchant center can be read in the additional resources.

Flowboost Labelizer: step-by-step instructions

// — install script:
// — add as supplemental feed:
// – update spreadsheet from v2 to v3:

If you want to give it a try, you can use our free version. See code underneath. Use the above step-by-step instructions to make it work. But, if you’re familiar with scripts you can 100% figure it out yourself.

// Flowboost Labelizer 1.1 by Floris de Schrijver
// custom requests? [email protected]

// V1.1 - enter your data as variables 

// make copy of the spreadsheet
// add the spreadsheet URL as supplemental feed to Google merchant center (check cel H1 for custom_label_1, label or product type)

// ------- make sure you register at for other free scripts, automation tips, how-to and much more (to come)

// ------- updates will be uploaded in the library first 
// -- v3.1 is live since july 2023 > advanced settings, better segmentation en graphs

// ------- Newsletters to check > 
// -- PPC Edge newsletter > 

// -------

// let it run daily, smooth sailing

// be aware: products with no impressions will not be added, include all other items in the no-index campaign or ad group

// -------

// Enter your data here >

var SPREADSHEET_URL = ""; // make a copy of the spreadsheet, don’t change any tabs in the sheet

var breakevenRoas = 6.5; // set it 10-20% lower than the account target
var AverageCvr = 5; // conversion rate on average in shopping in past daysAgo
var impressionThreshold = 50; // less than 50 means a ‘zombie’ or ‘no-index’
var daysAgo = 90; // days you like to look back at

// Advanced settings only available in premium labelizer script (get it in the library)

   // - conversion lag in days
   // - segment on 1 or multiple campaign(s) only
   // - capitalization bug
   // - 4 graphs and over time improvement
   // - bucket for proven bad products
   // - creates buckets based relative- instead of absolute difference in ROAS
   // - bucket based on tCPA or tROAS

// Start script don't edit anything below here

function main(){
var products = getFilteredShoppingProducts(daysAgo);
products.sort(function(a,b){return a[0] > b[0];});
products = products.slice(0, 999999);

function getFilteredShoppingProducts(daysAgo){
var today = new Date();
var daysAgo = new Date(today.getFullYear(), today.getMonth(), today.getDate() - daysAgo);
var dateFrom = Utilities.formatDate(daysAgo, AdWordsApp.currentAccount().getTimeZone(), 'yyyyMMdd');
var dateTo = Utilities.formatDate(today, AdWordsApp.currentAccount().getTimeZone(), 'yyyyMMdd');
var query = 
    "SELECT OfferId, Impressions, Clicks, Ctr, Cost, Conversions, ConversionValue " +
    "DURING "+ dateFrom +","+ dateTo;
var products = [];
var count = 0;
var report =;
var rows = report.rows();
while (rows.hasNext()){
var row =;
var offer_id = row['OfferId'];
var impressions = row['Impressions'].toString();
var clicks = row['Clicks'].toString();
var cost = row['Cost'].toString();
var conversions = row['Conversions'].toString();
var conversionValue = row['ConversionValue'].toString();
var convValuePerCost = (conversionValue.replace(",", "") / cost.replace(",", "")).toString();
if (isNaN(convValuePerCost)){
  convValuePerCost = 0;
var isProductType = '';
if (clicks > ( 300 / AverageCvr ) && convValuePerCost >= breakevenRoas + 1){
  isProductType = 'over-index';
} else if (clicks >= ( 100 / AverageCvr ) && convValuePerCost >= breakevenRoas ){
  isProductType = 'index';
} else if (convValuePerCost >= breakevenRoas - 1){
  isProductType = 'near-index';  
} else if (impressions < impressionThreshold){
  isProductType = 'no-index';
} else {
  isProductType = 'under-index';
products.push([offer_id, impressions, clicks, cost, conversions, conversionValue, convValuePerCost, isProductType]);
count+= 1;
return products;

function pushToSpreadsheet(data){
var spreadsheet = SpreadsheetApp.openByUrl(SPREADSHEET_URL);
var sheet = spreadsheet.getSheetByName('Flowbelizer');
var lastRow = sheet.getMaxRows();
var start_row=2;
var endRow=start_row+data.length-1;
var range = sheet.getRange('A'+start_row+':'+'H'+endRow);
if (data.length>0){range.setValues(data);}

Next to this shopping performance functions. There are other functions too. If you’d like to know how account level functions work just check out that article.