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.

You can use sitelinks to link to specific subpages within your site, aside from your main landing page, making your site easier to navigate for users.

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

Java

public static void runExample(AdWordsServicesInterface adWordsServices, AdWordsSession session,
    Long campaignId) throws ApiException, RemoteException {
  // Get the CustomerService.
  CustomerServiceInterface customerService =
      adWordsServices.get(session, CustomerServiceInterface.class);

  // Find the matching customer and its time zone. The getCustomers method will return
  // a single Customer object corresponding to the session's clientCustomerId.
  Customer customer = customerService.getCustomers()[0];
  DateTimeZone customerTimeZone = DateTimeZone.forID(customer.getDateTimeZone());
  System.out.printf(
      "Found customer ID %d with time zone '%s'.%n",
      customer.getCustomerId(), customer.getDateTimeZone());

  // Get the CampaignExtensionSettingService.
  CampaignExtensionSettingServiceInterface campaignExtensionSettingService =
      adWordsServices.get(session, CampaignExtensionSettingServiceInterface.class);

  // Create the sitelinks.
  SitelinkFeedItem sitelink1 =
      createSiteLinkFeedItem("Store Hours", "http://www.example.com/storehours");

  // Show the Thanksgiving specials link only from 20 - 27 Nov.
  SitelinkFeedItem sitelink2 =
      createSiteLinkFeedItem("Thanksgiving Specials", "http://www.example.com/thanksgiving");

  // The time zone of the start and end date/times must match the time zone of the customer.
  DateTime startTime = new DateTime(DateTime.now().getYear(), 11, 20, 0, 0, 0, customerTimeZone);
  if (startTime.isBeforeNow()) {
    // Move the startTime to next year if the current date is past November 20th.
    startTime = startTime.plusYears(1);
  }
  sitelink2.setStartTime(startTime.toString("yyyyMMdd HHmmss ZZZ"));
  // Use the same year as startTime when creating endTime.
  DateTime endTime = new DateTime(startTime.getYear(), 11, 27, 23, 59, 59, customerTimeZone);
  sitelink2.setEndTime(endTime.toString("yyyyMMdd HHmmss ZZZ"));

  // Target this sitelink for United States only. See
  // https://developers.google.com/adwords/api/docs/appendix/geotargeting
  // for valid geolocation codes.
  Location unitedStates = new Location();
  unitedStates.setId(2840L);
  sitelink2.setGeoTargeting(unitedStates);

  // Restrict targeting only to people physically within the United States.
  // Otherwise, this could also show to people interested in the United States
  // but not physically located there.
  FeedItemGeoRestriction geoTargetingRestriction = new FeedItemGeoRestriction();
  geoTargetingRestriction.setGeoRestriction(GeoRestriction.LOCATION_OF_PRESENCE);
  sitelink2.setGeoTargetingRestriction(geoTargetingRestriction);

  // Show the wifi details primarily for high end mobile users.
  SitelinkFeedItem sitelink3 =
      createSiteLinkFeedItem("Wifi available", "http://www.example.com/mobile/wifi");
  // See https://developers.google.com/adwords/api/docs/appendix/platforms for device criteria
  // IDs.
  FeedItemDevicePreference devicePreference = new FeedItemDevicePreference(30001L);
  sitelink3.setDevicePreference(devicePreference);

  // Target this sitelink for the keyword "free wifi".
  Keyword wifiKeyword = new Keyword();
  wifiKeyword.setText("free wifi");
  wifiKeyword.setMatchType(KeywordMatchType.BROAD);
  sitelink3.setKeywordTargeting(wifiKeyword);

  // Show the happy hours link only during Mon - Fri 6PM to 9PM.
  SitelinkFeedItem sitelink4 =
      createSiteLinkFeedItem("Happy hours", "http://www.example.com/happyhours");
  sitelink4.setScheduling(new FeedItemScheduling(new FeedItemSchedule[] {
      new FeedItemSchedule(DayOfWeek.MONDAY, 18, MinuteOfHour.ZERO, 21, MinuteOfHour.ZERO),
      new FeedItemSchedule(DayOfWeek.TUESDAY, 18, MinuteOfHour.ZERO, 21, MinuteOfHour.ZERO),
      new FeedItemSchedule(DayOfWeek.WEDNESDAY, 18, MinuteOfHour.ZERO, 21, MinuteOfHour.ZERO),
      new FeedItemSchedule(DayOfWeek.THURSDAY, 18, MinuteOfHour.ZERO, 21, MinuteOfHour.ZERO),
      new FeedItemSchedule(DayOfWeek.FRIDAY, 18, MinuteOfHour.ZERO, 21, MinuteOfHour.ZERO)}));

  // Create your campaign extension settings. This associates the sitelinks
  // to your campaign.
  CampaignExtensionSetting campaignExtensionSetting = new CampaignExtensionSetting();
  campaignExtensionSetting.setCampaignId(campaignId);
  campaignExtensionSetting.setExtensionType(FeedType.SITELINK);
  ExtensionSetting extensionSetting = new ExtensionSetting();
  extensionSetting.setExtensions(
      new ExtensionFeedItem[] {sitelink1, sitelink2, sitelink3, sitelink4});
  campaignExtensionSetting.setExtensionSetting(extensionSetting);

  CampaignExtensionSettingOperation operation = new CampaignExtensionSettingOperation();
  operation.setOperand(campaignExtensionSetting);
  operation.setOperator(Operator.ADD);

  // Add the extensions.
  CampaignExtensionSettingReturnValue returnValue =
      campaignExtensionSettingService.mutate(new CampaignExtensionSettingOperation[] {operation});
  if (returnValue.getValue() != null && returnValue.getValue().length > 0) {
    CampaignExtensionSetting newExtensionSetting = returnValue.getValue(0);
    System.out.printf("Extension setting with type '%s' was added to campaign ID %d.%n",
        newExtensionSetting.getExtensionType().getValue(), newExtensionSetting.getCampaignId());
  } else {
    System.out.println("No extension settings were created.");
  }
}

/**
 * Creates a new {@link SitelinkFeedItem} with the specified attributes.
 *
 * @param sitelinkText the text for the sitelink
 * @param sitelinkUrl the URL for the sitelink
 * @return a new SitelinkFeedItem
 */
private static SitelinkFeedItem createSiteLinkFeedItem(String sitelinkText, String sitelinkUrl) {
  SitelinkFeedItem sitelinkFeedItem = new SitelinkFeedItem();
  sitelinkFeedItem.setSitelinkText(sitelinkText);
  sitelinkFeedItem.setSitelinkFinalUrls(new UrlList(new String[] {sitelinkUrl}));
  return sitelinkFeedItem;
}

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:

Java

CallFeedItem callFeedItem = new CallFeedItem();
callFeedItem.setCallCountryCode("US");
callFeedItem.setCallPhoneNumber("212-565-0000");

CampaignExtensionSetting campaignExtensionSetting = new CampaignExtensionSetting();
campaignExtensionSetting.setCampaignId(campaignId);
campaignExtensionSetting.setExtensionType(FeedType.CALL);
ExtensionSetting extensionSetting = new ExtensionSetting();
extensionSetting.setExtensions(new ExtensionFeedItem[] {callFeedItem});
campaignExtensionSetting.setExtensionSetting(extensionSetting);

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:

Java

ReviewFeedItem reviewFeedItem = new ReviewFeedItem();
reviewFeedItem.setReviewSourceName("Example food review magazine");
reviewFeedItem.setReviewSourceUrl(
    "http://example.com/review/2014/03/the-amazing-example-hotel");
reviewFeedItem.setReviewText("The best food in New York City!");
reviewFeedItem.setReviewTextExactlyQuoted(true);

CampaignExtensionSetting campaignExtensionSetting = new CampaignExtensionSetting();
campaignExtensionSetting.setCampaignId(campaignId);
campaignExtensionSetting.setExtensionType(FeedType.REVIEW);
ExtensionSetting extensionSetting = new ExtensionSetting();
extensionSetting.setExtensions(new ExtensionFeedItem[] {reviewFeedItem});
campaignExtensionSetting.setExtensionSetting(extensionSetting);

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:

Java

CalloutFeedItem freeDeliveryCallout = new CalloutFeedItem();
freeDeliveryCallout.setCalloutText("Free delivery");

CalloutFeedItem kidsCallout = new CalloutFeedItem();
kidsCallout.setCalloutText("Kids eat free");

CampaignExtensionSetting campaignExtensionSetting = new CampaignExtensionSetting();
campaignExtensionSetting.setCampaignId(campaignId);
campaignExtensionSetting.setExtensionType(FeedType.CALLOUT);
ExtensionSetting extensionSetting = new ExtensionSetting();
extensionSetting.setExtensions(new ExtensionFeedItem[] {freeDeliveryCallout, kidsCallout});
campaignExtensionSetting.setExtensionSetting(extensionSetting);

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:

Java

AppFeedItem appFeedItem = new AppFeedItem();
appFeedItem.setAppId("com.example.mobileapp");
appFeedItem.setAppStore(AppFeedItemAppStore.GOOGLE_PLAY);
appFeedItem.setAppLinkText("Install our mobile app!");
UrlList finalUrlsList = new UrlList();
finalUrlsList.setUrls(
    new String[] {"https://play.google.com/store/apps/details?id=com.example.mobileapp"});
appFeedItem.setAppFinalUrls(finalUrlsList);

CampaignExtensionSetting campaignExtensionSetting = new CampaignExtensionSetting();
campaignExtensionSetting.setCampaignId(campaignId);
campaignExtensionSetting.setExtensionType(FeedType.APP);
ExtensionSetting extensionSetting = new ExtensionSetting();
extensionSetting.setExtensions(new ExtensionFeedItem[] {appFeedItem});
campaignExtensionSetting.setExtensionSetting(extensionSetting);

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:

Java

MessageFeedItem messageFeedItem = new MessageFeedItem();
messageFeedItem.setMessageBusinessName("Travel Here");
messageFeedItem.setMessageCountryCode("US");
messageFeedItem.setMessagePhoneNumber("212-565-0000");
messageFeedItem.setMessageText("I want to know more");
messageFeedItem.setMessageExtensionText("Ask us about travel.");

CampaignExtensionSetting campaignExtensionSetting = new CampaignExtensionSetting();
campaignExtensionSetting.setCampaignId(campaignId);
campaignExtensionSetting.setExtensionType(FeedType.MESSAGE);
ExtensionSetting extensionSetting = new ExtensionSetting();
extensionSetting.setExtensions(new ExtensionFeedItem[] {messageFeedItem});
campaignExtensionSetting.setExtensionSetting(extensionSetting);

Price extensions

A price extension is an 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:

Java

// 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 = new ArrayList<>();
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);

Python

# To create a price extension, at least three table rows are needed.
table_rows = [
    CreatePriceTableRow('Scrubs', 'Body Scrub, Salt Scrub',
                        'https://www.example.com/scrubs',
                        60 * MICROS_PER_DOLLAR, 'USD', 'PER_HOUR',
                        final_mobile_url='http://m.example.com/scrubs'),
    CreatePriceTableRow('Hair Cuts', 'Once a month',
                        'https://www.example.com/haircuts',
                        75 * MICROS_PER_DOLLAR, 'USD', 'PER_MONTH',
                        final_mobile_url='http://m.example.com/haircuts'),
    CreatePriceTableRow('Skin Care Package', 'Four times a month',
                        'https://www.examples.com/skincarepackage',
                        250 * MICROS_PER_DOLLAR, 'USD', 'PER_MONTH',
                        final_mobile_url=(
                            'http://m.example.com/skincarepackage'))
]

# Create the price extension feed item.
customer_extension_setting = {
    'extensionType': 'PRICE',
    'extensionSetting': {
        'extensions': [{
            'priceExtensionType': 'SERVICES',
            'trackingUrlTemplate': 'http://tracker.example.com/?u={lpurl}',
            'language': 'en',
            'campaignTargeting': {
                'TargetingCampaignId': campaign_id
            },
            'scheduling': {
                'feedItemSchedules': [
                    {
                        'dayOfWeek': 'SATURDAY',
                        'startHour': 10,
                        'startMinute': 'ZERO',
                        'endHour': 22,
                        'endMinute': 'ZERO'
                    },
                    {
                        'dayOfWeek': 'SUNDAY',
                        'startHour': 10,
                        'startMinute': 'ZERO',
                        'endHour': 18,
                        'endMinute': 'ZERO'
                    }
                ]
            },
            'tableRows': table_rows,
            # Price qualifier is optional.
            'priceQualifier': 'FROM',
            'xsi_type': 'PriceFeedItem'
        }]
    }
}

PHP

// Create the price extension feed item.
$priceFeedItem = new PriceFeedItem();
$priceFeedItem->setPriceExtensionType(PriceExtensionType::SERVICES);
// Price qualifer is optional.
$priceFeedItem->setPriceQualifier(PriceExtensionPriceQualifier::FROM);
$priceFeedItem->setTrackingUrlTemplate(
    'http://tracker.example.com/?u={lpurl}');
$priceFeedItem->setLanguage('en');
$priceFeedItem->setCampaignTargeting(
    new FeedItemCampaignTargeting($campaignId));
$priceFeedItem->setScheduling(new FeedItemScheduling([
    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.
$tableRows = [];
$tableRows[] = self::createPriceTableRow(
    'Scrubs',
    'Body Scrub, Salt Scrub',
    'http://www.example.com/scrubs',
    60 * self::MICROS_PER_DOLLAR,
    'USD',
    PriceExtensionPriceUnit::PER_HOUR,
    'http://m.example.com/scrubs'
);
$tableRows[] = self::createPriceTableRow(
    'Hair Cuts',
    'Once a month',
    'http://www.example.com/haircuts',
    75 * self::MICROS_PER_DOLLAR,
    'USD',
    PriceExtensionPriceUnit::PER_MONTH,
    'http://m.example.com/haircuts'
);
$tableRows[] = self::createPriceTableRow(
    'Skin Care Package',
    'Four times a month',
    'http://www.example.com/skincarepackage',
    250 * self::MICROS_PER_DOLLAR,
    'USD',
    PriceExtensionPriceUnit::PER_MONTH
);

$priceFeedItem->setTableRows($tableRows);

// Create your customer extension settings. This associates the price
// extension to your account.
$customerExtensionSetting = new CustomerExtensionSetting();
$customerExtensionSetting->setExtensionType(FeedType::PRICE);
$customerExtensionSetting->setExtensionSetting(new ExtensionSetting());
$customerExtensionSetting->getExtensionSetting()->setExtensions(
    [$priceFeedItem]);

Perl

# Create the price extension feed item.
my $price_feed_item = Google::Ads::AdWords::v201708::PriceFeedItem->new({
    priceExtensionType => "SERVICES",
    # Price qualifer is optional.
    priceQualifier      => "FROM",
    trackingUrlTemplate => "http://tracker.example.com/?u={lpurl}",
    language            => 'en',
    campaignTargeting =>
      Google::Ads::AdWords::v201708::FeedItemCampaignTargeting->new({
        TargetingCampaignId => $campaign_id
      }
      ),
    scheduling => Google::Ads::AdWords::v201708::FeedItemScheduling->new({
        feedItemSchedules => [
          Google::Ads::AdWords::v201708::FeedItemSchedule->new({
              dayOfWeek   => "SUNDAY",
              startHour   => "10",
              startMinute => "ZERO",
              endHour     => "18",
              endMinute   => "ZERO"
            }
          ),
          Google::Ads::AdWords::v201708::FeedItemSchedule->new({
              dayOfWeek   => "SATURDAY",
              startHour   => "10",
              startMinute => "ZERO",
              endHour     => "22",
              endMinute   => "ZERO"
            })]})});

# To create a price extension, at least three table rows are needed.
my @table_rows = ();
push @table_rows, create_price_table_row(
  "Scrubs",
  "Body Scrub, Salt Scrub",
  "http://www.example.com/scrubs",
  "http://m.example.com/scrubs",
  60000000,    # 60 USD
  "USD",
  "PER_HOUR"
);
push @table_rows, create_price_table_row(
  "Hair Cuts",
  "Once a month",
  "http://www.example.com/haircuts",
  "http://m.example.com/haircuts",
  75000000,    # 75 USD
  "USD",
  "PER_MONTH"
);
push @table_rows, create_price_table_row(
  "Skin Care Package",
  "Four times a month",
  "http://www.example.com/skincarepackage",
  undef,
  250000000,    # 250 USD
  "USD",
  "PER_MONTH"
);
$price_feed_item->set_tableRows(\@table_rows);

# Create your customer extension settings. This associates the price
# extension to your account.
my $customer_extension_setting =
  Google::Ads::AdWords::v201708::CustomerExtensionSetting->new({
    extensionType    => 'PRICE',
    extensionSetting => Google::Ads::AdWords::v201708::ExtensionSetting->new({
        extensions => [$price_feed_item]})});

Ruby

price_feed_item = {
  :xsi_type => 'PriceFeedItem',
  :price_extension_type => 'SERVICES',
  # Price qualifier is optional.
  :price_qualifier => 'FROM',
  :tracking_url_template => 'http://tracker.example.com/?u={lpurl}',
  :language => 'en',
  :campaign_targeting => {
    :targeting_campaign_id => campaign_id
  },
  :scheduling => {
    :feed_item_schedules => [
      {
        :day_of_week => 'SUNDAY',
        :start_hour => 10,
        :start_minute => 'ZERO',
        :end_hour => 18,
        :end_minute => 'ZERO'
      },
      {
        :day_of_week => 'SATURDAY',
        :start_hour => 10,
        :start_minute => 'ZERO',
        :end_hour => 22,
        :end_minute => 'ZERO'
      }
    ]
  },
  # To create a price extension, at least three table rows are needed.
  :table_rows => [
    create_price_table_row(
        'Scrubs',
        'Body Scrub, Salt Scrub',
        'http://www.example.com/scrubs',
        'http://m.example.com/scrubs',
        60000000,
        'USD',
        'PER_HOUR'
    ),
    create_price_table_row(
        'Hair Cuts',
        'Once a month',
        'http://www.example.com/haircuts',
        'http://m.example.com/haircuts',
        75000000,
        'USD',
        'PER_MONTH'
    ),
    create_price_table_row(
        'Skin Care Package',
        'Four times a month',
        'http://www.example.com/skincarepackage',
        nil,
        250000000,
        'USD',
        'PER_MONTH'
    )
  ]
}

customer_extension_setting = {
  :extension_type => 'PRICE',
  :extension_setting => {
    :extensions => [price_feed_item]
  }
}

The reference to createPriceTableRow is defined as shown below:

Java

/**
 * 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;
}

Python

def CreatePriceTableRow(header, description, final_url, price_in_micros,
                        currency_code, price_unit, final_mobile_url=None):
  """Helper function to generate a single row of a price table.

  Args:
    header: A str containing the header text of this row.
    description: A str description of this row in the price table.
    final_url: A str containing the final URL after all cross domain redirects.
    price_in_micros: An int indicating the price of the given currency in
      micros.
    currency_code: A str indicating the currency code being used.
    price_unit: A str enum indicating the price unit for this row.
    final_mobile_url: A str containing the final mobile URL after all cross
      domain redirects.

  Returns:
    A dictionary containing the contents of the generated price table row.
  """
  table_row = {
      'header': header,
      'description': description,
      'finalUrls': {'urls': [final_url]},
      'price': {
          'money': {
              'microAmount': price_in_micros,
          },
          'currencyCode': currency_code
      },
      'priceUnit': price_unit,
      'xsi_type': 'PriceTableRow'
  }

  if final_mobile_url:
    table_row['finalMobileUrls'] = {
        'urls': [final_mobile_url]
    }

  return table_row

PHP

/**
 * Creates a new price table row with the specified attributes.
 *
 * @param string $header the header of price table row
 * @param string $description the description of price table row
 * @param string $finalUrl the final URL of price table row
 * @param integer $priceInMicros the price in micro amount
 * @param string $currencyCode the 3-character currency code
 * @param string $priceUnit the unit of shown price
 * @param string|null $finalMobileUrl the mobile final URL of price table row
 */
private static function createPriceTableRow(
    $header,
    $description,
    $finalUrl,
    $priceInMicros,
    $currencyCode,
    $priceUnit,
    $finalMobileUrl = null
) {
  $priceTableRow = new PriceTableRow();
  $priceTableRow->setHeader($header);
  $priceTableRow->setDescription($description);
  $priceTableRow->setFinalUrls(new UrlList([$finalUrl]));
  $money = new Money();
  $money->setMicroAmount($priceInMicros);
  $moneyWithCurrency = new MoneyWithCurrency();
  $moneyWithCurrency->setMoney($money);
  $moneyWithCurrency->setCurrencyCode($currencyCode);
  $priceTableRow->setPrice($moneyWithCurrency);
  $priceTableRow->setPriceUnit($priceUnit);

  if ($finalMobileUrl !== null) {
    $priceTableRow->setFinalMobileUrls(new UrlList([$finalMobileUrl]));
  }
  return $priceTableRow;
}

Perl

# Creates a new price table row with the specified attributes.
sub create_price_table_row {
  my ($header, $description, $final_url, $final_mobile_url, $price_in_micros,
    $currency_code, $price_unit)
    = @_;
  my $price_table_row = Google::Ads::AdWords::v201708::PriceTableRow->new({
      header      => $header,
      description => $description,
      finalUrls =>
        [Google::Ads::AdWords::v201708::UrlList->new({urls => [$final_url]})],
      price => Google::Ads::AdWords::v201708::MoneyWithCurrency->new({
          money => Google::Ads::AdWords::v201708::Money->new({
              microAmount => $price_in_micros
            }
          ),
          currencyCode => $currency_code
        }
      ),
      priceUnit => $price_unit
    });

  # Optional: set the mobile final URLs.
  if ($final_mobile_url) {
    $price_table_row->set_finalMobileUrls([
        Google::Ads::AdWords::v201708::UrlList->new(
          {urls => [$final_mobile_url]})]);
  }

  return $price_table_row;
}

Ruby

def create_price_table_row(header, description, final_url, final_mobile_url,
    price_in_micros, currency_code, price_unit)
  ret_val = {
    :header => header,
    :description => description,
    :final_urls => {
      :urls => [final_url]
    },
    :price => {
      :money => {
        :micro_amount => price_in_micros
      },
      :currency_code => currency_code
    },
    :price_unit => price_unit
  }

  # Optional: Set the mobile final URLs.
  unless final_mobile_url.nil? or final_mobile_url.empty?
    ret_val[:final_mobile_urls] = {:urls => [final_mobile_url]}
  end
end

Promotion extensions

Promotion extensions allow you to highlight sales and other promotions that let users see how they can save by buying now. There are many fields that let you customize exactly what appears in the extension, but the required ones are:

  • promotionTarget - The text that appears on the ad when the extension is shown.
  • percentOff or moneyAmountOff - One of these is required. They show the user how big the discount is.

    • percentOff takes a value in micros, where 1 million micros represents 1%, and is shown as a percentage when rendered. For example, the value 10000000 will render as "10%".
    • moneyAmountOff is also in micros. It requires both a currency and an amount of money.
  • finalUrls - The URL targets for your landing page if someone clicks on the extension.

Optional fields:

  • occasion - For example, NEW_YEARS or HALLOWEEN.

    • When using occasion without using extension setting services, make sure the values exactly match the documented enum values for PromotionFeedItem.
  • discountModifier - Allows you to add "up to" verbiage to the promotion, in case you have variable promotion rates.

    • When using discountModifier without using extension setting services, make sure the values exactly match the documented enum values for PromotionFeedItem.
  • promotionCode - For your users to enter to get the discount.

  • ordersOverAmount - To indicate a minimum spend before the user qualifies for the promotion.

    • Note that only one promotionCode or ordersOverAmount is allowed per PromotionFeedItem.
  • promotionStart and promotionEnd - Ensures that the extension only shows during the promotion period. These expect a datetime format, though only midnight is allowed. So for example, you can set promotionStart to '20170601 000000' and promotionEnd to '20170701 000000' for a June-only promotion.

    • You can clear these fields by setting the special value: '00000101 000000'.
  • language - Provides a language for your promotion by inputting a language code. This defaults to English.

For details on the rest of the fields, check out the PromotionFeedItem docs.

Here's an example code snippet showing how to create a promotion extension:

Java

PromotionFeedItem promotionFeedItem = new PromotionFeedItem();
promotionFeedItem.setPromotionTarget("Wool Socks");
promotionFeedItem.setPercentOff(10_000_000L);
promotionFeedItem.setPromotionCode("WinterSocksDeal");
UrlList finalUrlsList = new UrlList();
finalUrlsList.setUrls(new String[] {"http://www.example.com/socks"});
promotionFeedItem.setFinalUrls(finalUrlsList);

CampaignExtensionSetting campaignExtensionSetting = new CampaignExtensionSetting();
campaignExtensionSetting.setCampaignId(campaignId);
campaignExtensionSetting.setExtensionType(FeedType.PROMOTION);
ExtensionSetting extensionSetting = new ExtensionSetting();
extensionSetting.setExtensions(new ExtensionFeedItem[] {promotionFeedItem});
campaignExtensionSetting.setExtensionSetting(extensionSetting);

Updating ad extensions

Suppose you decided to change your restaurant's happy hours on Saturday to 5pm to 10pm.

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

Java

CampaignExtensionSettingServiceInterface campaignExtensionSettingService =
    adWordsServices.get(session, CampaignExtensionSettingServiceInterface.class);
Selector selector =
    new SelectorBuilder()
        .fields(
            CampaignExtensionSettingField.CampaignId,
            CampaignExtensionSettingField.ExtensionType,
            CampaignExtensionSettingField.Extensions)
        .equals(CampaignExtensionSettingField.CampaignId, Long.toString(campaignId))
        .equals(CampaignExtensionSettingField.ExtensionType, FeedType.SITELINK.getValue())
        .build();

CampaignExtensionSettingPage page = campaignExtensionSettingService.get(selector);

Next, update the desired sitelink:

Java

CampaignExtensionSetting campaignExtensionSetting = page.getEntries(0);
for (ExtensionFeedItem extensionFeedItem :
    campaignExtensionSetting.getExtensionSetting().getExtensions()) {
  SitelinkFeedItem sitelinkFeedItem = (SitelinkFeedItem) extensionFeedItem;
  if ("Happy hours".equals(sitelinkFeedItem.getSitelinkText())) {
    for (FeedItemSchedule feedItemSchedule :
        sitelinkFeedItem.getScheduling().getFeedItemSchedules()) {
      if (DayOfWeek.FRIDAY.equals(feedItemSchedule.getDayOfWeek())) {
        feedItemSchedule.setStartHour(17);
        feedItemSchedule.setStartMinute(MinuteOfHour.ZERO);
        feedItemSchedule.setEndHour(22);
        feedItemSchedule.setEndMinute(MinuteOfHour.ZERO);
      }
    }
  }
}

Finally, send the modified campaignExtensionSetting to the server.

Java

CampaignExtensionSettingOperation operation = new CampaignExtensionSettingOperation();
operation.setOperator(Operator.SET);
operation.setOperand(campaignExtensionSetting);

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.

Removing ad extensions

Behind the scenes, the extension setting services are still using feeds to control your ad extensions. When you create a new extension setting, the service creates feed items for you in the appropriate feed and associates them with the given campaign, ad group, or account, depending on which service you used.

Similarly, when you remove an ExtensionFeedItem from an extension setting or remove the extension setting altogether, the service removes the association between the feed items and the attached entity, but it does not remove the feed items because they may be associated with another entity.

There are two ways to remove the association between a feed item and an entity:

  1. You can remove the entire extension setting with a REMOVE operator. This will remove the assocation between the entity and all feed items in the extension setting.
  2. You can remove individual ExtensionFeedItems within the extension setting, which you can do by specifying a new set of ExtensionFeedItems that does not contain the one you want to remove.

In both cases, the underlying feed items are not removed. Therefore, if the feed items are no longer associated with any other entities, you should consider deleting the feed items, which will free up space and allow you to generate new extensions in the future. Alternatively, you can reuse the feed items in other extension settings.

If you don't either remove or reuse feed items created from the extension setting services when you remove the extensions, these feed items will accrue over time and eventually you may reach the limit on the number of feed items you can have in your account.

Deleting feed items

If you want to delete the underlying feed items after you disassociate them from an entity, you can just grab the feedId and feedItemId from the extension you delete, and delete the corresponding FeedItem.

Java

CampaignExtensionSettingServiceInterface campaignExtensionSettingService =
    adWordsServices.get(session, CampaignExtensionSettingServiceInterface.class);
CampaignExtensionSettingOperation extensionSettingOperation =
    new CampaignExtensionSettingOperation();
extensionSettingOperation.setOperator(Operator.REMOVE);
extensionSettingOperation.setOperand(campaignExtensionSetting);

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

FeedItemServiceInterface feedItemService =
    adWordsServices.get(session, FeedItemServiceInterface.class);
List<FeedItemOperation> feedItemOperations = new ArrayList<>();
for (ExtensionFeedItem extensionFeedItem :
    campaignExtensionSetting.getExtensionSetting().getExtensions()) {
  FeedItemOperation feedItemOperation = new FeedItemOperation();
  feedItemOperation.setOperator(Operator.REMOVE);

  FeedItem feedItem = new FeedItem();
  feedItem.setFeedId(extensionFeedItem.getFeedId());
  feedItem.setFeedItemId(extensionFeedItem.getFeedItemId());
  feedItemOperation.setOperand(feedItem);
  feedItemOperations.add(feedItemOperation);
}

feedItemService.mutate(
    feedItemOperations.toArray(new FeedItemOperation[feedItemOperations.size()]));

The first REMOVE, on the CampaignExtensionSettingService, removes the association between that extension and the campaign. The second REMOVE, on the FeedItemService, removes the underlying feed items, freeing up space so that you can use new extensions in the future.

Reusing feed items

You can associate the same feed item with multiple entities. For example, if you want the same sitelinks to appear on multiple campaigns, you can associate each campaign with the same set of feed items.

Similarly, after removing an ad extension from one entity, you can associate the underlying feed item with a different entity by capturing the feedItemId from the response.

For example, if you have the old feedItemId from a previously removed extension setting stored in a variable storedFeedItemId, you can reuse it by populating only the feedItemId on the SitelinkFeedItem:

Java

SitelinkFeedItem sitelinkFeedItem = new SitelinkFeedItem();
sitelinkFeedItem.setFeedItemId(storedFeedItemId);

CampaignExtensionSetting campaignExtensionSetting = new CampaignExtensionSetting();
campaignExtensionSetting.setCampaignId(campaignId);
campaignExtensionSetting.setExtensionType(FeedType.SITELINK);
ExtensionSetting extensionSetting = new ExtensionSetting();
extensionSetting.setExtensions(new ExtensionFeedItem[] {sitelinkFeedItem});
campaignExtensionSetting.setExtensionSetting(extensionSetting);

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:

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:

Java

FeedServiceInterface feedService = adWordsServices.get(session, FeedServiceInterface.class);
String query = "SELECT Id, Name, Attributes WHERE Origin = 'USER' AND FeedStatus = 'ENABLED'";

List<Feed> feeds = new ArrayList<>();
int offset = 0;
FeedPage feedPage;

do {
  String pageQuery = String.format(query + " LIMIT %d, %d", offset, PAGE_SIZE);
  feedPage = feedService.query(pageQuery);
  if (feedPage.getEntries() != null) {
    feeds.addAll(Arrays.asList(feedPage.getEntries()));
  }
  offset += PAGE_SIZE;
} while (offset < feedPage.getTotalNumEntries());

return feeds;

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

Java

// Get the FeedItemService.
FeedItemServiceInterface feedItemService =
    adWordsServices.get(session, FeedItemServiceInterface.class);
String query = String.format(
    "SELECT FeedItemId, AttributeValues, Scheduling WHERE Status = 'ENABLED' AND FeedId = %d",
    feed.getId());

List<FeedItem> feedItems = new ArrayList<>();
int offset = 0;
FeedItemPage feedItemPage;

do {
  String pageQuery = String.format(query + " LIMIT %d, %d", offset, PAGE_SIZE);
  feedItemPage = feedItemService.query(pageQuery);
  if (feedItemPage.getEntries() != null) {
    feedItems.addAll(Arrays.asList(feedItemPage.getEntries()));
  }
  offset += PAGE_SIZE;
} while (offset < feedItemPage.getTotalNumEntries());

return feedItems;

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

Java

private static class SiteLinkFromFeed {
  private String text;
  private String url;
  private String[] finalUrls;
  private String[] finalMobileUrls;
  private String trackingUrlTemplate;
  private String line2;
  private String line3;
  private FeedItemScheduling scheduling;
}

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

Java

// See the Placeholder reference page for a list of all the placeholder types and fields.
// https://developers.google.com/adwords/api/docs/appendix/placeholders
private static final int PLACEHOLDER_SITELINKS = 1;

// See the Placeholder reference page for a list of all the placeholder types and fields.
// https://developers.google.com/adwords/api/docs/appendix/placeholders
private static final int PLACEHOLDER_FIELD_SITELINK_LINK_TEXT = 1;
private static final int PLACEHOLDER_FIELD_SITELINK_URL = 2;
private static final int PLACEHOLDER_FIELD_LINE_2_TEXT = 3;
private static final int PLACEHOLDER_FIELD_LINE_3_TEXT = 4;
private static final int PLACEHOLDER_FIELD_FINAL_URLS = 5;
private static final int PLACEHOLDER_FIELD_FINAL_MOBILE_URLS = 6;
private static final int PLACEHOLDER_FIELD_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.

You can retrieve the FeedMapping as follows:

Java

private static Multimap<Long, Integer> getFeedMapping(AdWordsServicesInterface adWordsServices,
    AdWordsSession session, Feed feed, long placeholderType) throws Exception {
  // Get the FeedMappingService.
  FeedMappingServiceInterface feedMappingService =
      adWordsServices.get(session, FeedMappingServiceInterface.class);
  String query = String.format(
      "SELECT FeedMappingId, AttributeFieldMappings WHERE FeedId = %d and PlaceholderType = %d "
      + "AND Status = 'ENABLED'", feed.getId(), placeholderType);

  Multimap<Long, Integer> attributeMappings = HashMultimap.create();
  int offset = 0;
  FeedMappingPage feedMappingPage;

  do {
    String pageQuery = String.format(query + " LIMIT %d, %d", offset, PAGE_SIZE);
    feedMappingPage = feedMappingService.query(pageQuery);
    if (feedMappingPage.getEntries() != null) {
      // Normally, a feed attribute is mapped only to one field. However, you may map it to more
      // than one field if needed.
      for (FeedMapping feedMapping : feedMappingPage.getEntries()) {
        for (AttributeFieldMapping attributeMapping : feedMapping.getAttributeFieldMappings()) {
          attributeMappings.put(attributeMapping.getFeedAttributeId(),
              attributeMapping.getFieldId());
        }
      }
    }
    offset += PAGE_SIZE;
  } while (offset < feedMappingPage.getTotalNumEntries());

  return attributeMappings;
}

Now retrieve all of the FeedItems for the Feed and use the information from the FeedMapping to populate the properties of helper objects:

Java

private static Map<Long, SiteLinkFromFeed> getSiteLinksFromFeed(
    AdWordsServicesInterface adWordsServices, AdWordsSession session, Feed feed)
    throws Exception {
  // Retrieve the feed's attribute mapping.
  Multimap<Long, Integer> feedMappings =
      getFeedMapping(adWordsServices, session, feed, PLACEHOLDER_SITELINKS);

  Map<Long, SiteLinkFromFeed> feedItems = Maps.newHashMap();

  for (FeedItem feedItem : getFeedItems(adWordsServices, session, feed)) {
    SiteLinkFromFeed siteLinkFromFeed = new SiteLinkFromFeed();

    for (FeedItemAttributeValue attributeValue : feedItem.getAttributeValues()) {
      // Skip this attribute if it hasn't been mapped to a field.
      if (!feedMappings.containsKey(attributeValue.getFeedAttributeId())) {
        continue;
      }

      for (Integer fieldId : feedMappings.get(attributeValue.getFeedAttributeId())) {
        switch (fieldId) {
          case PLACEHOLDER_FIELD_SITELINK_LINK_TEXT:
            siteLinkFromFeed.text = attributeValue.getStringValue();
            break;
          case PLACEHOLDER_FIELD_SITELINK_URL:
            siteLinkFromFeed.url = attributeValue.getStringValue();
            break;
          case PLACEHOLDER_FIELD_FINAL_URLS:
            siteLinkFromFeed.finalUrls = attributeValue.getStringValues();
            break;
          case PLACEHOLDER_FIELD_FINAL_MOBILE_URLS:
            siteLinkFromFeed.finalMobileUrls = attributeValue.getStringValues();
            break;
          case PLACEHOLDER_FIELD_TRACKING_URL_TEMPLATE:
            siteLinkFromFeed.trackingUrlTemplate = attributeValue.getStringValue();
            break;
          case PLACEHOLDER_FIELD_LINE_2_TEXT:
            siteLinkFromFeed.line2 = attributeValue.getStringValue();
            break;
          case PLACEHOLDER_FIELD_LINE_3_TEXT:
            siteLinkFromFeed.line3 = attributeValue.getStringValue();
            break;
          default:
            // Ignore attributes that do not map to a predefined placeholder field.
            break;
        }
      }
    }
    siteLinkFromFeed.scheduling = feedItem.getScheduling();

    feedItems.put(feedItem.getFeedItemId(), siteLinkFromFeed);
  }

  return feedItems;
}

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…)

Java

/**
 * Returns the list of feed item IDs that are used by a campaign through a given campaign feed.
 */
private static Set<Long> getFeedItemIdsForCampaign(CampaignFeed campaignFeed) throws Exception {
  Set<Long> feedItemIds = Sets.newHashSet();

  FunctionOperator functionOperator = campaignFeed.getMatchingFunction().getOperator();

  if (FunctionOperator.IN.equals(functionOperator)) {
    // Check if matchingFunction is of the form IN(FEED_ITEM_ID,{xxx,xxx}).
    // Extract feed items if applicable.
    feedItemIds.addAll(getFeedItemIdsFromArgument(campaignFeed.getMatchingFunction()));
  } else if (FunctionOperator.AND.equals(functionOperator)) {
    for (FunctionArgumentOperand argument : campaignFeed.getMatchingFunction().getLhsOperand()) {
      // Check if matchingFunction is of the form IN(FEED_ITEM_ID,{xxx,xxx}).
      // Extract feed items if applicable.
      if (argument instanceof FunctionOperand) {
        FunctionOperand operand = (FunctionOperand) argument;
        if (FunctionOperator.IN.equals(operand.getValue().getOperator())) {
          feedItemIds.addAll(getFeedItemIdsFromArgument(operand.getValue()));
        }
      }
    }
  } else {
    // There are no other matching functions involving feed item IDs.
  }

  return feedItemIds;
}

/**
 * Gets the set of feed item IDs from the function if it is of the form:
 * <code>IN(FEED_ITEM_ID,{xxx,xxx})</code>. Otherwise, returns an empty set.
 */
private static Set<Long> getFeedItemIdsFromArgument(Function function) {
  Set<Long> feedItemIds = Sets.newHashSet();

  if (function.getLhsOperand().length == 1
      && function.getLhsOperand(0) instanceof RequestContextOperand) {
    RequestContextOperand requestContextOperand =
        (RequestContextOperand) function.getLhsOperand(0);
    if (RequestContextOperandContextType.FEED_ITEM_ID.equals(
        requestContextOperand.getContextType())
        && FunctionOperator.IN.equals(function.getOperator())) {
      for (FunctionArgumentOperand argument : function.getRhsOperand()) {
        if (argument instanceof ConstantOperand) {
          feedItemIds.add(((ConstantOperand) argument).getLongValue());
        }
      }
    }
  }

  return feedItemIds;
}

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.

Java

private static CampaignFeed deleteCampaignFeed(AdWordsServicesInterface adWordsServices,
    AdWordsSession session, CampaignFeed campaignFeed) throws Exception {
  // Get the CampaignFeedService.
  CampaignFeedServiceInterface campaignFeedService =
      adWordsServices.get(session, CampaignFeedServiceInterface.class);

  CampaignFeedOperation operation = new CampaignFeedOperation();
  operation.setOperand(campaignFeed);
  operation.setOperator(Operator.REMOVE);

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

Add extension settings

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

Java

private static void createExtensionSetting(AdWordsServicesInterface adWordsServices,
    AdWordsSession session,
    Map<Long, SiteLinkFromFeed> feedItems,
    CampaignFeed campaignFeed,
    Set<Long> feedItemIds,
    ExtensionSettingPlatform platformRestrictions) throws Exception {
  // Get the CampaignExtensionSettingService.
  CampaignExtensionSettingServiceInterface campaignExtensionSettingService =
      adWordsServices.get(session, CampaignExtensionSettingServiceInterface.class);

  CampaignExtensionSetting campaignExtensionSetting = new CampaignExtensionSetting();
  campaignExtensionSetting.setCampaignId(campaignFeed.getCampaignId());
  campaignExtensionSetting.setExtensionType(FeedType.SITELINK);

  ExtensionSetting extensionSetting = new ExtensionSetting();

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

  for (Long feedItemId : feedItemIds) {
    SiteLinkFromFeed siteLinkFromFeed = feedItems.get(feedItemId);
    SitelinkFeedItem siteLinkFeedItem = new SitelinkFeedItem();
    siteLinkFeedItem.setSitelinkText(siteLinkFromFeed.text);

    if (siteLinkFromFeed.finalUrls != null && siteLinkFromFeed.finalUrls.length > 0) {
      siteLinkFeedItem.setSitelinkFinalUrls(new UrlList(siteLinkFromFeed.finalUrls));
      if (siteLinkFromFeed.finalMobileUrls != null
          && siteLinkFromFeed.finalMobileUrls.length > 0) {
        siteLinkFeedItem.setSitelinkFinalMobileUrls(
            new UrlList(siteLinkFromFeed.finalMobileUrls));
      }
      siteLinkFeedItem.setSitelinkTrackingUrlTemplate(siteLinkFromFeed.trackingUrlTemplate);
    } else {
      siteLinkFeedItem.setSitelinkUrl(siteLinkFromFeed.url);
    }

    siteLinkFeedItem.setSitelinkLine2(siteLinkFromFeed.line2);
    siteLinkFeedItem.setSitelinkLine3(siteLinkFromFeed.line3);
    siteLinkFeedItem.setScheduling(siteLinkFromFeed.scheduling);

    extensionFeedItems.add(siteLinkFeedItem);
  }

  extensionSetting.setExtensions(
      extensionFeedItems.toArray(new ExtensionFeedItem[extensionFeedItems.size()]));

  extensionSetting.setPlatformRestrictions(platformRestrictions);

  campaignExtensionSetting.setExtensionSetting(extensionSetting);

  CampaignExtensionSettingOperation operation = new CampaignExtensionSettingOperation();
  operation.setOperand(campaignExtensionSetting);
  operation.setOperator(Operator.ADD);

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

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:

Java

private static void deleteOldFeedItems(
    AdWordsServicesInterface adWordsServices,
    AdWordsSession session,
    Set<Long> feedItemIds,
    Feed feed)
    throws Exception {
  // Get the FeedItemService.
  FeedItemServiceInterface feedItemService =
      adWordsServices.get(session, FeedItemServiceInterface.class);

  if (feedItemIds.isEmpty()) {
    return;
  }

  List<FeedItemOperation> operations = new ArrayList<>();
  for (Long feedItemId : feedItemIds) {
    FeedItemOperation operation = new FeedItemOperation();

    FeedItem feedItem = new FeedItem();
    feedItem.setFeedId(feed.getId());
    feedItem.setFeedItemId(feedItemId);

    operation.setOperand(feedItem);
    operation.setOperator(Operator.REMOVE);

    operations.add(operation);
  }

  feedItemService.mutate(operations.toArray(new FeedItemOperation[operations.size()]));
}

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:

Java

// Get all of the feeds for the session's account.
List<Feed> feeds = getFeeds(adWordsServices, session);

for (Feed feed : feeds) {
  // Retrieve all the sitelinks from the current feed.
  Map<Long, SiteLinkFromFeed> feedItems = getSiteLinksFromFeed(adWordsServices, session, feed);

  // Get all the instances where a sitelink from this feed has been added to a campaign.
  List<CampaignFeed> campaignFeeds =
      getCampaignFeeds(adWordsServices, session, feed, PLACEHOLDER_SITELINKS);

  Set<Long> allFeedItemsToDelete = Sets.newHashSet();
  for (CampaignFeed campaignFeed : campaignFeeds) {
    // Retrieve the sitelinks that have been associated with this campaign.
    Set<Long> feedItemIds = getFeedItemIdsForCampaign(campaignFeed);

    ExtensionSettingPlatform platformRestrictions =
        getPlatformRestictionsForCampaign(campaignFeed);

    if (feedItemIds.isEmpty()) {
      System.out.printf("Migration skipped for campaign feed with campaign ID %d "
          + "and feed ID %d because no mapped feed item IDs were found in the "
          + "campaign feed's matching function.%n", campaignFeed.getCampaignId(),
          campaignFeed.getFeedId());
    } else {
      // Delete the campaign feed that associates the sitelinks from the feed to the campaign.
      deleteCampaignFeed(adWordsServices, session, campaignFeed);

      // Create extension settings instead of sitelinks.
      createExtensionSetting(adWordsServices,
          session,
          feedItems,
          campaignFeed,
          feedItemIds,
          platformRestrictions);

      // Mark the sitelinks from the feed for deletion.
      allFeedItemsToDelete.addAll(feedItemIds);
    }
  }

  // Delete all the sitelinks from the feed.
  deleteOldFeedItems(adWordsServices, session, allFeedItemsToDelete, feed);
}

Code examples

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

Enviar comentários sobre…

Precisa de ajuda? Acesse nossa página de suporte.