Migrating Feed-based Extensions

This document describes the migration and sunset process for Feed-based extensions. Feed-based extensions will be migrated to new Asset-based extensions in two batches, and will fully sunset in early 2022.

Overview

The existing Feed-based extensions paradigm will be deprecated in favor of Asset-based extensions. Asset-based extensions reduce the complexity of creating and managing extensions. Action is required in order to ensure that you maintain control over your ad extensions.

The term "Feed-based extension" refers to any of the resources or functions associated with the following services:

While Feed-based extensions and Asset-based extensions will coexist for a time, we strongly recommend using Asset-based extensions as soon as is feasible.

If you do have active extensions of both types, you may see a notification on the Extensions page in the Google Ads UI, similar to the screenshot below. "Legacy" extensions are Feed-based extensions, while "Upgraded" extensions are Asset-based.

Note that once you have created any Asset-based extensions, the view will default to "Upgraded" extensions.

Migration Details

Feeds are "migrated" by copying their existing values into a new Asset-based extension instance. Existing Feed-based extensions will be automatically copied into Asset-based extensions on specific dates. We recommend performing the migration process yourself through the Google Ads API using the steps in this guide.

All Feed-based extensions will be unlinked from any customer, campaign, or ad group after the automatic migration and will no longer serve.

Migration Schedule

All existing Feed-based extensions will automatically migrate to Asset-based extensions in two batches:

Extension Types Auto Migration Date Sunset Date
Callout*
Promotion
Sitelink*
Structured Snippet*
October 20, 2021 February 15, 2022
App
Call
Hotel Callout
Image
Price
February 15, 2022 April 8, 2022

*Sitelink, Callout, and Structured Snippet are currently available to test accounts only.

All CREATE and MUTATE actions will be disabled for the relevant extension types after each auto migration date. You can opt-out specific account IDs from the first batch automatic migration (Callout, Promotion, Sitelink, and Structured Snippet). However, note that these extensions will become unavailable for all accounts on February 15, 2022. Instructions for opting out will be provided at a later date.

Reporting for Feed-based extensions will be available until August 2022.

The automatic migration process will create Asset-based extensions equivalent to the current Feed-based extensions and replace all occurrences of AdGroupFeed, CampaignFeed, and CustomerFeed with equivalent AdGroupAsset, CampaignAsset, and CustomerAsset. If the ad group, campaign, or customer has already been attached to an Asset, the corresponding Feed will be ignored.

Changes in Asset-based Extensions

While Asset-based extensions provide the same functionality as the existing Feed-based extensions, some existing features have been removed, including Matching Functions and most targeting fields.

For targeting, the ad group, campaign, keyword, location, and mobile fields will be unavailable to Asset-based extensions. Existing ad group, campaign, and mobile targeting will be honored during the automatic migration. Start time, end time, and ad schedules will be unavailable for the Asset-based Structured Snippet extension, and will not be automatically migrated.

For Asset-based extensions going forward, we recommend using targeting features already available at the campaign and ad group level. For mobile preference, consider including mobile-friendly URLs for any links.

Matching Functions will not be available to Asset-based extensions. During the automatic migration, only the following Matching Functions will be honored:

Matching Function Migration Notes
EQUALS(FEED_ITEM_ID, 1234567) Migrated as-is
IN(FEED_ITEM_ID,{1234567,1234568,1234569}) Migrated as-is
IDENTITY(false) Migrated as excluded_parent_asset_field_types
IDENTITY(true) Migrated with all linked feed items
AND(
IN(FEED_ITEM_ID,{1234567,1234568,1234569}),
EQUALS(CONTEXT.DEVICE,"Mobile"))
Migrated as IN(FEED_ITEM_ID,{1234567,1234568,1234569})
AND(
EQUALS(FEED_ITEM_ID, 1234567),
EQUALS(CONTEXT.DEVICE,"Mobile"))
Migrated as EQUALS(FEED_ITEM_ID,1234567)

Migration Procedure

To migrate a Feed-based extension to an Asset-based extension, take the following actions:

  1. Identify an existing Feed-based extension that you intend to continue to use.
  2. Copy the Feed-based extension's contents into a new instance of the corresponding Asset-based extension. For example, the contents of an ExtensionFeedItem of type Promotion would be copied into a new PromotionAsset object.
  3. Associate the new Asset-based extension with the same Ad Groups and Campaigns.
  4. Verify that the Asset-based extension is correctly configured.
  5. Remove the Feed-based extension from your account.

An extension type is fully migrated when all feed items with a particular PlaceholderType have been removed. See removing extensions for guidance.

The following example explains each step of the above process with a Promotion extension.

Identify Feed-based extensions

Use the following Google Ads Query Language query to request active Extension Feed Item IDs of a particular extension type.

SELECT
  extension_feed_item.id
FROM extension_feed_item
WHERE
  extension_feed_item.status = 'ENABLED'
  AND extension_feed_item.extension_type = 'PROMOTION'

The IDs returned can then be used to fetch extension details.

Fetch a Feed-based Extension Details

Once you have identified a Feed-based extension to migrate, request its full details using its ID using another Google Ads Query Language query.

SELECT
    extension_feed_item.id,
    extension_feed_item.ad_schedules,
    extension_feed_item.device,
    extension_feed_item.status,
    extension_feed_item.start_date_time,
    extension_feed_item.end_date_time,
    extension_feed_item.targeted_campaign,
    extension_feed_item.targeted_ad_group,
    extension_feed_item.promotion_feed_item.discount_modifier,
    extension_feed_item.promotion_feed_item.final_mobile_urls,
    extension_feed_item.promotion_feed_item.final_url_suffix,
    extension_feed_item.promotion_feed_item.final_urls,
    extension_feed_item.promotion_feed_item.language_code,
    extension_feed_item.promotion_feed_item.money_amount_off.amount_micros,
    extension_feed_item.promotion_feed_item.money_amount_off.currency_code,
    extension_feed_item.promotion_feed_item.occasion,
    extension_feed_item.promotion_feed_item.orders_over_amount.amount_micros,
    extension_feed_item.promotion_feed_item.orders_over_amount.currency_code,
    extension_feed_item.promotion_feed_item.percent_off,
    extension_feed_item.promotion_feed_item.promotion_code,
    extension_feed_item.promotion_feed_item.promotion_end_date,
    extension_feed_item.promotion_feed_item.promotion_start_date,
    extension_feed_item.promotion_feed_item.promotion_target,
    extension_feed_item.promotion_feed_item.tracking_url_template
FROM extension_feed_item
WHERE
    extension_feed_item.extension_type = 'PROMOTION'
    AND extension_feed_item.id = 123456789012
LIMIT 1";

The returned ExtensionFeedItem will have all set values currently contained in the specified Feed-based extension that can be applied to an Asset-based extension.

If using URL Custom Parameters, you will need to fetch these using an additional Google Ads Query Language query. Add any results to the ExtensionFeedItem instance retrieved by the previous query. Note that the Feed Item ID in this query is the same as the Extension Feed Item ID used in the previous query.

SELECT feed_item.url_custom_parameters
FROM feed_item
WHERE feed_item.id = 123456789012";

Create an Asset-based Extension

The values fetched in the previous step can then be applied to a new instance of the relevant Asset.

C#

private string CreatePromotionAssetFromFeed(GoogleAdsClient client, long customerId,
    ExtensionFeedItem extensionFeedItem)
{
    // Get the Asset Service client.
    AssetServiceClient assetServiceClient = client.GetService(Services.V7.AssetService);

    PromotionFeedItem promotionFeedItem = extensionFeedItem.PromotionFeedItem;

    // Create the Promotion asset.
    Asset asset = new Asset
    {
        // Name field is optional.
        Name = $"Migrated from feed item #{extensionFeedItem.Id}",
        PromotionAsset = new PromotionAsset
        {
            PromotionTarget = promotionFeedItem.PromotionTarget,
            DiscountModifier = promotionFeedItem.DiscountModifier,
            RedemptionStartDate = promotionFeedItem.PromotionStartDate,
            RedemptionEndDate = promotionFeedItem.PromotionEndDate,
            Occasion = promotionFeedItem.Occasion,
            LanguageCode = promotionFeedItem.LanguageCode,
        },
        TrackingUrlTemplate = promotionFeedItem.TrackingUrlTemplate,
        FinalUrlSuffix = promotionFeedItem.FinalUrlSuffix
    };

    // Either PercentOff or MoneyAmountOff must be set.
    if (promotionFeedItem.PercentOff > 0)
    {
        // Adjust the percent off scale when copying.
        asset.PromotionAsset.PercentOff = promotionFeedItem.PercentOff / 100;
    }
    else
    {
        asset.PromotionAsset.MoneyAmountOff = new Money
        {
            AmountMicros = promotionFeedItem.MoneyAmountOff.AmountMicros,
            CurrencyCode = promotionFeedItem.MoneyAmountOff.CurrencyCode
        };
    }

    // Either PromotionCode or OrdersOverAmount must be set.
    if (!string.IsNullOrEmpty(promotionFeedItem.PromotionCode))
    {
        asset.PromotionAsset.PromotionCode = promotionFeedItem.PromotionCode;
    }
    else
    {
        asset.PromotionAsset.OrdersOverAmount = new Money
        {
            AmountMicros = promotionFeedItem.OrdersOverAmount.AmountMicros,
            CurrencyCode = promotionFeedItem.OrdersOverAmount.CurrencyCode
        };
    }

    // Set the start and end dates if set in the existing extension.
    if (extensionFeedItem.HasStartDateTime)
    {
        asset.PromotionAsset.StartDate = DateTime.Parse(extensionFeedItem.StartDateTime)
            .ToString("yyyy-MM-dd");
    }

    if (extensionFeedItem.HasEndDateTime)
    {
        asset.PromotionAsset.EndDate = DateTime.Parse(extensionFeedItem.EndDateTime)
            .ToString("yyyy-MM-dd");
    }

    asset.PromotionAsset.AdScheduleTargets.Add(extensionFeedItem.AdSchedules);
    asset.FinalUrls.Add(promotionFeedItem.FinalUrls);
    asset.FinalMobileUrls.Add(promotionFeedItem.FinalMobileUrls);
    asset.UrlCustomParameters.Add(promotionFeedItem.UrlCustomParameters);

    // Build an operation to create the Promotion asset.
    AssetOperation operation = new AssetOperation
    {
        Create = asset
    };

    // Issue the request and return the resource name of the new Promotion asset.
    MutateAssetsResponse response = assetServiceClient.MutateAssets(
        customerId.ToString(), new[] {operation});
    Console.WriteLine("Created Promotion asset with resource name " +
        $"{response.Results.First().ResourceName}");
    return response.Results.First().ResourceName;
}

Associate the Asset with Campaigns and Ad Groups

The new Asset can now be associated with the same Customers, Campaigns, and Ad Groups as the original Feed-based Extension. First, fetch the resource IDs that are currently associated with the extension setting.

C#

private List<long> GetTargetedCampaignIds(GoogleAdsServiceClient googleAdsServiceClient,
    long customerId, string extensionFeedResourceName)
{
    List<long> campaignIds = new List<long>();

    string query = @"
        SELECT campaign.id, campaign_extension_setting.extension_feed_items
        FROM campaign_extension_setting
        WHERE campaign_extension_setting.extension_type = 'PROMOTION'
          AND campaign.status != 'REMOVED'";

    googleAdsServiceClient.SearchStream(customerId.ToString(), query,
        delegate(SearchGoogleAdsStreamResponse response)
        {
            foreach (GoogleAdsRow googleAdsRow in response.Results)
            {
                // Add the campaign ID to the list of IDs if the extension feed item is
                // associated with this extension setting.
                if (googleAdsRow.CampaignExtensionSetting.ExtensionFeedItems.Contains(
                    extensionFeedResourceName))
                {
                    Console.WriteLine(
                        $"Found matching campaign with ID {googleAdsRow.Campaign.Id}.");
                    campaignIds.Add(googleAdsRow.Campaign.Id);
                }
            }
        }
    );

    return campaignIds;
}

Then, create and upload a new CampaignAsset, AdGroupAsset, or CustomerAsset to link the Asset with each resource.

C#

private void AssociateAssetWithCampaigns(GoogleAdsClient client, long customerId,
    string promotionAssetResourceName, List<long> campaignIds)
{
    if (campaignIds.Count == 0)
    {
        Console.WriteLine("Asset was not associated with any campaigns.");
        return;
    }

    CampaignAssetServiceClient campaignAssetServiceClient = client.GetService(Services.V7
        .CampaignAssetService);

    List<CampaignAssetOperation> operations = new List<CampaignAssetOperation>();

    foreach (long campaignId in campaignIds)
    {
        operations.Add(new CampaignAssetOperation
        {
            Create = new CampaignAsset
            {
                Asset = promotionAssetResourceName,
                FieldType = AssetFieldTypeEnum.Types.AssetFieldType.Promotion,
                Campaign = ResourceNames.Campaign(customerId, campaignId),
            }
        });
    }

    MutateCampaignAssetsResponse response = campaignAssetServiceClient.MutateCampaignAssets(
        customerId.ToString(), operations);

    foreach (MutateCampaignAssetResult result in response.Results)
    {
        Console.WriteLine($"Created campaign asset with resource name " +
            $"{result.ResourceName}.");
    }
}

Verify the Asset Contents

Use the following Google Ads Query Language query to fetch the contents of a Promotion Asset. Inspect the results to confirm that all relevant fields have been copied correctly.

SELECT
  asset.id,
  asset.name,
  asset.type,
  asset.final_urls,
  asset.final_mobile_urls,
  asset.final_url_suffix,
  asset.tracking_url_template,
  asset.promotion_asset.promotion_target,
  asset.promotion_asset.discount_modifier,
  asset.promotion_asset.redemption_start_date,
  asset.promotion_asset.redemption_end_date,
  asset.promotion_asset.occasion,
  asset.promotion_asset.language_code,
  asset.promotion_asset.percent_off,
  asset.promotion_asset.money_amount_off.amount_micros,
  asset.promotion_asset.money_amount_off.currency_code,
  asset.promotion_asset.promotion_code,
  asset.promotion_asset.orders_over_amount.amount_micros,
  asset.promotion_asset.orders_over_amount.currency_code,
  asset.promotion_asset.start_date,
  asset.promotion_asset.end_date,
  asset.promotion_asset.ad_schedule_targets
FROM asset
WHERE asset.resource_name = 'customers/123456789/assets/123456789012'

Remove the Feed-based Extension

Once you have verified that the Asset-based extension accurately reflects the original Feeds-based extension, you should remove the Feed-based extension.

Feeds Reports Availability

Historical feeds reports will continue to be available until August 2022. Feed-based extensions will no longer generate new data after migration.