How to share sitelinks between campaigns via the Batch Service?

160 views
Skip to first unread message

Alexander Cavalli

unread,
Feb 27, 2017, 3:56:39 PM2/27/17
to AdWords API Forum
Hi there,

This is somewhat related to an earlier discussion I had with Nadine Sundquist here:

I would like to generate sitelinks that are shared between multiple campaigns using the Batch Service, preferably with a single batch job. Assuming this is possible, what sort of operations will I need to use to achieve this? In my particular case, all of the campaigns will already have been created, and I have their ids, but the sitelinks may or may not exist yet. 

In the case where the sitelinks are new, I have tried something like this (I'm using Ruby, and in this case I made the campaigns earlier in the Batch Job and used pseudo ids, but that probably shouldn't matter):

extension_setting = {
  xsi_type: 'ExtensionSetting',
  extensions: [
    feed_item_id: -5,
    xsi_type: 'SitelinkFeedItem',
    sitelink_text: 'Store Hours',
    sitelink_final_urls: {
      urls: ['http://www.example.com/']
    }
  ]
}

campaign_extension1 = {
  campaign_id: -2,
  extension_type: 'SITELINK',
  extension_setting: extension_setting
}
campaign_extension1_op = {
  xsi_type: 'CampaignExtensionSettingOperation',
  operator: 'ADD',
  operand: campaign_extension1
}
campaign_extension2 = {
  campaign_id: -4,
  extension_type: 'SITELINK',
  extension_setting: extension_setting
}
campaign_extension2_op = {
  xsi_type: 'CampaignExtensionSettingOperation',
  operator: 'ADD',
  operand: campaign_extension2
}

# ops here contains operations to generate campaigns with ids -2 and -4 as well
ops = ops << campaign_extension1_op << campaign_extension2_op

But the response contained a DUPLICATE_ELEMENT error on the campaign_extension_2_op operation.

  {
    "result": {
      "campaign_extension_setting": {
        "extension_type": "SITELINK",
        "extension_setting": {
          "extensions": {
            "feed_id": "20505208",
            "feed_item_id": "11262219902",
            "status": "ENABLED",
            "feed_type": "SITELINK",
            "extension_feed_item_type": "SitelinkFeedItem",
            "sitelink_text": "Store Hours",
            "sitelink_final_urls": {
              "urls": "http://www.example.com/"
            }
          },
          "platform_restrictions": "NONE"
        }
      }
    },
    "index": "12"
  },
  {
    "error_list": {
      "errors": {
        "field_path": "operations[13].operand.extensionSetting.extensions[0].feedItemId",
        "trigger": "TempFeedItemId{id=5}",
        "error_string": "DistinctError.DUPLICATE_ELEMENT",
        "api_error_type": "DistinctError",
        "reason": "DUPLICATE_ELEMENT"
      }
    },
    "index": "13"
  }

There are many other operation permutations that conceivably could work (including some combination of FeedItemOperations and CampaignExtensionSettingOperations, for instance), so I thought I would reach out before I attempted to test all of them. For both the case of new sitelinks and existing sitelinks (where I have a feed item id), what operations should I use to attach these sitelinks to multiple campaigns within a Batch Job?

Thanks,
- Alex



Vishal Vinayak (Adwords API Team)

unread,
Feb 27, 2017, 4:59:20 PM2/27/17
to AdWords API Forum
Hi Alex,

DUPLICATE_ELEMENT error might be returned in a response if the request contains two identical elements. From the error message, it looks like the temporary feed item ID (-5) might be causing the issue. Also, I can see that the response has multiple operations (element with the index 13 containing the error). Could you please check if you are uploading other operations in the batch job as well, specifically any operation that is re-using temporary feed_item_id : -5? 

Regards,
Vishal, AdWords API Team

Alexander Cavalli

unread,
Feb 27, 2017, 5:04:10 PM2/27/17
to AdWords API Forum
Hi Vishal,

The -5 is getting used in the "extension_setting" object, which is inserted into both campaign_extensions. I assumed that without that id the API would not recognize that I wanted to use the same extension_setting for both campaigns (and it would make two identical ones, instead). I'll try that though, thanks!

- Alex

Alexander Cavalli

unread,
Feb 27, 2017, 5:21:15 PM2/27/17
to AdWords API Forum
Hi Vishal,

When I remove the -5 from the extension_setting, the API generates two distinct sitelinks, rather than one sitelink that gets linked to two campaigns. I confirmed this by going into the UI and changing the text on one of them and finding it wasn't changed on the other.

Here's the results:
  {
    "result": {
      "campaign_extension_setting": {
        "extension_type": "SITELINK",
        "extension_setting": {
          "extensions": {
            "feed_id": "20505208",
            "feed_item_id": "11266444676",
            "status": "ENABLED",
            "feed_type": "SITELINK",
            "extension_feed_item_type": "SitelinkFeedItem",
            "sitelink_text": "Store Hours",
            "sitelink_final_urls": {
              "urls": "http://www.example.com/"
            }
          },
          "platform_restrictions": "NONE"
        }
      }
    },
    "index": "12"
  },
  {
    "result": {
      "campaign_extension_setting": {
        "extension_type": "SITELINK",
        "extension_setting": {
          "extensions": {
            "feed_id": "20505208",
            "feed_item_id": "11266444679",
            "status": "ENABLED",
            "feed_type": "SITELINK",
            "extension_feed_item_type": "SitelinkFeedItem",
            "sitelink_text": "Store Hours",
            "sitelink_final_urls": {
              "urls": "http://www.example.com/"
            }
          },
          "platform_restrictions": "NONE"
        }
      }
    },
    "index": "13"
  }

Notably, I think, the feed item ids are not the same.

Just to be clear, my original problem was not that I was getting a DUPLICATE_ELEMENT error specifically, but that it wasn't clear how to do this "single sitelink mapped to multiple campaigns" thing, and that the one approach I had tried had that particular error.

Thanks again for the help!
- Alex

Vishal Vinayak (Adwords API Team)

unread,
Feb 28, 2017, 12:43:26 PM2/28/17
to AdWords API Forum
Hi Alex,

Since there are duplicate elements in the upload when trying to add the same extension setting object to multiple campaigns, I would recommend creating the extension setting separately using CampaignExtensionSettingService (rather than a dependent operation), fetching the feed ID and feed item ID from the extension and using these IDs when trying to add the extension setting to multiple campaigns. This would also help you in the case where the sitelink already exists and you do not want to create duplicates. 

Referring to the Batch Jobs documentation, Temporary IDs allow you to reference the result of an ADD operation from the previous operation in the same batch job. However, you need to ensure that the operation that creates a parent object comes before the operations that create its child objects.

In your case, the situation becomes a little different, as explained below:

Expected Scenario:
When you define an extension_setting object with a feed item ID of -5, you are assuming the object to hold this value for both (or all) operations where this extension_setting object would be applied. 

Actual Scenario:
During creation of the first campaign extension, the extension setting is created as a dependent object of the first campaign extension setting operation (and not the other way around). As a result, when the second operation executes, the original extension setting object is now treated as a duplicate. 

Please let me know if you are facing issues when creating extension separately and re-using them in the batch job. 

Alexander Cavalli

unread,
Feb 28, 2017, 1:57:12 PM2/28/17
to AdWords API Forum
Hi Vishal,

Thanks for the information. Let me rephrase just to make sure I'm understanding properly.

You're suggesting that, if I have a new sitelink that I want to tie to multiple campaigns, I should 
1) add that sitelink to Campaign 1 using the CampaignExtensionSettingService (not as part of a BatchService call), 
2) extract the FeedId and FeedItemId from the response of the CampaignExtensionSettingService call, and then 
3) use the now-created feed item in the Batch Job as a CampaignExtensionOperation for each campaign I want to tie it to (other than the first one, which got linked in step 1)? 

Which is to say, there's no way to accomplish this without multiple service calls (i.e. it's impossible to do within a single batch job)? For example, could I do something like this instead, in a single batch job:
1) Create a FeedItemOperation to create the sitelink with a Temporary ID
2) Use the Temporary ID in several subsequent CampaignExtensionSettingOperations to tie it to the campaigns I want to tie it to (I suppose with the SET operator)?

Thanks again for the help.
- Alex

Alexander Cavalli

unread,
Feb 28, 2017, 3:04:15 PM2/28/17
to AdWords API Forum
Thanks to help from Vishal, I got this working by doing nearly what I had originally, but changing the second (and all subsequent) CampaignExtensionSettingOperations to SET, with the previously used Temporary ID. Like this:

campaign_extension1 = {
  campaign_id
: -2,
  extension_type
: 'SITELINK',

  extension_setting
: {
    xsi_type
: 'ExtensionSetting',
    extensions
: [
      feed_item_id
: -5,                 # Temporary ID established here

      xsi_type
: 'SitelinkFeedItem',
      sitelink_text
: 'Store Hours',
      sitelink_final_urls
: {
        urls
: ['http://www.example.com/']
     
}
   
]
 
}
}

campaign_extension1_op
= {
  xsi_type
: 'CampaignExtensionSettingOperation',

 
operator: 'ADD',                     # ADD operator for first one, to make the sitelink

  operand
: campaign_extension1
}


campaign_extension2
= {
  campaign_id
: -4,
  extension_type
: 'SITELINK',

  extension_setting
: {
    xsi_type
: 'ExtensionSetting',
    extensions
: [
      feed_item_id
: -5,                # Temporary ID re-used here
      xsi_type
: 'SitelinkFeedItem'
   
]

 
}
}
campaign_extension2_op
= {
  xsi_type
: 'CampaignExtensionSettingOperation',

 
operator: 'SET',                     # SET operator, instead of ADD
  operand
: campaign_extension2
}
ops
= ops << campaign_extension1_op << campaign_extension2_op

This results in the same feed item (based on feed item id) being used:

  {
    "result": {
      "campaign_extension_setting": {
        "extension_type": "SITELINK",
        "extension_setting": {
          "extensions": {
            "feed_id": "20505208",
            "feed_item_id": "11299267966",
            "status": "ENABLED",
            "feed_type": "SITELINK",
            "extension_feed_item_type": "SitelinkFeedItem",
            "sitelink_text": "Store Hours",
            "sitelink_final_urls": {
              "urls": "http://www.example.com/"
            }
          },
          "platform_restrictions": "NONE"
        }
      }
    },
    "index": "12"
  },
  {
    "result": {
      "campaign_extension_setting": {
        "extension_type": "SITELINK",
        "extension_setting": {
          "extensions": {
            "feed_id": "20505208",
            "feed_item_id": "11299267966",
            "status": "ENABLED",
            "feed_type": "SITELINK",
            "extension_feed_item_type": "SitelinkFeedItem",
            "sitelink_text": "Store Hours",
            "sitelink_final_urls": {
              "urls": "http://www.example.com/"
            }
          },
          "platform_restrictions": "NONE"
        }
      }
    },
    "index": "13"
  }

Confirmed that this was the same sitelink in the UI as well, it checks out.

Thanks again for the help Vishal.
- Alex

Vishal Vinayak (Adwords API Team)

unread,
Feb 28, 2017, 5:55:23 PM2/28/17
to AdWords API Forum
Hi Alex,

The second attempt looks a little different from the first one in that the extension setting object is created as a nested object. Did you face a similar issue when using the SET operator in the second operation of the original API call?

Alexander Cavalli

unread,
Feb 28, 2017, 6:04:53 PM2/28/17
to AdWords API Forum
Hi Vishal,

I think it is the same, or nearly so. The extension setting object is a nested object in either case. In the first one I saved that object to a var first and it was identical in both campaign extension settings, and in the second one I inlined it and the SET operation didn't include the details of the object other than the id (text, final url; since it shouldn't need them). I didn't try using SET as the second operation in my original example, but I'm guessing it would work as well. In any case, the object nesting is identical, the differences are the SET operation and the lack of text and final url in the extension setting of the SET operator. Can't say for sure which difference made the difference, but I was figuring it was the operator.

- Alex

Vishal Vinayak (Adwords API Team)

unread,
Mar 1, 2017, 3:35:27 PM3/1/17
to AdWords API Forum
Hi Alex,

As mentioned in the documentation, if you use an ADD operator when an extension already exists, the existing extension is modified (i.e. ADD words like SET in that case). I tried using the operations in your second attempt (with SET), changed the SET to an ADD and I could still see the operations added to a single extension. However, when I tried adding operations using your initial approach (where extension_setting is declared externally in a variable), the second operation did not go through, either with an ADD or a SET operator. Therefore, I believe the issue in your case would be because of the way objects were defined. Could you please confirm if that's the case on your side as well?

Alexander Cavalli

unread,
Mar 1, 2017, 4:53:43 PM3/1/17
to AdWords API Forum
Hi Vishal,

I made two changes between my first and second examples. They were:

1. Usage of two ADD operators (first example) vs ADD then SET (second example)
2. Usage of a fully defined extension, i.e. including sitelink_text and sitelink_final_urls (first example) vs a partially defined extension, with just feed item (temporary) id and xsi type (second example)

I assumed my second example worked because intuitively to me the operator was the significant change here, but it turns out I had it backward, and it was the removal of the "extra fields" on the second operation (even though those fields were identical to the previous operation, and the temporary id was already set up) that made the difference. I ran an examples with each permutation (I've attached the operations and results), and the outcomes were:

* Two ADD operations, only id and type on the second operation -> success
* ADD then SET, only id and type on the second (SET) operation -> success
* Two ADD operations, identical, fully defined extension on the second operation -> failure (duplicate element)
* ADD then SET, fully defined extension on the second operation -> failure (duplicate element)

This was a bit unexpected. A few followup questions:

1. What is the purpose of having an ADD and a SET operator here if they behave identically? Or rather, when should I choose to use one vs the other (as opposed to always using the ADD operator)?
2. In general, does the batch service detect a "new object" by looking for the presence of any fields other than "id" (which is what it seems to be doing in this case)? This feels slightly backward to me, e.g. (this would probably never happen) but if I were to add a campaign in one operation then run a SET on that same campaign temporary id in a later operation, with perhaps a status update (maybe I want to start the campaign paused and unpause it at the very end), it seems like in this case the presence of the status field wouldn't and shouldn't cause the API to think I'm trying to make a new object (and also the fact that it's a SET operation). 

Thanks, again, for the help!
- Alex
shared_sitelinks_batch_service.rb

Vishal Vinayak (Adwords API Team)

unread,
Mar 2, 2017, 10:05:54 AM3/2/17
to AdWords API Forum
Hi Alex,

Thanks for confirming. To your question, you can use either ADD or SET to add / update extensions. I can check with my team and let you know more on the purpose of their existence as two operators (and not one) and why the batch job behaves the way it does. Myself or somebody from my team will get back to you soon.  

Nadine Sundquist (AdWords API Team)

unread,
Mar 2, 2017, 6:29:54 PM3/2/17
to AdWords API Forum
Hi Alex,

I remember when the idea of having SET and ADD do the same operation came into being, so I can shed some light on it. From a coding perspective, people found it cumbersome to have to have to do a get() to see if something already existed before deciding if they needed to do a SET versus an ADD. That was an extra operation, and when you're doing a lot of these, it adds up. It was easier for people just to do a SET no matter what. The API could then figure out for them if it needed to be an ADD or not. There were fewer calls to the API, and it made the performance of people's applications that run against the API better as a result.

Batch processing does allow for you to create an object earlier in the job, and then mutate it in some way later on in the job. It does rely on that ID. Now, we do recommend against doing that in our Best Practices. The reason we recommend against doing that is because of performance and also that we don't guarantee the order of all the operations. That means some of those mutates() of the newly created object may happen in a different order than you would expect.

I hope that clears it up for you!

Best,
Nadine, AdWords API Team

Alexander Cavalli

unread,
Mar 3, 2017, 10:52:28 AM3/3/17
to AdWords API Forum
Hi Nadine,

Thanks for the info. A couple more clarifications, if you don't mind! :)

1. Reading that doc you sent on SET and ADD being somewhat interchangeable, there's a note about "If the SET operator is used in a case where an ad extension does not exist, a new ad extension is added." What fields is the API using to determine whether or not the extension already exists? 
2. Given that the API doesn't guarantee the order of all the operations, does that mean I will need to plan on creating the sitelinks separately first, and then adding any additional mappings as part of the batch job? Or is it safe for that particular kind of operation for me to create a sitelink and then create additional mappings for that same sitelink using its temporary id later (within a batch job)? 

- Alex

Nadine Sundquist (AdWords API Team)

unread,
Mar 3, 2017, 12:04:26 PM3/3/17
to AdWords API Forum
Hi Alex,

I absolutely don't mind! I'm glad I can help. Yay!
1. When looking at the ExtensionFeedItem, the feedId and the feedItemId can be keyed off of to determine if the item is unique at the campaign, ad group, or customer level.
2. The BatchJobService does have logic in place that if a temporary ID is relied upon by another item in the job, then that kind of order has to be maintained. You are safe with your use case.

Regards,
Nadine, AdWords API Team

Alexander Cavalli

unread,
Mar 3, 2017, 12:42:12 PM3/3/17
to AdWords API Forum
Hi Nadine,

Thanks, I think I've got all the info I need in order to code this out properly. 

I'm not sure I'm totally grokking this whole setup though, so, for my own edification, let me see if I've got this all correct:

1. ADD and SET are interchangeable on the CampaignExtensionSettingOperation when you already have a feed item id and just want to make sure that feed item is mapped to a campaign. The API will detect the mapping already exists, or not, and do the right thing.
2. When you don't have a feed item id, you need to use ADD to generate one (to create the feed item, and incidentally the mapping as well).
3. A CampaignExtensionSettingOperation with only feed item ids in the extension setting list will map those extension settings to the campaign (assuming the ids are valid or are temporary ids created earlier in the batch job).
4. A CampaignExtensionSettingOperation with fully defined feed items (e.g. text and url in the case of sitelinks) will attempt to modify the extension feed item AND set the mapping.
4a. In a Batch Job, you can't submit a fully defined extension feed item (as a child of the CampaignExtensionSettingOperation) with a temporary id in two different campaigns - the second one will attempt to mutate the first one, or redefine it, or something (Duplicate element error). But you can reference the temporary id for the purposes of making a new campaign mapping, and using either SET or ADD (as mentioned in #1). 

Thanks again.
- Alex

Nadine Sundquist (AdWords API Team)

unread,
Mar 3, 2017, 4:31:48 PM3/3/17
to AdWords API Forum
Hi Alex,

Before I confirm this, let me check with the person here who wrote that specific piece of code. I just want to make sure it's exactly correct for you. I'll get back to you as soon as I hear back from him.

Cheers,
Nadine, AdWords API Team

Nadine Sundquist (AdWords API Team)

unread,
Mar 3, 2017, 5:36:14 PM3/3/17
to AdWords API Forum
Hello Alex,

My teammates is totally awesome, and I already got a response back! To summarize what he's saying here, what you have should work just fine where you first fill in all the fields in your first call and then from then on you use the temporary ID for the feed item in all subsequent calls. 

1 & 2: ADD and SET operations are completely identical in the ExtensionSetting services in all cases, whether or not an ExtensionSetting already exists on a Campaign or Ad Group, whether or not the extensions being added already exist, and so on. You do not need to use ADD when creating new extensions.
3: This is correct. If you only provide FeedItem IDs and no other fields in an extension, the result is that we will assume you're referring to a FeedItem that already exists, and we will re-use that FeedItem.
4: FeedMappings are updated automatically during any operation, regardless of the contents of the extensions in the request.
4a: I think the way this works is that if you have two requests, the first request can contain a fully-populated extension with a temp ID, and the second request can have an extension with only that temp ID (and no other fields populated). Populating additional fields in the second request won't work if you're using the same temp ID.

I hope that clears things up!

Take care,
Nadine, AdWords API Team

Alexander Cavalli

unread,
Mar 3, 2017, 5:50:01 PM3/3/17
to AdWords API Forum
Hi Nadine,

Thanks for the timely response! That definitely clears up the particulars. I'm not sure what to mark here as the best answer - it's a bit scattered all over - so feel free to change the one I've marked if something else might be more helpful to future readers.

I'll reach out again I have more questions. Thanks again!
- Alex
Reply all
Reply to author
Forward
0 new messages