In order to have as much control over the keywords per product in our shopping campaigns, we always place all products in an ad group. That way, you can exclude words per product on which other products can still be displayed.

But as soon as you have more than 10 products in your shop, you do not want to do this manually. You have to create an ad group per product, add a product, add a bid and exclude 'Other products'. That is why we have created a script that automatically updates this based on your Merchant Center. The script goes through all products and checks whether these are already in the shopping campaigns. If the products are new, they will be added to the campaigns.

Set up campaigns manually

From Google Ads Scripts we can not create new Shopping campaigns. That is why you have to create and set it yourself as you wish. When you next run this script for the first time, you will have to do this several times to ensure that all products are in it. On average, the script gets 500 products in half an hour, so turn the script the first time often enough to add all products. You can also split your campaigns by a field in Merchant Center, like brand or productType. If you're doing this, make sure the brand or productType is in the campaign name, so the script can select the right campaign to add the product.

Settings

To run this script properly, you need to set the following things correctly:

  • LOG: Specify whether the script should report the intermediate steps by adjusting the value to 'true'.
  • MERCHANT_ID: The ID of your Merchant Center account. The user running the script in Google Ads, needs to be added as a user in Merchant Center. Also, enable the advanced API 'Shopping Content' in the script.
  • DEFAULT_BID: The Max. CPC that is filled in for each product.
  • SPLIT_FIELD: Do you want to split your campaigns by a Merchant Center field? Set that field here.

Scheduling: If you have more than 500 products, you set them the first time for each hour. When all products are in the campaigns, change this to daily. Make sure you choose a time after updating your product feed in Merchant Center.

Script
// Copyright 2019. Increase BV. All Rights Reserved.
//
// Created By: Tibbe van Asten
// for Increase B.V.
//
// Created 15-04-2019
// Last update: 18-09-2019
//
// ABOUT THE SCRIPT
// All products in the productfeed are checked for existance in the
// Google Shopping campaigns. If new, they're added to the right campaign
//
////////////////////////////////////////////////////////////////////

var config = {

  LOG : true,

  // Connect Merchant Center. Add user to MC that runs this script.
  // Also enable Advanced API 'Shopping content'.
  MERCHANT_ID : "123456789",

  // Set a default bid. This will be used for adgroups and products
  DEFAULT_BID : 0.5,

  // Campaigns can't be created by this script. You'll have to do this by hand.
  // Do you want to split your campaigns by a field from Merchant Center?
  // Possible values: brand, productType
  // For example: when you split by brand, make sure the brand is in the campaign name
  SPLIT_FIELD : "brand"
}

////////////////////////////////////////////////////////////////////

function main(){

  // First off, we collect all productdata from Merchant Center.
  // We also check all active adgroups
  var products = connectMerchant();
  var activeProducts = activeAdGroups();

  var addProducts = {};

  // All products of the feed will be checked
  for (var i = 0; i < products.length; i++) {

    // If the products-Id is already the name of an existing adgroup, we don't add it.
    // If the product is out of stock, we don't add it.

    if(activeProducts.indexOf(products[i]["offerId"]) < 0 && products[i]["availability"] == "in stock"){
      var productId = products[i]["offerId"];

      // If you want to split your campaigns, we'll add this to info
      if(config.SPLIT_FIELD != ""){
        var split = products[i][config.SPLIT_FIELD];
      }

      if (addProducts[productId] == null) {
        addProducts[productId] = [];
      }
      addProducts[productId].push([productId, split]);
    }

  } // for statement

    if(config.LOG === true){
      Logger.log("Found " + Object.keys(addProducts).length + " to be added");
      Logger.log(" ");
    }

  // Adding all products not already in the campaigns
  for (var key in addProducts) {

    var campaignSelector = AdsApp
      .shoppingCampaigns()
      .withCondition("Status = ENABLED");

    if(config.SPLIT_FIELD != ""){
      campaignSelector = campaignSelector.withCondition("Name CONTAINS '" + addProducts[key][0][1] + "'");
    }

    var campaignIterator = campaignSelector.get();

    while(campaignIterator.hasNext()){
      var campaign = campaignIterator.next();

      // Adding adgroup to Shopping campaign
      var shoppingAdGroup = campaign
        .newAdGroupBuilder()
        .withName(addProducts[key][0][0])
        .withCpc(config.DEFAULT_BID)
        .withStatus("ENABLED")
        .build()
        .getResult();

        if(config.LOG === true){
          Logger.log(shoppingAdGroup.getName() + " added to " + campaign.getName());
        }

      // Adding an ad to the new adgroup
      var ad = shoppingAdGroup
        .newAdBuilder()
        .build()
        .getResult();

      // Creating the root productgroup 'All Products' and select it
      shoppingAdGroup.createRootProductGroup();
      var root = shoppingAdGroup.rootProductGroup();

      // Creating a productgroup for this product
      root.newChild().itemIdBuilder()
        .withBid(config.DEFAULT_BID)
        .withValue(addProducts[key][0][0])
        .build();

      // Making sure all other products in this adgroup are excluded
      var children = root.children().get();
      while (children.hasNext()) {
        var child = children.next();
        if (child.isOtherCase()) {
          child.exclude();
        }
      } // childrenIterator

    } // campaignIterator

  } // addProductsIterator

} // function main()

////////////////////////////////////////////////////////////////////

function activeAdGroups(){

  var activeProducts = [];

  // We will check if an adGroup with this ID as name already exists
  var adGroupIterator = AdsApp
    .shoppingAdGroups()
    .withCondition("CampaignStatus = ENABLED")
    .withCondition("Status != REMOVED")
    .get();

  while(adGroupIterator.hasNext()){
    var adGroup = adGroupIterator.next();
    activeProducts.push(adGroup.getName());
  } // adGroupIterator

  return activeProducts;

} // activeAdGroups

////////////////////////////////////////////////////////////////////

function connectMerchant(){

    if(config.MERCHANT == "123456789"){
      throw Error("Change the Merchant ID in the settings");
    }

  var pageToken;
  var pageNum = 1;
  var maxResults = 250;
  var products = [];

  do {
    var productList = ShoppingContent.Products.list(config.MERCHANT_ID, {
      pageToken: pageToken,
      maxResults: maxResults
    });

    if (productList.resources) {
      for (var i = 0; i < productList.resources.length; i++) {
        products.push(productList.resources[i]);
      }
    }

    pageToken = productList.nextPageToken;
    pageNum++;
  } while (pageToken);

  return products;

} // function connectMerchant

Sharing knowledge

Adsscripts.com is all about sharing knowledge. In the current market, PPC specialists like to keep their knowledge and experience to themselves. We're convinced that sharing knowledge can ensure that everyone gets better at their work. We want to change this by sharing our knowledge about scripts with everyone.

Do you also want to contribute? We are open to new ideas and feedback on everything you find on Adsscripts.com.

Contact us

Nils Rooijmans
Google Ads Scripter
Water Cooler Topics
Nils Rooijmans, Google Ads Scripter
Bas Baudoin
Teamleader PPC
Happy Leads
Bas Baudoin, Teamleader PPC
Martijn Kraan
Freelance PPC Specialist
Brightstep
Martijn Kraan, Freelance PPC Specialist
Tibbe van Asten
PPC Specialist
Founder Adsscripts
Tibbe van Asten, Senior PPC Automation Specialist