Extension Setting Services

Using extension setting services

Suppose you're advertising a restaurant in AdWords. You've already set up an AdWords campaign, but want to show the following extra information about your business:

  • Store hours: This link takes users to http://www.example.com/storehours.
  • Nutrition data: This link takes users to http://www.example.com/menu/nutritiondata, where nutritional information about menu items is listed.
  • Happy hours: This link takes users to http://www.example.com/happyhours, and should only be shown during these hours: 6pm to 9pm, Monday through Friday; and 5pm to 8pm on Saturday.
  • Thanksgiving special: You plan to have a special Thanksgiving menu from November 27 to 28. This link should be displayed only from November 20 to 27 so users can make their reservation at http://www.example.com/thanksgiving.

Each of the items above is an ad extension in AdWords. You can manage extensions using the extension setting services in the AdWords API.

Adding ad extensions

Ad extensions can be added at the account, campaign, or ad group level, though not all extension types are available at all levels. For new ad extensions, use either the ADD or SET operator. If the SET operator is used in a case where an ad extension does not exist, a new ad extension is added. Ad extensions come in several formats: For this example, we'll use sitelinks.

The following code snippet shows how to add new ad extensions to a campaign using CampaignExtensionSettingService.

// Get the CampaignExtensionSettingService.
CampaignExtensionSettingService campaignExtensionSettingService =
    (CampaignExtensionSettingService) user.GetService(
         AdWordsService.v201702.CampaignExtensionSettingService);

CampaignExtensionSetting campaignExtensionSetting =
    new CampaignExtensionSetting();
campaignExtensionSetting.campaignId = campaignId;
campaignExtensionSetting.extensionType = FeedType.SITELINK;

SitelinkFeedItem link1 = new SitelinkFeedItem() {
    sitelinkText = "Store Hours",
    sitelinkUrl = "http://www.example.com/storehours"
};

SitelinkFeedItem link2 = new SitelinkFeedItem() {
    sitelinkText = "Thanksgiving Specials",
    sitelinkUrl = "http://www.example.com/thanksgiving",
    startTime = "20141120 000000 EST",
    endTime = "20141127 235959 EST",

    // Target this sitelink for United States only. See
    // https://developers.google.com/adwords/api/docs/appendix/geotargeting
    // for valid geolocation codes.
    geoTargeting = new Location () {
      id = 2840
    };
};

SitelinkFeedItem link3 = new SitelinkFeedItem() {
    sitelinkText = "Nutrition Data",
    sitelinkUrl = "http://www.example.com/menu/nutritiondata"
};

SitelinkFeedItem link4 = new SitelinkFeedItem() {
    sitelinkText = "Happy hours",
    sitelinkUrl = "http://www.example.com/happyhours",
    scheduling = new FeedItemSchedule[] {
        new FeedItemSchedule() {
            dayOfWeek = AdWords.v201702.DayOfWeek.MONDAY,
            startHour = 18,
            startMinute = MinuteOfHour.ZERO,
            endHour = 21,
            endMinute = MinuteOfHour.ZERO
        },
        new FeedItemSchedule() {
            dayOfWeek = AdWords.v201702.DayOfWeek.TUESDAY,
            startHour = 18,
            startMinute = MinuteOfHour.ZERO,
            endHour = 21,
            endMinute = MinuteOfHour.ZERO
        },
        new FeedItemSchedule() {
            dayOfWeek = AdWords.v201702.DayOfWeek.WEDNESDAY,
            startHour = 18,
            startMinute = MinuteOfHour.ZERO,
            endHour = 21,
            endMinute = MinuteOfHour.ZERO
        },
        new FeedItemSchedule() {
            dayOfWeek = AdWords.v201702.DayOfWeek.THURSDAY,
            startHour = 18,
            startMinute = MinuteOfHour.ZERO,
            endHour = 21,
            endMinute = MinuteOfHour.ZERO
        },
        new FeedItemSchedule() {
            dayOfWeek = AdWords.v201702.DayOfWeek.FRIDAY,
            startHour = 18,
            startMinute = MinuteOfHour.ZERO,
            endHour = 21,
            endMinute = MinuteOfHour.ZERO
        },
        new FeedItemSchedule() {
            dayOfWeek = AdWords.v201702.DayOfWeek.SATURDAY,
            startHour = 17,
            startMinute = MinuteOfHour.ZERO,
            endHour = 21,
            endMinute = MinuteOfHour.ZERO
        }
    }
};

campaignExtensionSetting.extensionSetting = new ExtensionSetting() {
    extensions = new ExtensionFeedItem[] {
        link1, link2, link3, link4
    },
};

CampaignExtensionSettingOperation operation = new CampaignExtensionSettingOperation() {
    operand = campaignExtensionSetting,
    @operator = Operator.ADD,
};

CampaignExtensionSettingReturnValue retVal = campaignExtensionSettingService.mutate(
    new CampaignExtensionSettingOperation[] {operation});

Other ad extension types are added in a similar manner, as shown below.

Call extensions

You can list a business phone number alongside your ads that customers can call directly from the ad, using a CallFeedItem extension. The following code snippet shows how to do this:

CallFeedItem callFeedItem = new CallFeedItem() {
    callCountryCode = "US",
    callPhoneNumber = "212-565-0000",
};

CampaignExtensionSetting campaignExtensionSetting =
    new CampaignExtensionSetting();
campaignExtensionSetting.campaignId = campaignId;

campaignExtensionSetting.extensionSetting = new ExtensionSetting() {
    extensions = new ExtensionFeedItem[] { callFeedItem },
};

Review extensions

Business reviews can be listed alongside your ads using a review extension. The following code snippet shows how to create a review extension:

ReviewFeedItem reviewFeeditem = new ReviewFeedItem() {
    reviewSourceName = "Example Food review magazine",
    reviewSourceUrl =
        "http://www.example.com/reviews/2014/03/the-amazing-example-hotel",
    reviewText = "The best food in New York city!",
    reviewTextExactlyQuoted = true
};

CampaignExtensionSetting campaignExtensionSetting =
    new CampaignExtensionSetting();
campaignExtensionSetting.campaignId = campaignId;

campaignExtensionSetting.extensionSetting = new ExtensionSetting() {
    extensions = new ExtensionFeedItem[] { reviewFeeditem },
};

Callout extensions

The callout ad extension lets you provide additional details, such as what products or services you offer, using text right below your search ads. The following code snippet shows how to create a callout extension:

CalloutFeedItem freeDeliveryCallout = new CalloutFeedItem() {
   calloutText = "Free delivery",
};
CalloutFeedItem kidsCallout = new CalloutFeedItem() {
   calloutText = "Kids eat free",
};

CampaignExtensionSetting campaignExtensionSetting = new CampaignExtensionSetting();
campaignExtensionSetting.campaignId = campaignId;
campaignExtensionSetting.extensionType = FeedType.CALLOUT;
campaignExtensionSetting.extensionSetting = new ExtensionSetting() {
    extensions = new ExtensionFeedItem[] {
        freeDeliveryCallout, kidsCallout
    }
};

App extensions

If your business developed an Android or iOS app to target mobile users, you can display links to the app alongside your ads using an app extension. App extensions are shown only to users who are currently logged in to Google, and who haven't yet installed your app. Clicking the app extension link takes them to the Google Play or Apple iTunes page for your app. This code snippet shows how to create an app extension for an application on Google Play:

AppFeedItem appFeedItem = new AppFeedItem() {
    appId = "com.example.mobileapp",
    appStore = AppFeedItemAppStore.GOOGLE_PLAY,
    appLinkText = "Install our mobile app!",
    appUrl = "https://play.google.com/store/apps/details?id=com.example.mobileapp"
};

CampaignExtensionSetting campaignExtensionSetting =
    new CampaignExtensionSetting();
campaignExtensionSetting.campaignId = campaignId;

campaignExtensionSetting.extensionSetting = new ExtensionSetting() {
    extensions = new ExtensionFeedItem[] { appFeedItem },
};

Message extensions

Message extensions allow people to see your ad, click an icon, and contact you directly by text message. With one tap on your ad, people can contact you to book an appointment, get a quote, ask for information, or request a service. The following code snippet shows you how to do this using a MessageFeedItem:

MessageFeedItem messageFeedItem = new MessageFeedItem() {
    messageBusinessName = "Travel Here",
    messageCountryCode = "US",
    messagePhoneNumber = "212-565-0000",
    messageText = "I want to know more.",
    messageExtensionText = "Ask us about travel.",
};

CampaignExtensionSetting campaignExtensionSetting =
    new CampaignExtensionSetting();
campaignExtensionSetting.campaignId = campaignId;

campaignExtensionSetting.extensionSetting = new ExtensionSetting() {
    extensions = new ExtensionFeedItem[] { messageFeedItem },
};

Price extensions

A price extension is a new mobile-only extension type that provides users comparative details and pricing of your products or services. Price extensions allow you to enter a list of services, products, events, or other items, with a list of prices to display to the user.

Fully configurable and targetable like other extension types, price extensions show titles, descriptive information, and pricing details for at least three and as many as eight of your products or services.

Price extensions can be attached at the account, campaign, or ad group level.

The following code snippet shows how to create a price extension:

// Create the price extension feed item.
PriceFeedItem priceFeedItem = new PriceFeedItem();
priceFeedItem.setPriceExtensionType(PriceExtensionType.SERVICES);

// Price qualifier is optional.
priceFeedItem.setPriceQualifier(PriceExtensionPriceQualifier.FROM);
priceFeedItem.setTrackingUrlTemplate("http://tracker.example.com/?u={lpurl}");
priceFeedItem.setLanguage("en");
FeedItemCampaignTargeting campaignTargeting = new FeedItemCampaignTargeting();
campaignTargeting.setTargetingCampaignId(campaignId);
priceFeedItem.setCampaignTargeting(campaignTargeting);
priceFeedItem.setScheduling(
    new FeedItemScheduling(
        new FeedItemSchedule[] {
          new FeedItemSchedule(DayOfWeek.SUNDAY, 10, MinuteOfHour.ZERO, 18, MinuteOfHour.ZERO),
          new FeedItemSchedule(DayOfWeek.SATURDAY, 10, MinuteOfHour.ZERO, 22, MinuteOfHour.ZERO)
        }));

// To create a price extension, at least three table rows are needed.
List<PriceTableRow> priceTableRows = Lists.newArrayList();
String currencyCode = "USD";
priceTableRows.add(
    createPriceTableRow(
        "Scrubs",
        "Body Scrub, Salt Scrub",
        "http://www.example.com/scrubs",
        "http://m.example.com/scrubs",
        60000000,
        currencyCode,
        PriceExtensionPriceUnit.PER_HOUR));
priceTableRows.add(
    createPriceTableRow(
        "Hair Cuts",
        "Once a month",
        "http://www.example.com/haircuts",
        "http://m.example.com/haircuts",
        75000000,
        currencyCode,
        PriceExtensionPriceUnit.PER_MONTH));
priceTableRows.add(
    createPriceTableRow(
        "Skin Care Package",
        "Four times a month",
        "http://www.example.com/skincarepackage",
        null,
        250000000,
        currencyCode,
        PriceExtensionPriceUnit.PER_MONTH));
priceFeedItem.setTableRows(priceTableRows.toArray(new PriceTableRow[priceTableRows.size()]));

// Create your campaign extension settings. This associates the sitelinks
// to your campaign.
CustomerExtensionSetting customerExtensionSetting = new CustomerExtensionSetting();
customerExtensionSetting.setExtensionType(FeedType.PRICE);
ExtensionSetting extensionSetting = new ExtensionSetting();
extensionSetting.setExtensions(new ExtensionFeedItem[] {priceFeedItem});
customerExtensionSetting.setExtensionSetting(extensionSetting);

The reference to createPriceTableRow is defined as shown below:

/**
 * Creates a new {@link PriceTableRow} with the specified attributes.
 *
 * @param header the header for the row
 * @param description the description for the row
 * @param finalUrl the final URL for the row
 * @param finalMobileUrl the final mobile URL for the row, or null if this field should not be set
 * @param priceInMicros the price for the row, in micros
 * @param currencyCode the currency for the row
 * @param priceUnit the price unit for the row
 * @return a new {@link PriceTableRow}
 */
private static PriceTableRow createPriceTableRow(
    String header,
    String description,
    String finalUrl,
    String finalMobileUrl,
    long priceInMicros,
    String currencyCode,
    PriceExtensionPriceUnit priceUnit) {
  PriceTableRow priceTableRow = new PriceTableRow();
  priceTableRow.setHeader(header);
  priceTableRow.setDescription(description);

  UrlList finalUrls = new UrlList();
  finalUrls.setUrls(new String[] {finalUrl});
  priceTableRow.setFinalUrls(finalUrls);

  if (finalMobileUrl != null) {
    UrlList finalMobileUrls = new UrlList();
    finalMobileUrls.setUrls(new String[] {finalMobileUrl});
    priceTableRow.setFinalMobileUrls(finalMobileUrls);
  }

  MoneyWithCurrency price = new MoneyWithCurrency();
  Money priceMoney = new Money();
  price.setCurrencyCode(currencyCode);
  priceMoney.setMicroAmount(priceInMicros);
  price.setMoney(priceMoney);
  priceTableRow.setPrice(price);
  priceTableRow.setPriceUnit(priceUnit);

  return priceTableRow;
}

Update ad extensions

Suppose you decided to change your restaurant's happy hours on Saturday to 5pm to 10pm. For existing ad extensions, use either the ADD or SET operator. If the ADD operator is used when an ad extension already exists, the existing ad extension is modified.

The first step is to retrieve the list of existing settings:

// Get the CampaignExtensionSettingService.
CampaignExtensionSettingService campaignExtensionSettingService =
    (CampaignExtensionSettingService) user.GetService(
         AdWordsService.v201702.CampaignExtensionSettingService);

Selector selector = new Selector() {
    fields = new string[] { "CampaignId", "ExtensionType", "Extensions" },
    predicates = new Predicate[] {
        new Predicate() {
            field = "CampaignId",
            @operator = PredicateOperator.EQUALS,
            values = new string[] {campaignId.ToString()}
        },
        new Predicate() {
            field = "ExtensionType",
            @operator = PredicateOperator.EQUALS,
            values = new string[] {"SITELINK"}
        },
    }
};

CampaignExtensionSettingPage page = campaignExtensionSettingService.get(selector);

Next, update the desired sitelink:

CampaignExtensionSetting campaignExtensionSetting = page.entries[0];
foreach (SitelinkFeedItem siteLink in
     campaignExtensionSetting.extensionSetting.extensions) {
  if (siteLink.sitelinkText == "Happy hours") {
    foreach (FeedItemSchedule schedule in siteLink.scheduling) {
      if (schedule.dayOfWeek == AdWords.v201702.DayOfWeek.SATURDAY) {
         schedule.startHour = 17;
         schedule.startMinute = MinuteOfHour.ZERO;
         schedule.endHour = 22;
         schedule.endMinute = MinuteOfHour.ZERO;
      }
    }
  }
}

Finally, send the modified campaignExtensionSetting to the server.

CampaignExtensionSettingOperation operation = new CampaignExtensionSettingOperation() {
  operand = campaignExtensionSetting,
  @operator = Operator.SET,
};

To prevent the new settings from overwriting the old settings, remember to send back all feed items, even if you're modifying just one item.

Targeting options for extensions

In addition to using an ExtensionSetting to target an extension at the customer, campaign, or ad group levels, you can also set targeting options on an individual ExtensionFeedItem by setting the campaignTargeting, adGroupTargeting, keywordTargeting, or geoTargeting attributes.

These targeting options will be combined with the ExtensionSetting properties to select which extension feed items will be used for a given impression.

For example, let's say you create a SitelinkFeedItem with the following targeting options:

adGroupTargeting.TargetingAdGroupId = 12345
keywordTargeting.id = 7890

In addition, you set the AdGroupExtensionSetting's platform restriction to MOBILE.

When serving impressions for this ad group, AdWords only serves sitelinks from the SitelinkFeedItem if the impression attributes satisfy the targeting options on both AdGroupExtensionSetting and SitelinkFeedItem objects.

Case Impression attributes Result
1
  • ad group ID 12345
  • keyword 7890
  • a desktop user
The SitelinkFeedItem will not be used because the platform (desktop) does not satisfy the targeting options on AdGroupExtensionSetting.
2
  • ad group ID 12345
  • keyword 8910
  • a mobile user.
The SitelinkFeedItem will not be used because the keyword (7890) does not satisfy targeting options on SitelinkFeedItem.
3
  • ad group ID 12345
  • keyword 7890
  • a mobile user.
The SitelinkFeedItem will be used because the attributes satisfy the targeting options on both AdGroupExtensionSetting and SitelinkFeedItem.

Track ad extension performance

Use the Placeholder report or Placeholder Feed Item report to track the performance of your ad extensions. To identify your ad extensions use the FeedItemId column.

Migrate ad extensions to extension setting services

The rest of this guide covers the pros and cons of extension setting services, to help you decide whether to start using them, or stay with feed services. It also describes how to migrate to the new services.

Extension setting services provide a simplified layer over the existing feed-based ad extensions. The new services:

  • Support concrete types for all supported ad extensions.
  • Provide a simplified API that allows you to manage ad extensions for a campaign or ad group.

Should I migrate?

Extension setting services cover all ad extension features currently available in the AdWords user interface. Most users will benefit from spending some time to rewrite their services and migrate their data to use the simplified extension setting services. The following sections will help you decide whether or not to migrate to the extension setting services.

When are extension setting services preferred?

If you simply need to add ad extensions to a campaign or ad group, set its device preference, or manage its schedule, then we recommend using extension setting services. An added benefit when using extension setting services is that unlike feed services, we maintain your feed's schema and data, so you won't have to keep up with the underlying feed structure.

When are legacy feed services preferred?

We recommend using the legacy feed services if you need to use one or more of the features listed below:

  • Location extensions: Extension setting services do not include support for location extensions.
  • Custom fields or matching functions: Extension setting services don't support custom fields in feeds, or writing custom matching functions.
  • Multiple feeds: Extension setting services only support one feed per extension type that is managed by the system.

Migration steps

If you've only used feeds created in the AdWords user interface, you can use extension setting services immediately.

However, if you've created custom feeds with the API, you'll need to first perform the following steps:

  1. Retrieve feed items in your custom feeds.
  2. Identify feed items you want to keep.
  3. Delete the CustomerFeed, CampaignFeed and AdGroupFeeds that use the feed items from the custom feeds.
  4. Create CustomerExtensionSetting, CampaignExtensionSetting and AdGroupExtensionSettings that use the new ExtensionFeedItems.
  5. (Optional) Delete feed items that are no longer in use.

The sections below focus on upgrading ad extensions at the campaign level, but the same process applies at the ad group level.

Retrieve feed items in your custom feeds

To retrieve feed items in your custom feeds, you must first identify all custom feeds you've created using the AdWords API, as follows:

private Feed[] GetFeeds(AdWordsUser user) {
  FeedService feedService = (FeedService)user.GetService(
      AdWordsService.v201702.FeedService);
  FeedPage page = feedService.query("SELECT Id, Name, Attributes where " +
      "Origin='USER' and FeedStatus='ENABLED'");
  return page.entries;
}

Next, retrieve feed items in these feeds. The example uses sitelinks, but the approach is similar for the other ad extensions.

private FeedItem[] GetFeedItems(AdWordsUser user, long feedId) {
  FeedItemService feedItemService = (FeedItemService) user.GetService(
      AdWordsService.v201702.FeedItemService);
  FeedItemPage page = feedItemService.query(string.Format("Select FeedItemId, " +
      "AttributeValues, Scheduling where Status = 'ENABLED' and FeedId = '{0}'", feedId));
  return page.entries;
}

For ease of handling, load the values into a locally defined class named SitelinkFromFeed:

class SiteLinkFromFeed {
  public long FeedId {get;set;}
  public long FeedItemId { get; set; }
  public string Text { get; set; }
  public string Url { get; set; }
  public string[] FinalUrls { get; set; }
  public string[] FinalMobileUrls { get; set; }
  public string TrackingUrlTemplate { get; set; }
  public string Line2 { get; set; }
  public string Line3 { get; set; }
  public FeedItemSchedule[] Scheduling { get; set; }
}

You should also define a few constants to help better parse the FeedMappings. You can find these placeholder values on the feed placeholder page.

const int PLACEHOLDER_TYPE_SITELINKS = 1;

class SiteLinkFields {
  public const long TEXT = 1;
  public const long URL = 2;
  public const long LINE2 = 3;
  public const long LINE3 = 4;
  public const long FINAL_URLS = 5;
  public const long FINAL_MOBILE_URLS = 6;
  public const long TRACKING_URL_TEMPLATE = 7;
};

To convert a FeedItem into a SitelinksFromFeed object, you must retrieve the FeedMapping for the feed, and then map each attribute into the corresponding object properties:

private Dictionary<long, SiteLinkFromFeed> GetSiteLinksFromFeed(AdWordsUser user,
    long feedId) {
  Dictionary<long, SiteLinkFromFeed> siteLinks =
      new Dictionary<long, SiteLinkFromFeed>();

  Dictionary<long, long> feedMappings = GetFeedMapping(user, feedId,
      PLACEHOLDER_TYPE_SITELINKS);
  FeedItem[] feedItems = GetFeedItems(user, feedId);

  if (feedItems != null) {
    foreach (FeedItem feedItem in feedItems) {
      SiteLinkFromFeed sitelinkFromFeed = new SiteLinkFromFeed() {
        FeedId = feedItem.feedId,
        FeedItemId = feedItem.feedItemId
      };
      foreach (FeedItemAttributeValue attributeValue in feedItem.attributeValues) {
        switch (attributeValue.feedAttributeId) {
          case SiteLinkFields.TEXT:
            sitelinkFromFeed.Text = attributeValue.stringValue;
            break;

          case SiteLinkFields.URL:
            sitelinkFromFeed.Url = attributeValue.stringValue;
            break;

          case SiteLinkFields.FINAL_URLS:
            sitelinkFromFeed.FinalUrls = attributeValue.stringValues;
            break;

          case SiteLinkFields.FINAL_MOBILE_URLS:
            sitelinkFromFeed.FinalMobileUrls = attributeValue.stringValues;
            break;

          case SiteLinkFields.TRACKING_URL_TEMPLATE:
            sitelinkFromFeed.TrackingUrlTemplate = attributeValue.stringValue;
            break;

          case SiteLinkFields.LINE2:
            sitelinkFromFeed.Line2 = attributeValue.stringValue;
            break;

          case SiteLinkFields.LINE3:
            sitelinkFromFeed.Text = attributeValue.stringValue;
            break;
        }
      }
      sitelinkFromFeed.Scheduling = feedItem.scheduling;
      siteLinks.Add(feedItem.feedItemId, sitelinkFromFeed);
    }
  }
  return siteLinks;
}

private Dictionary<long, long> GetFeedMapping(AdWordsUser user, long feedId,
    long placeHolderType) {
  FeedMappingService feedMappingService = (FeedMappingService) user.GetService(
      AdWordsService.v201702.FeedMappingService);
  FeedMappingPage page = feedMappingService.query(string.Format(
      "SELECT FeedMappingId,AttributeFieldMappings where FeedId='{0}' " +
      "and PlaceholderType={1} and Status='ENABLED'",
      feedId, placeHolderType));

  Dictionary<long, long> attributeMappings = new Dictionary<long, long>();

  if (page.entries != null) {
    foreach (FeedMapping feedMapping in page.entries) {
      foreach (AttributeFieldMapping attributeMapping in
          feedMapping.attributeFieldMappings) {
        attributeMappings.Add(attributeMapping.feedAttributeId,
            attributeMapping.fieldId);
      }
    }
  }
  return attributeMappings;
}

Identify feed items you want to keep

Identify the list of feed items associated with a campaign by retrieving the CampaignFeed associated with the campaign and parsing its matchingFunction. Since matching functions allow you to write a general-purpose expression tree, writing a parser for a general case is complex, and we won't cover it in this guide. We'll instead pick the simplest case, where a few feed items from a single feed have been associated with a campaign. This matching function takes the following form:

FEEDITEM_ID IN (FEEDITEM_ID_1, FEEDITEM_ID_2…)

private CampaignFeed[] GetCampaignFeeds(AdWordsUser user, Feed feed, int placeholderType) {
  CampaignFeedService campaignFeedService = (CampaignFeedService) user.GetService(
      AdWordsService.v201702.CampaignFeedService);

  CampaignFeedPage page = campaignFeedService.query(string.Format(
      "SELECT CampaignId, MatchingFunction, PlaceholderTypes where Status='ENABLED' " +
      "and FeedId = '{0}' and PlaceholderTypes CONTAINS_ANY[{1}]", feed.id, placeholderType));
  return page.entries;
}

private List<long> GetFeedItemsForCampaign(CampaignFeed campaignFeed) {
  List<long> feedItems = new List<long>();
  if (campaignFeed.matchingFunction.lhsOperand.Length == 1 &&
      campaignFeed.matchingFunction.lhsOperand[0] is RequestContextOperand &&
      (campaignFeed.matchingFunction.lhsOperand[0] as
           RequestContextOperand).contextType ==
           RequestContextOperandContextType.FEED_ITEM_ID &&
      campaignFeed.matchingFunction.@operator == FunctionOperator.IN) {
    foreach (ConstantOperand argument in campaignFeed.matchingFunction.rhsOperand) {
      feedItems.Add(argument.longValue);
    }
  }
  return feedItems;
}

Delete existing CampaignFeed

Before you add extension settings to a campaign, you must delete the campaign's existing CampaignFeed. This is required since AdWords supports only one CampaignFeed per extension type for a campaign. Deleting your manually-created CampaignFeed allows the CampaignExtensionSettingService to create a new system-managed CampaignFeed when you add extension settings.

private CampaignFeed DeleteCampaignFeed(AdWordsUser user, CampaignFeed campaignFeed) {
  CampaignFeedService campaignFeedService = (CampaignFeedService) user.GetService(
      AdWordsService.v201702.CampaignFeedService);

  CampaignFeedOperation operation = new CampaignFeedOperation() {
    operand = campaignFeed,
    @operator = Operator.REMOVE,
  };

  return campaignFeedService.mutate(
       new CampaignFeedOperation[] { operation }).value[0];
}

Add extension settings

You can now create extension settings corresponding to the old feed items:

private static void CreateExtensionSetting(AdWordsUser user,
    Dictionary<long, SiteLinkFromFeed> feedItems, CampaignFeed campaignFeed,
    List<long> feedItemIds) {
  CampaignExtensionSetting extensionSetting = new CampaignExtensionSetting() {
    campaignId = campaignFeed.campaignId,
    extensionType = FeedType.SITELINK,
    extensionSetting = new ExtensionSetting() {
    }
  };

  List<ExtensionFeedItem> extensionFeedItems = new List<ExtensionFeedItem>();

  foreach (long feedItemId in feedItemIds) {
    SiteLinkFromFeed feedItem = feedItems[feedItemId];
    SitelinkFeedItem newFeedItem = new SitelinkFeedItem() {
      sitelinkText = feedItem.Text,
      sitelinkUrl = feedItem.Url,
      sitelinkFinalUrls = feedItem.FinalUrls,
      sitelinkFinalMobileUrls = feedItem.FinalMobileUrls,
      sitelinkTrackingUrlTemplate = feedItem.TrackingUrlTemplate,
      sitelinkLine2 = feedItem.Line2,
      sitelinkLine3 = feedItem.Line3,
      scheduling = feedItem.Scheduling
    };
    extensionFeedItems.Add(newFeedItem);
  }
  extensionSetting.extensionSetting.extensions = extensionFeedItems.ToArray();

  CampaignExtensionSettingService campaignExtensionSettingService =
      (CampaignExtensionSettingService) user.GetService(
          AdWordsService.v201702.CampaignExtensionSettingService);
  CampaignExtensionSettingOperation operation =
      new CampaignExtensionSettingOperation() {
        operand = extensionSetting,
        @operator = Operator.ADD
      };

  campaignExtensionSettingService.mutate(
      new CampaignExtensionSettingOperation[] {operation});
  return;
}

Keep in mind that this process does not de-duplicate the extension settings; you may want to enhance this code to exclude duplicate items.

Delete old FeedItems

Finally, delete the old FeedItems:

private void DeleteOldFeedItems(AdWordsUser user, List<long> feedItemIds,
    long feedId) {
  if (feedItemIds.Count == 0) {
    return;
  }
  List<FeedItemOperation> operations = new List<FeedItemOperation>();
  foreach (long feedItemId in feedItemIds) {
    FeedItemOperation operation = new FeedItemOperation() {
      @operator = Operator.REMOVE,
      operand = new FeedItem() {
        feedItemId = feedItemId,
        feedId = feedId
      }
    };
    operations.Add(operation);
  }
  FeedItemService feedItemService = (FeedItemService) user.GetService(
      AdWordsService.v201702.FeedItemService);
  feedItemService.mutate(operations.ToArray());
  return;
}

This is an optional step, but we recommend deleting unused feed items to keep the number of active feed items under system limits.

Putting it all together: The main loop

The main loop of this program corresponds to the sequence of steps discussed earlier:

Feed[] feeds = GetFeeds(user);
foreach (Feed feed in feeds) {
  Dictionary<long, SiteLinkFromFeed> feedItems = GetSiteLinksFromFeed(user, feed.id);
  CampaignFeed[] campaignFeeds = GetCampaignFeeds(user, feed,
      PLACEHOLDER_TYPE_SITELINKS);

  if (campaignFeeds != null) {
    HashSet<long> allFeedItemsToDelete = new HashSet<long>();
    foreach (CampaignFeed campaignFeed in campaignFeeds) {
      // Optional: Replace with custom logic that upgrades only selected
      // feed items.
      List<long> feedItemIds = GetFeedItemsForCampaign(campaignFeed);
      DeleteCampaignFeed(user, campaignFeed);
      CreateExtensionSetting(user, feedItems, campaignFeed, feedItemIds);
      allFeedItemsToDelete.UnionWith(feedItemIds);
    }
    DeleteOldFeedItems(user, new List<long>(allFeedItemsToDelete), feed.id);
  }
}

Code examples

Refer to the following code examples in our client libraries to learn more about extension setting services.

Send feedback about...

AdWords API
AdWords API
Need help? Visit our support page.