Script to Subdivide Product Groups by Item Id

1,805 views
Skip to first unread message

Kevin Weitzner

unread,
Feb 25, 2015, 6:47:20 PM2/25/15
to adwords...@googlegroups.com
I am looking to manage bids at the Item ID level - there are two possibilities that I believe are possible.

1) Keep everything in "All Products" and the script will go through the product group to change bid on any outliers ( pulled out via withCondition)

2) Script to subdivide All Products such that each Item ID is it's own product group - this is what I would prefer to do.

Some of my campaigns have over 10,000 items in it - so the built in function to subdivide is only creating subdivisions for the first 1000.

Is there a way to automate this?  I cannot find out how to iterate a product groups items to create all of them as their own subdivision.

Thanks,

Kevin

Alexander Wang

unread,
Feb 25, 2015, 7:13:52 PM2/25/15
to adwords...@googlegroups.com
Hi Kevin,

I think there are some limits on how large product group hierarchies can get. I believe the UI will limit you to 1000 subdivisions. I think similar restrictions will be enforced when creating the hierarchies from scripts or the web API. I'd need to double-check this though; I can't seem to find any external documentation on this.

That said, the hierarchies are specified per ad group. So while your campaigns may have > 10,000 items in them, perhaps your ad groups can be structured so each affects a smaller number of items? Maybe you can set up 10 or so ad groups and have each ad group contain 1000 subdivisions? I think a set up like this could work for you:
Campaign
> Ad Group 1
> > Root product group
> > > Item 1
> > > Item 2
> > > Item 3
...
> > > Item 1000
> > > Everything else (excluded)
> Ad Group 2
> > Root product group
> > > Item 1001
> > > Item 1002
...

This will ensure each item has exactly one subdivision in your campaign and that the ad groups don't compete with each other so to speak (i.e. they'll bid on different item ids).

We have code snippets on creating product groups here. You can also create new shopping ad groups via scripts. We have pointers to documentation for that here. I don't know where your Item IDs are stored, but if they're in a Google spreadsheet (for example), it'd be fairly straightforward to iterate over the spreadsheet, create new shopping ad groups and subdivide the root product group into item ids.

Once you have the structure defined, you can now manage these product groups' bids by doing something like:
var productGroups = AdWordsApp.productGroups()
   
.withCondition("ProductGroup CONTAINS '" + ITEM_ID + "'")
   
.withCondition("CampaignName = '" + CAMPAIGN_NAME + "'")
   
.get();
if (productGroups.hasNext()) {
 
var productGroup = productGroups.next();
 
// adjust productGroup cpc as needed.
}
This will allow you to fetch all product groups that are for that Item ID regardless of the ad group they are in. If you decide to further subdivide Item IDs, you'd need to tweak this a bit. You can see this forum post for a more detailed explanation on this.

Also, I want to call out a solution we have on our developer site. This solution will create new shopping ad groups and product groups as specified in a spreadsheet. Since the construction is (hopefully) a one-time operation, it might be simplest for you to just copy-paste the solution and set up a spreadsheet to create the ad groups + product groups.

Let me know if any of this is unclear. Feel free to reach out if you have any other issues or concerns.

Cheers,
Alex

Kevin Weitzner

unread,
Feb 26, 2015, 9:44:38 AM2/26/15
to adwords...@googlegroups.com
Hi Alex,

Yeah it appears that my idea will not work given this information -- is it possible for a script to look for individual item IDs withCondition and if the condition is satsified, create a product group out of that particular item?

Example:

Campaign Name:
Lighting - Chandeliers
Adgroup: Generic Name
All Products (12,000) set to default bid


I would like the script to iterate through all active shopping campaigns, looking for any product that fulfills X condition (impressions > X, or Conversions, whatever.. I can implement the logic once I know the basics of this)

If it finds this product, can it subdivide the adgroup - where this single item is now pulled out?  I will attach a screenshot -- so it would have looked through this adgroup where all products are in the "All products" bucket -- found this item code where impressions >5 and create it's own product group for the item id.

I basically want to set everything to the same starting bid and find outliers to test a few things out.
AdWords Screenshot.png

Alexander Wang

unread,
Feb 26, 2015, 2:59:41 PM2/26/15
to adwords...@googlegroups.com
Hi Kevin,

I think I understand and I think what you're trying to do is possible to an extent. It sounds like you'd want 2 scripts. 1 script to create the initial campaign structure and another to manage the campaign once its created (and handle "outliers").

Once you've created a campaign (manually via the UI), you can create 12 ad groups (this can be done via the UI or via a script), each ad group containing 1000 item ids. You need to do this if you want to track how specific item ids are doing (and thus be able to identify outliers). If you just had a single "All products" product group in your campaign and no other product groups, then all clicks, impressions, other stats would be attributed to this single "All products" product group. You wouldn't be able to tell (from the UI or from a script) how Item ID 1234 is doing. So you'd want to set up your product group hierarchies like I outlined in my initial response.

Once this is all done, you can write a second script that identifies outliers, removes them from one of the original 12 ad groups, creates a new ad group specifically for this item, and creates a single product group (this item id) with everything else excluded. I think this would give you the behavior you are looking for.

This 2nd script might look something like:
function main() {
 
// All of the product groups are defined in a single campaign, so let's focus on it.
 
var campaign = AdWordsApp.shoppingCampaigns().withCondition("Name = '" + CAMPAIGN_NAME + "'");
 
// Find all active product groups in the campaign that match the specified conditions.
 
// (We want to ignore any product groups that are already set aside in their own ad groups.
 
var outliers = campaign.productGroups()
     
.withCondition("Impressions > 500")
     
.withCondition("AdGroupName DOES_NOT_CONTAIN 'Outlier'")
     
.forDateRange("THIS_MONTH")
     
.get();
 
while (outliers.hasNext()) {
   
// We found an "outlier" product group. Let's split it off from the rest of the product groups and move it into its own ad group.
   
var outlier = outliers.next();
   
var itemId = outlier.asItemId();
   
// First we create a fresh ad group for this product.
   
// The bids are less important than the name. The name contains 'Outlier' and ensures that subsequent iterations of this script
   
// do not move the product group again (it's already in its own ad group, no need to do it again).
   
var newAdGroup = campaign.newAdGroupBuilder()
       
.withBiddingStrategy("MANUAL_CPC")
       
.withCpc(0.5)
       
.withName("Outlier - " + itemId.getValue())
       
.build()
       
.getResult();
   
// Now we need to create a root product group (aka "All products").
    newAdGroup
.createRootProductGroup();
   
var root = newAdGroup.rootProductGroup();
   
// Now we can create the product group for the outlier item id.
    root
.newChild().itemIdBuilder()
       
.withBid(0.6)
       
.withValue(itemId.getValue())
       
.build();
   
// We want to exclude every other product from serving in this ad group
   
var children = root.children().get();
   
while (children.hasNext()) {
     
var child = children.next();
     
if (child.isOtherCase()) {
        child
.exclude();
     
}
   
}
   
// Now we can remove the product group from the rest of the ad groups.
    itemId
.remove();
 
}
}

Basically the script would convert this hierarchy:
Campaign
> Ad Group 1
> > Root product group
> > > Item 1
> > > Item 2
> > > Item 3
...
> > > Item 1000
> > > Everything else (excluded)
> Ad Group 2
> > Root product group
> > > Item 1001
> > > Item 1002
...

Into this hierarchy:
Campaign
> Ad Group 1
> > Root product group
> > > Item 1
> > > Item 2
> > > Item 4
...
> > > Item 1000
> > > Everything else (excluded)
> Ad Group 2
> > Root product group
> > > Item 1001
> > > Item 1002
...
> > > Item 2000
> > > Everything else (excluded)
...
> Outlier - Item 3
> > All Products
> > > Item 3
> > > Everything else (excluded)

You can see if "Item 3" was an "outlier", it was extracted from the rest of the products so that it could be managed separately in its own ad group ("Outlier - Item 3").

Cheers,
Alex

Kevin Weitzner

unread,
Feb 27, 2015, 10:16:26 AM2/27/15
to adwords...@googlegroups.com
Hi Alex,

Thank you so very much for going through this in such depth.  I will try to figure the rest out to implement this.

Regards,

Kevin

Troy Hartenstine

unread,
Sep 23, 2019, 3:42:35 AM9/23/19
to Google Ads Scripts Forum
The above code is not valid

Error: Cannot find function productGroups in object [ShoppingCampaignSelector]

Google Ads Scripts Forum Advisor

unread,
Sep 23, 2019, 5:57:41 AM9/23/19
to adwords...@googlegroups.com

Hi Troy,

It seems that the script which is provided by Alex is missing an iteration of shopping campaigns. With this, you may try to modify the line 6 - 10 in the script like below:

 var campaignIterator = campaign.get();  
 if(campaignIterator.hasNext()){
  var shoppingCampaigns = campaignIterator.next();
 }else{
  return;
 }

 var outliers = shoppingCampaigns.productGroups()


  .withCondition("Impressions > 500")
  .withCondition("AdGroupName DOES_NOT_CONTAIN 'Outlier'")
  .forDateRange("THIS_MONTH")
  .get();



Also, since this is an old thread, feel free to open a new thread so we can track this on our end better.

Regards,
Hiroyuki
Google Ads Scripts Team



ref:_00D1U1174p._5001UHHc7v:ref
Reply all
Reply to author
Forward
0 new messages