Add Ad Customizer

Java
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.ads.googleads.examples.advancedoperations;

import com.beust.jcommander.Parameter;
import com.google.ads.googleads.examples.utils.ArgumentNames;
import com.google.ads.googleads.examples.utils.CodeSampleParams;
import com.google.ads.googleads.lib.GoogleAdsClient;
import com.google.ads.googleads.v3.common.ExpandedTextAdInfo;
import com.google.ads.googleads.v3.enums.AdCustomizerPlaceholderFieldEnum.AdCustomizerPlaceholderField;
import com.google.ads.googleads.v3.enums.FeedAttributeTypeEnum.FeedAttributeType;
import com.google.ads.googleads.v3.enums.PlaceholderTypeEnum.PlaceholderType;
import com.google.ads.googleads.v3.errors.GoogleAdsError;
import com.google.ads.googleads.v3.errors.GoogleAdsException;
import com.google.ads.googleads.v3.resources.Ad;
import com.google.ads.googleads.v3.resources.AdGroupAd;
import com.google.ads.googleads.v3.resources.AttributeFieldMapping;
import com.google.ads.googleads.v3.resources.Feed;
import com.google.ads.googleads.v3.resources.FeedAttribute;
import com.google.ads.googleads.v3.resources.FeedItem;
import com.google.ads.googleads.v3.resources.FeedItemAttributeValue;
import com.google.ads.googleads.v3.resources.FeedItemTarget;
import com.google.ads.googleads.v3.resources.FeedMapping;
import com.google.ads.googleads.v3.services.AdGroupAdOperation;
import com.google.ads.googleads.v3.services.AdGroupAdServiceClient;
import com.google.ads.googleads.v3.services.FeedItemOperation;
import com.google.ads.googleads.v3.services.FeedItemServiceClient;
import com.google.ads.googleads.v3.services.FeedItemTargetOperation;
import com.google.ads.googleads.v3.services.FeedItemTargetServiceClient;
import com.google.ads.googleads.v3.services.FeedMappingOperation;
import com.google.ads.googleads.v3.services.FeedMappingServiceClient;
import com.google.ads.googleads.v3.services.FeedOperation;
import com.google.ads.googleads.v3.services.FeedServiceClient;
import com.google.ads.googleads.v3.services.GoogleAdsServiceClient;
import com.google.ads.googleads.v3.services.GoogleAdsServiceClient.SearchPagedResponse;
import com.google.ads.googleads.v3.services.MutateAdGroupAdResult;
import com.google.ads.googleads.v3.services.MutateAdGroupAdsResponse;
import com.google.ads.googleads.v3.services.MutateFeedItemResult;
import com.google.ads.googleads.v3.services.MutateFeedItemTargetsResponse;
import com.google.ads.googleads.v3.services.MutateFeedItemsResponse;
import com.google.ads.googleads.v3.services.MutateFeedMappingsResponse;
import com.google.ads.googleads.v3.services.MutateFeedsResponse;
import com.google.ads.googleads.v3.services.SearchGoogleAdsRequest;
import com.google.ads.googleads.v3.utils.ResourceNames;
import com.google.common.collect.ImmutableList;
import com.google.protobuf.StringValue;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.joda.time.DateTime;

/**
 * Adds an ad customizer feed and associates it with the customer. Then it adds an ad that uses the
 * feed to populate dynamic data.
 */
public class AddAdCustomizer {

  // We're doing only searches by resource_name in this example, we can set page size = 1.
  private static final int PAGE_SIZE = 1;

  // We're creating two different ad groups to be dynamically populated by the same feed.
  private static final int NUMBER_OF_AD_GROUPS = 2;

  private static class AddAdCustomizerParams extends CodeSampleParams {

    @Parameter(names = ArgumentNames.CUSTOMER_ID, required = true)
    private Long customerId;

    @Parameter(names = ArgumentNames.AD_GROUP_ID, required = true)
    private List<Long> adGroupIds;
  }

  public static void main(String[] args) throws IOException {
    AddAdCustomizerParams params = new AddAdCustomizerParams();
    if (!params.parseArguments(args)) {

      // Either pass the required parameters for this example on the command line, or insert them
      // into the code here. See the parameter class definition above for descriptions.
      params.customerId = Long.parseLong("INSERT_CUSTOMER_ID_HERE");
      params.adGroupIds =
          Arrays.asList(
              Long.parseLong("INSERT_AD_GROUP_ID_HERE"), Long.parseLong("INSERT_AD_GROUP_ID_HERE"));
    }

    GoogleAdsClient googleAdsClient;
    try {
      googleAdsClient = GoogleAdsClient.newBuilder().fromPropertiesFile().build();
    } catch (FileNotFoundException fnfe) {
      System.err.printf(
          "Failed to load GoogleAdsClient configuration from file. Exception: %s%n", fnfe);
      return;
    } catch (IOException ioe) {
      System.err.printf("Failed to create GoogleAdsClient. Exception: %s%n", ioe);
      return;
    }

    try {
      new AddAdCustomizer().runExample(googleAdsClient, params);
    } catch (GoogleAdsException gae) {
      // GoogleAdsException is the base class for most exceptions thrown by an API request.
      // Instances of this exception have a message and a GoogleAdsFailure that contains a
      // collection of GoogleAdsErrors that indicate the underlying causes of the
      // GoogleAdsException.
      System.err.printf(
          "Request ID %s failed due to GoogleAdsException. Underlying errors:%n",
          gae.getRequestId());
      int i = 0;
      for (GoogleAdsError googleAdsError : gae.getGoogleAdsFailure().getErrorsList()) {
        System.err.printf("  Error %d: %s%n", i++, googleAdsError);
      }
    }
  }

  /**
   * Runs the example.
   *
   * @param googleAdsClient the Google Ads API client.
   * @param params the example parameters.
   * @throws GoogleAdsException if an API request failed with one or more service errors.
   */
  private void runExample(GoogleAdsClient googleAdsClient, AddAdCustomizerParams params) {

    if (params.adGroupIds.size() != NUMBER_OF_AD_GROUPS) {
      throw new IllegalArgumentException(
          "Please pass exactly two ad group IDs in the adGroupId parameter.");
    }

    String feedName = "Ad Customizer example feed " + System.currentTimeMillis();

    // Create a feed to be used as the ad customizer.
    String adCustomizerFeedResourceName =
        createAdCustomizerFeed(googleAdsClient, params.customerId, feedName);

    // Retrieve the attributes for the newly created feed.
    Map<String, FeedAttribute> adCustomizerFeedAttributes =
        getFeedAttributes(googleAdsClient, params.customerId, adCustomizerFeedResourceName);

    // Map the feed to the ad customizer placeholder type to mark it as an ad customizer.
    createAdCustomizerMapping(
        googleAdsClient,
        params.customerId,
        adCustomizerFeedResourceName,
        adCustomizerFeedAttributes);

    // Create the feed items that will fill the placeholders in the ads customized by the feed.
    List<String> feedItemResourceNames =
        createFeedItems(
            googleAdsClient,
            params.customerId,
            adCustomizerFeedResourceName,
            adCustomizerFeedAttributes);

    // Create a feed item targeting to associate the feed items with specific ad groups to
    // prevent them from being used in other ways.
    createFeedItemTargets(
        googleAdsClient, params.customerId, params.adGroupIds, feedItemResourceNames);

    // Create ads with the customizations provided by the feed items.
    createAdsWithCustomizations(googleAdsClient, params.customerId, params.adGroupIds, feedName);
  }

  /**
   * Creates a feed to be used for ad customization.
   *
   * @param googleAdsClient the Google Ads API client.
   * @param customerId the client customer ID.
   * @param feedName the name of the feed to create.
   * @return the resource name of the newly created feed.
   */
  private String createAdCustomizerFeed(
      GoogleAdsClient googleAdsClient, long customerId, String feedName) {

    // Creates three feed attributes: a name, a price and a date. The attribute names are arbitrary
    // choices and will be used as placeholders in the ad text fields.
    FeedAttribute nameAttribute =
        FeedAttribute.newBuilder()
            .setName(StringValue.of("Name"))
            .setType(FeedAttributeType.STRING)
            .build();

    FeedAttribute priceAttribute =
        FeedAttribute.newBuilder()
            .setName(StringValue.of("Price"))
            .setType(FeedAttributeType.STRING)
            .build();

    FeedAttribute dateAttribute =
        FeedAttribute.newBuilder()
            .setName(StringValue.of("Date"))
            .setType(FeedAttributeType.DATE_TIME)
            .build();

    Feed adCustomizerFeed =
        Feed.newBuilder()
            .setName(StringValue.of(feedName))
            .addAttributes(nameAttribute)
            .addAttributes(priceAttribute)
            .addAttributes(dateAttribute)
            .build();

    FeedOperation feedOperation = FeedOperation.newBuilder().setCreate(adCustomizerFeed).build();

    try (FeedServiceClient feedServiceClient =
        googleAdsClient.getLatestVersion().createFeedServiceClient()) {

      MutateFeedsResponse response =
          feedServiceClient.mutateFeeds(Long.toString(customerId), ImmutableList.of(feedOperation));

      String feedResourceName = response.getResults(0).getResourceName();
      System.out.printf("Added feed with resource name %s.%n", feedResourceName);
      return feedResourceName;
    }
  }

  /**
   * Retrieves all the attributes for a feed and returns them in a map using the attribute names as
   * keys.
   *
   * @param googleAdsClient the Google Ads API client.
   * @param customerId the client customer ID.
   * @param feedResourceName the resource name of the feed.
   * @return the attributes of the feed.
   */
  private Map<String, FeedAttribute> getFeedAttributes(
      GoogleAdsClient googleAdsClient, long customerId, String feedResourceName) {
    String query =
        String.format(
            "SELECT feed.attributes, feed.name FROM feed WHERE feed.resource_name = '%s'",
            feedResourceName);

    SearchGoogleAdsRequest request =
        SearchGoogleAdsRequest.newBuilder()
            .setCustomerId(Long.toString(customerId))
            .setPageSize(PAGE_SIZE)
            .setQuery(query)
            .build();

    Map<String, FeedAttribute> feedAttributes = new HashMap<>();
    try (GoogleAdsServiceClient googleAdsServiceClient =
        googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) {
      SearchPagedResponse searchPagedResponse = googleAdsServiceClient.search(request);

      Feed feed = searchPagedResponse.iterateAll().iterator().next().getFeed();

      System.out.printf(
          "Found the following attributes for feed with name '%s':%n", feed.getName().getValue());
      for (FeedAttribute feedAttribute : feed.getAttributesList()) {
        System.out.printf(
            "\t'%s' with id %d and type '%s'%n",
            feedAttribute.getName().getValue(),
            feedAttribute.getId().getValue(),
            feedAttribute.getType());
        feedAttributes.put(feedAttribute.getName().getValue(), feedAttribute);
      }
    }
    return feedAttributes;
  }

  /**
   * Creates a feed mapping and sets the feed as an ad customizer feed.
   *
   * @param googleAdsClient the Google Ads API client.
   * @param customerId the client customer ID.
   * @param feedResourceName the resource name of the feed.
   * @param feedAttributes the attributes of the feed.
   */
  private void createAdCustomizerMapping(
      GoogleAdsClient googleAdsClient,
      long customerId,
      String feedResourceName,
      Map<String, FeedAttribute> feedAttributes) {

    // Map the feed attributes to ad customizer placeholder fields.
    // For a full list of ad customizer placeholder fields, see
    // https://developers.google.com/google-ads/api/reference/rpc/google.ads.googleads.v3.enums#google.ads.googleads.v3.enums.AdCustomizerPlaceholderFieldEnum.AdCustomizerPlaceholderField
    AttributeFieldMapping nameFieldMapping =
        AttributeFieldMapping.newBuilder()
            .setFeedAttributeId(feedAttributes.get("Name").getId())
            .setAdCustomizerField(AdCustomizerPlaceholderField.STRING)
            .build();

    AttributeFieldMapping priceFieldMapping =
        AttributeFieldMapping.newBuilder()
            .setFeedAttributeId(feedAttributes.get("Price").getId())
            .setAdCustomizerField(AdCustomizerPlaceholderField.PRICE)
            .build();

    AttributeFieldMapping dateFieldMapping =
        AttributeFieldMapping.newBuilder()
            .setFeedAttributeId(feedAttributes.get("Date").getId())
            .setAdCustomizerField(AdCustomizerPlaceholderField.DATE)
            .build();

    FeedMapping feedMapping =
        FeedMapping.newBuilder()
            .setFeed(StringValue.of(feedResourceName))
            // Sets the feed to the AD_CUSTOMIZER placeholder type.
            .setPlaceholderType(PlaceholderType.AD_CUSTOMIZER)
            .addAttributeFieldMappings(nameFieldMapping)
            .addAttributeFieldMappings(priceFieldMapping)
            .addAttributeFieldMappings(dateFieldMapping)
            .build();

    FeedMappingOperation feedMappingOperation =
        FeedMappingOperation.newBuilder().setCreate(feedMapping).build();

    try (FeedMappingServiceClient feedMappingServiceClient =
        googleAdsClient.getLatestVersion().createFeedMappingServiceClient()) {

      MutateFeedMappingsResponse response =
          feedMappingServiceClient.mutateFeedMappings(
              Long.toString(customerId), ImmutableList.of(feedMappingOperation));

      System.out.printf(
          "Added feed mapping with resource name %s.%n", response.getResults(0).getResourceName());
    }
  }

  /**
   * Creates two different feed items to enable two different ad customizations.
   *
   * @param googleAdsClient the Google Ads API client.
   * @param customerId the client customer ID.
   * @param feedResourceName the resource name of the feed.
   * @param feedAttributes the attributes of the feed.
   * @return the resource names of the feed items.
   */
  private List<String> createFeedItems(
      GoogleAdsClient googleAdsClient,
      long customerId,
      String feedResourceName,
      Map<String, FeedAttribute> feedAttributes) {

    List<FeedItemOperation> feedItemOperations = new ArrayList<>();

    DateTime marsDate = DateTime.now().withDayOfMonth(1).withHourOfDay(0).withMinuteOfHour(0);
    feedItemOperations.add(
        createFeedItemOperation(
            "Mars",
            "$1234.56",
            marsDate.toString("yyyyMMdd HHmmss"),
            feedResourceName,
            feedAttributes));

    DateTime venusDate = DateTime.now().withDayOfMonth(15).withHourOfDay(0).withMinuteOfHour(0);
    feedItemOperations.add(
        createFeedItemOperation(
            "Venus",
            "$1450.00",
            venusDate.toString("yyyyMMdd HHmmss"),
            feedResourceName,
            feedAttributes));

    try (FeedItemServiceClient feedItemServiceClient =
        googleAdsClient.getLatestVersion().createFeedItemServiceClient()) {
      List<String> feedItemResourceNames = new ArrayList<>();
      MutateFeedItemsResponse response =
          feedItemServiceClient.mutateFeedItems(Long.toString(customerId), feedItemOperations);

      System.out.printf("Added %d feed items:%n", response.getResultsCount());

      for (MutateFeedItemResult result : response.getResultsList()) {
        String feedItemResourceName = result.getResourceName();
        feedItemResourceNames.add(feedItemResourceName);
        System.out.printf("Added feed item with resource name %s.%n", feedItemResourceName);
      }
      return feedItemResourceNames;
    }
  }

  /**
   * Helper function to create a FeedItemOperation.
   *
   * @param name the value of the Name attribute.
   * @param price the value of the Price attribute.
   * @param date the value of the Date attribute.
   * @param feedResourceName the resource name of the feed.
   * @param feedAttributes the attributes to be set on the feed.
   * @return a FeedItemOperation to create a feed item.
   */
  private FeedItemOperation createFeedItemOperation(
      String name,
      String price,
      String date,
      String feedResourceName,
      Map<String, FeedAttribute> feedAttributes) {
    FeedItemAttributeValue nameAttributeValue =
        FeedItemAttributeValue.newBuilder()
            .setFeedAttributeId(feedAttributes.get("Name").getId())
            .setStringValue(StringValue.of(name))
            .build();

    FeedItemAttributeValue priceAttributeValue =
        FeedItemAttributeValue.newBuilder()
            .setFeedAttributeId(feedAttributes.get("Price").getId())
            .setStringValue(StringValue.of(price))
            .build();

    FeedItemAttributeValue dateAttributeValue =
        FeedItemAttributeValue.newBuilder()
            .setFeedAttributeId(feedAttributes.get("Date").getId())
            .setStringValue(StringValue.of(date))
            .build();

    FeedItem feedItem =
        FeedItem.newBuilder()
            .setFeed(StringValue.of(feedResourceName))
            .addAttributeValues(nameAttributeValue)
            .addAttributeValues(priceAttributeValue)
            .addAttributeValues(dateAttributeValue)
            .build();

    return FeedItemOperation.newBuilder().setCreate(feedItem).build();
  }

  /**
   * Restricts the feed items to work only with a specific ad group; this prevents the feed items
   * from being used elsewhere and makes sure they are used only for customizing a specific ad
   * group.
   *
   * @param googleAdsClient the Google Ads API client.
   * @param customerId the client customer ID.
   * @param adGroupIds the ad group IDs to bind the feed items to.
   * @param feedItemResourceNames the resource names of the feed items.
   */
  private void createFeedItemTargets(
      GoogleAdsClient googleAdsClient,
      long customerId,
      List<Long> adGroupIds,
      List<String> feedItemResourceNames) {

    // Bind each feed item to a specific ad group to make sure it will only be used to customize
    // ads inside that ad group; using the feed item elsewhere will result in an error.
    for (int i = 0; i < feedItemResourceNames.size(); i++) {
      String feedItemResourceName = feedItemResourceNames.get(i);
      Long adGroupId = adGroupIds.get(i);

      FeedItemTarget feedItemTarget =
          FeedItemTarget.newBuilder()
              .setAdGroup(StringValue.of(ResourceNames.adGroup(customerId, adGroupId)))
              .setFeedItem(StringValue.of(feedItemResourceName))
              .build();

      FeedItemTargetOperation feedItemTargetOperation =
          FeedItemTargetOperation.newBuilder().setCreate(feedItemTarget).build();

      try (FeedItemTargetServiceClient feedItemTargetServiceClient =
          googleAdsClient.getLatestVersion().createFeedItemTargetServiceClient()) {

        MutateFeedItemTargetsResponse response =
            feedItemTargetServiceClient.mutateFeedItemTargets(
                Long.toString(customerId), ImmutableList.of(feedItemTargetOperation));

        String feedItemTargetResourceName = response.getResults(0).getResourceName();
        System.out.printf(
            "Added feed item target with resource name '%s'.%n", feedItemTargetResourceName);
      }
    }
  }

  /**
   * Creates expanded text ads that use the ad customizer feed to populate the placeholders.
   *
   * @param googleAdsClient the Google Ads API client.
   * @param customerId the client customer ID.
   * @param adGroupIds the ad group IDs in which to create the ads.
   * @param feedName the name of the feed.
   */
  private void createAdsWithCustomizations(
      GoogleAdsClient googleAdsClient, long customerId, List<Long> adGroupIds, String feedName) {

    // Creates an expanded text ad using the feed attribute names as placeholders.
    ExpandedTextAdInfo expandedTextAdInfo =
        ExpandedTextAdInfo.newBuilder()
            .setHeadlinePart1(
                StringValue.of(String.format("Luxury cruise to {=%s.Name}", feedName)))
            .setHeadlinePart2(StringValue.of(String.format("Only {=%s.Price}", feedName)))
            .setDescription(
                StringValue.of(String.format("Offer ends in {=countdown(%s.Date)}!", feedName)))
            .build();

    Ad ad =
        Ad.newBuilder()
            .setExpandedTextAd(expandedTextAdInfo)
            .addFinalUrls(StringValue.of("http://www.example.com"))
            .build();

    List<AdGroupAdOperation> adGroupAdOperations = new ArrayList<>();

    // Creates the same ad in all ad groups. When they serve, they will show different values,
    // since they match different feed items.
    for (Long adGroupId : adGroupIds) {
      AdGroupAd adGroupAd =
          AdGroupAd.newBuilder()
              .setAd(ad)
              .setAdGroup(StringValue.of(ResourceNames.adGroup(customerId, adGroupId)))
              .build();

      AdGroupAdOperation adGroupAdOperation =
          AdGroupAdOperation.newBuilder().setCreate(adGroupAd).build();

      adGroupAdOperations.add(adGroupAdOperation);
    }

    try (AdGroupAdServiceClient adGroupAdServiceClient =
        googleAdsClient.getLatestVersion().createAdGroupAdServiceClient()) {

      MutateAdGroupAdsResponse response =
          adGroupAdServiceClient.mutateAdGroupAds(Long.toString(customerId), adGroupAdOperations);

      System.out.printf("Added %d ads:%n", response.getResultsCount());
      for (MutateAdGroupAdResult result : response.getResultsList()) {
        System.out.printf("Added an ad with resource name '%s'.%n", result.getResourceName());
      }
    }
  }
}
C#
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using Google.Ads.GoogleAds.Lib;
using Google.Ads.GoogleAds.V3.Common;
using Google.Ads.GoogleAds.V3.Errors;
using Google.Ads.GoogleAds.V3.Resources;
using Google.Ads.GoogleAds.V3.Services;

using System;
using System.Collections.Generic;
using System.Linq;
using static Google.Ads.GoogleAds.V3.Enums.AdCustomizerPlaceholderFieldEnum.Types;
using static Google.Ads.GoogleAds.V3.Enums.FeedAttributeTypeEnum.Types;
using static Google.Ads.GoogleAds.V3.Enums.PlaceholderTypeEnum.Types;

namespace Google.Ads.GoogleAds.Examples.V3
{
    /// <summary>
    /// This code example adds an ad customizer feed and associates it with the customer.
    /// Then it adds an ad that uses the feed to populate dynamic data.
    /// </summary>
    public class AddAdCustomizer : ExampleBase
    {
        /// <summary>
        /// Main method, to run this code example as a standalone application.
        /// </summary>
        /// <param name="args">The command line arguments.</param>
        public static void Main(string[] args)
        {
            AddAdCustomizer codeExample = new AddAdCustomizer();
            Console.WriteLine(codeExample.Description);

            // The Google Ads customer ID for which the call is made.
            long customerId = long.Parse("INSERT_CUSTOMER_ID_HERE");

            // ID of the ad group to which ads are added.
            long adGroupId1 = long.Parse("INSERT_AD_GROUP_ID_HERE");

            // ID of the ad group to which ads are added.
            long adGroupId2 = long.Parse("INSERT_AD_GROUP_ID_HERE");


            codeExample.Run(new GoogleAdsClient(), customerId, adGroupId1, adGroupId2);
        }

        /// <summary>
        /// Returns a description about the code example.
        /// </summary>
        public override string Description
        {
            get
            {
                return "This code example adds an ad customizer feed and associates it with the " +
                    "customer. Then it adds an ad that uses the feed to populate dynamic data.";
            }
        }

        /// <summary>
        /// Runs the code example.
        /// </summary>
        /// <param name="client">The Google Ads client.</param>
        /// <param name="customerId">The Google Ads customer ID for which the call is made.</param>
        /// <param name="adGroupId1">ID of the first ad group to which ad customizers are added.
        /// </param>
        /// <param name="adGroupId2">ID of the second ad group to which ad customizers are added.
        /// </param>
        public void Run(GoogleAdsClient client, long customerId, long adGroupId1, long adGroupId2)
        {
            // Get the AdGroupBidModifierService.
            AdGroupBidModifierServiceClient adGroupBidModifierService =
                client.GetService(Services.V3.AdGroupBidModifierService);

            string feedName = "Ad Customizer example feed " + ExampleUtilities.GetRandomString();

            try
            {
                // Create a feed to be used as the ad customizer.
                string adCustomizerFeedResourceName =
                    CreateAdCustomizerFeed(client, customerId, feedName);

                // Retrieve the attributes for the newly created feed.
                Dictionary<string, FeedAttribute> adCustomizerFeedAttributes =
                    GetFeedAttributes(client, customerId, adCustomizerFeedResourceName);

                // Map the feed to the ad customizer placeholder type to mark it as an ad customizer.
                CreateAdCustomizerMapping(client, customerId, adCustomizerFeedResourceName,
                    adCustomizerFeedAttributes);

                // Create the feed items that will fill the placeholders in the ads customized by the feed.
                List<string> feedItemResourceNames = CreateFeedItems(client, customerId,
                    adCustomizerFeedResourceName, adCustomizerFeedAttributes);

                // Create a feed item targeting to associate the feed items with specific ad groups to
                // prevent them from being used in other ways.
                CreateFeedItemTargets(client, customerId, new[] { adGroupId1, adGroupId2 },
                    feedItemResourceNames);

                // Create ads with the customizations provided by the feed items.
                CreateAdsWithCustomizations(client, customerId, new[] { adGroupId1, adGroupId2 },
                    feedName);
            }
            catch (GoogleAdsException e)
            {
                Console.WriteLine("Failure:");
                Console.WriteLine($"Message: {e.Message}");
                Console.WriteLine($"Failure: {e.Failure}");
                Console.WriteLine($"Request ID: {e.RequestId}");
                throw;
            }

        }

        /// <summary>
        /// Creates a feed to be used for ad customization.
        /// </summary>
        /// <param name="client">The Google Ads client.</param>
        /// <param name="customerId">The Google Ads customer ID for which the call is made.</param>
        /// <param name="feedName">Name of the feed.</param>
        /// <returns>The resource name of the newly created feed.</returns>
        private string CreateAdCustomizerFeed(GoogleAdsClient client, long customerId,
            string feedName)
        {
            // Get the FeedServiceClient.
            FeedServiceClient feedService = client.GetService(Services.V3.FeedService);

            // Creates three feed attributes: a name, a price and a date. The attribute names
            // are arbitrary choices and will be used as placeholders in the ad text fields.
            FeedAttribute nameAttribute = new FeedAttribute()
            {
                Name = "Name",
                Type = FeedAttributeType.String
            };

            FeedAttribute priceAttribute = new FeedAttribute()
            {
                Name = "Price",
                Type = FeedAttributeType.String
            };

            FeedAttribute dateAttribute = new FeedAttribute()
            {
                Name = "Date",
                Type = FeedAttributeType.DateTime
            };

            Feed adCustomizerFeed = new Feed()
            {
                Name = feedName,
                Attributes = { nameAttribute, priceAttribute, dateAttribute }
            };

            FeedOperation feedOperation = new FeedOperation()
            {
                Create = adCustomizerFeed
            };

            MutateFeedsResponse response =
                feedService.MutateFeeds(customerId.ToString(), new[] { feedOperation });

            string feedResourceName = response.Results[0].ResourceName;
            Console.WriteLine($"Added feed with resource name '{feedResourceName}'.");
            return feedResourceName;
        }

        /// <summary>
        ///  Retrieves all the attributes for a feed and returns them in a map using the
        ///  attribute names as keys.
        /// </summary>
        /// <param name="client">The Google Ads client.</param>
        /// <param name="customerId">The Google Ads customer ID for which the call is made.</param>
        /// <param name="feedResourceName">The resource name of the feed.</param>
        /// <returns>The attributes of the feed.</returns>
        private Dictionary<string, FeedAttribute> GetFeedAttributes(GoogleAdsClient client,
                    long customerId, string feedResourceName)
        {
            // Get the GoogleAdsServiceClient.
            GoogleAdsServiceClient googleAdsService =
                client.GetService(Services.V3.GoogleAdsService);

            string query = $"SELECT feed.attributes, feed.name FROM feed WHERE " +
                $"feed.resource_name = '{feedResourceName}'";

            SearchGoogleAdsRequest request = new SearchGoogleAdsRequest()
            {
                CustomerId = customerId.ToString(),
                Query = query
            };

            Dictionary<string, FeedAttribute> feedAttributes =
                new Dictionary<string, FeedAttribute>();

            Feed feed = googleAdsService.Search(request).First().Feed;

            Console.WriteLine($"Found the following attributes for feed with name '{feed.Name}'");
            foreach (FeedAttribute feedAttribute in feed.Attributes)
            {
                Console.WriteLine($"\t'{feedAttribute.Name}' with id {feedAttribute.Id} and " +
                    $"type '{feedAttribute.Type}'");
                feedAttributes[feedAttribute.Name] = feedAttribute;
            }
            return feedAttributes;
        }

        /// <summary>
        /// Creates a feed mapping and sets the feed as an ad customizer feed.
        /// </summary>
        /// <param name="client">The Google Ads client.</param>
        /// <param name="customerId">The Google Ads customer ID for which the call is made.</param>
        /// <param name="feedResourceName">The resource name of the feed.</param>
        /// <param name="feedAttributes">The attributes of the feed.</param>
        private void CreateAdCustomizerMapping(GoogleAdsClient client, long customerId,
                    string feedResourceName, Dictionary<string, FeedAttribute> feedAttributes)
        {
            // Get the FeedMappingService.
            FeedMappingServiceClient feedMappingService =
                client.GetService(Services.V3.FeedMappingService);

            // Map the feed attributes to ad customizer placeholder fields.
            // For a full list of ad customizer placeholder fields, see
            // https://developers.google.com/google-ads/api/reference/rpc/Google.Ads.GoogleAds.V3.enums#Google.Ads.GoogleAds.V3.enums.AdCustomizerPlaceholderFieldEnum.AdCustomizerPlaceholderField
            AttributeFieldMapping nameFieldMapping = new AttributeFieldMapping()
            {
                FeedAttributeId = feedAttributes["Name"].Id,
                AdCustomizerField = AdCustomizerPlaceholderField.String
            };

            AttributeFieldMapping priceFieldMapping = new AttributeFieldMapping()
            {
                FeedAttributeId = feedAttributes["Price"].Id,
                AdCustomizerField = AdCustomizerPlaceholderField.Price
            };

            AttributeFieldMapping dateFieldMapping = new AttributeFieldMapping()
            {
                FeedAttributeId = feedAttributes["Date"].Id,
                AdCustomizerField = AdCustomizerPlaceholderField.Date
            };

            FeedMapping feedMapping = new FeedMapping()
            {
                Feed = feedResourceName,
                PlaceholderType = PlaceholderType.AdCustomizer,
                AttributeFieldMappings = { nameFieldMapping, priceFieldMapping, dateFieldMapping }
            };

            FeedMappingOperation operation = new FeedMappingOperation()
            {
                Create = feedMapping
            };

            MutateFeedMappingsResponse response =
                feedMappingService.MutateFeedMappings(customerId.ToString(), new[] { operation });

            Console.WriteLine($"Added feed mapping with resource name" +
                $" '{response.Results[0].ResourceName}'.");
        }

        /// <summary>
        /// Creates two different feed items to enable two different ad customizations.
        /// </summary>
        /// <param name="client">The Google Ads client.</param>
        /// <param name="customerId">The Google Ads customer ID for which the call is made.</param>
        /// <param name="feedResourceName">The resource name of the feed.</param>
        /// <param name="feedAttributes">The attributes of the feed.</param>
        /// <returns>The resource names of the feed items.</returns>
        private List<string> CreateFeedItems(GoogleAdsClient client, long customerId,
                    string feedResourceName, Dictionary<string, FeedAttribute> feedAttributes)
        {
            // Get the FeedItemServiceClient.
            FeedItemServiceClient feedItemService =
                client.GetService(Services.V3.FeedItemService);

            List<FeedItemOperation> feedItemOperations = new List<FeedItemOperation>();

            DateTime marsDate = new DateTime(DateTime.Now.Year, DateTime.Now.Month, 1);
            feedItemOperations.Add(
                CreateFeedItemOperation("Mars", "$1234.56", marsDate.ToString("yyyyMMdd HHmmss"),
                    feedResourceName, feedAttributes));

            DateTime venusDate = new DateTime(DateTime.Now.Year, DateTime.Now.Month, 15);
            feedItemOperations.Add(
                CreateFeedItemOperation("Venus", "$1450.00", venusDate.ToString("yyyyMMdd HHmmss"),
                    feedResourceName, feedAttributes));

            List<string> feedItemResourceNames = new List<string>();
            MutateFeedItemsResponse response =
                feedItemService.MutateFeedItems(customerId.ToString(), feedItemOperations);

            Console.WriteLine($"Added {response.Results.Count} feed items:");

            foreach (MutateFeedItemResult result in response.Results)
            {
                string feedItemResourceName = result.ResourceName;
                feedItemResourceNames.Add(feedItemResourceName);
                Console.WriteLine($"Added feed item with resource name '{feedItemResourceName}'.");
            }
            return feedItemResourceNames;
        }

        /// <summary>
        /// Helper function to create a FeedItemOperation.
        /// </summary>
        /// <param name="name">The value of the Name attribute.</param>
        /// <param name="price">The value of the Price attribute.</param>
        /// <param name="date">The value of the Date attribute.</param>
        /// <param name="feedResourceName">The resource name of the feed.</param>
        /// <param name="feedAttributes">The attributes to be set on the feed.</param>
        /// <returns>A FeedItemOperation to create a feed item.</returns>
        private FeedItemOperation CreateFeedItemOperation(string name, string price, string date,
                    string feedResourceName, Dictionary<string, FeedAttribute> feedAttributes)
        {
            FeedItemAttributeValue nameAttributeValue = new FeedItemAttributeValue()
            {
                FeedAttributeId = feedAttributes["Name"].Id,
                StringValue = name
            };

            FeedItemAttributeValue priceAttributeValue = new FeedItemAttributeValue()
            {
                FeedAttributeId = feedAttributes["Price"].Id,
                StringValue = price
            };

            FeedItemAttributeValue dateAttributeValue = new FeedItemAttributeValue()
            {
                FeedAttributeId = feedAttributes["Date"].Id,
                StringValue = date
            };

            FeedItem feedItem = new FeedItem()
            {
                Feed = feedResourceName,
                AttributeValues = { nameAttributeValue, priceAttributeValue, dateAttributeValue }
            };

            return new FeedItemOperation()
            {
                Create = feedItem
            };
        }

        /// <summary>
        /// Restricts the feed items to work only with a specific ad group; this prevents the
        /// feed items from being used elsewhere and makes sure they are used only for
        /// customizing a specific ad group.
        /// </summary>
        /// <param name="client">The Google Ads client.</param>
        /// <param name="customerId">The Google Ads customer ID for which the call is made.</param>
        /// <param name="adGroupIds">The ad group IDs to bind the feed items to..</param>
        /// <param name="feedItemResourceNames">The resource names of the feed items.</param>
        private void CreateFeedItemTargets(GoogleAdsClient client,
                    long customerId, long[] adGroupIds, List<string> feedItemResourceNames)
        {
            // Get the FeedItemTargetServiceClient.
            FeedItemTargetServiceClient feedItemTargetService =
                client.GetService(Services.V3.FeedItemTargetService);

            // Bind each feed item to a specific ad group to make sure it will only be used to
            // customize ads inside that ad group; using the feed item elsewhere will result
            // in an error.
            for (int i = 0; i < feedItemResourceNames.Count; i++)
            {
                string feedItemResourceName = feedItemResourceNames[i];
                long adGroupId = adGroupIds[i];

                FeedItemTarget feedItemTarget = new FeedItemTarget()
                {
                    AdGroup = ResourceNames.AdGroup(customerId, adGroupId),
                    FeedItem = feedItemResourceName
                };

                FeedItemTargetOperation feedItemTargetOperation = new FeedItemTargetOperation()
                {
                    Create = feedItemTarget
                };

                MutateFeedItemTargetsResponse response =
                    feedItemTargetService.MutateFeedItemTargets(customerId.ToString(),
                        new[] { feedItemTargetOperation });

                string feedItemTargetResourceName = response.Results[0].ResourceName;
                Console.WriteLine($"Added feed item target with resource name " +
                    $"'{response.Results[0].ResourceName}'.");
            }
        }

        /// <summary>
        /// Creates expanded text ads that use the ad customizer feed to populate the placeholders.
        /// </summary>
        /// <param name="client">The Google Ads client.</param>
        /// <param name="customerId">The Google Ads customer ID for which the call is made.</param>
        /// <param name="adGroupIds">The ad group IDs in which to create the ads.</param>
        /// <param name="feedName">Name of the feed.</param>
        private void CreateAdsWithCustomizations(GoogleAdsClient client, long customerId,
                    long[] adGroupIds, string feedName)
        {
            // Get the AdGroupAdServiceClient.
            AdGroupAdServiceClient adGroupAdService =
                client.GetService(Services.V3.AdGroupAdService);


            // Creates an expanded text ad using the feed attribute names as placeholders.
            Ad ad = new Ad()
            {
                ExpandedTextAd = new ExpandedTextAdInfo()
                {
                    HeadlinePart1 = $"Luxury cruise to {{={feedName}.Name}}",
                    HeadlinePart2 = $"Only {{={feedName}.Price}}",
                    Description = $"Offer ends in {{=countdown({feedName}.Date)}}!"
                },
                FinalUrls = { "http://www.example.com" }
            };

            List<AdGroupAdOperation> adGroupAdOperations = new List<AdGroupAdOperation>();

            // Creates the same ad in all ad groups. When they serve, they will show
            // different values, since they match different feed items.
            foreach (long adGroupId in adGroupIds)
            {
                AdGroupAd adGroupAd = new AdGroupAd()
                {
                    Ad = ad,
                    AdGroup = ResourceNames.AdGroup(customerId, adGroupId)
                };

                adGroupAdOperations.Add(new AdGroupAdOperation()
                {
                    Create = adGroupAd
                });
            }

            MutateAdGroupAdsResponse response =
                adGroupAdService.MutateAdGroupAds(customerId.ToString(), adGroupAdOperations);

            Console.WriteLine($"Added {response.Results.Count} ads:");
            foreach (MutateAdGroupAdResult result in response.Results)
            {
                Console.WriteLine($"Added an ad with resource name '{result.ResourceName}'.");
            }
        }
    }
}
PHP
<?php

/**
 * Copyright 2019 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace Google\Ads\GoogleAds\Examples\AdvancedOperations;

require __DIR__ . '/../../vendor/autoload.php';

use DateTime;
use GetOpt\GetOpt;
use Google\Ads\GoogleAds\Examples\Utils\ArgumentNames;
use Google\Ads\GoogleAds\Examples\Utils\ArgumentParser;
use Google\Ads\GoogleAds\Lib\V3\GoogleAdsClient;
use Google\Ads\GoogleAds\Lib\V3\GoogleAdsClientBuilder;
use Google\Ads\GoogleAds\Lib\V3\GoogleAdsException;
use Google\Ads\GoogleAds\Lib\OAuth2TokenBuilder;
use Google\Ads\GoogleAds\Util\V3\ResourceNames;
use Google\Ads\GoogleAds\V3\Common\ExpandedTextAdInfo;
use Google\Ads\GoogleAds\V3\Enums\FeedAttributeTypeEnum\FeedAttributeType;
use Google\Ads\GoogleAds\V3\Enums\FeedOriginEnum\FeedOrigin;
use Google\Ads\GoogleAds\V3\Resources\Ad;
use Google\Ads\GoogleAds\V3\Resources\AttributeFieldMapping;
use Google\Ads\GoogleAds\V3\Resources\Feed;
use Google\Ads\GoogleAds\V3\Resources\FeedAttribute;
use Google\Ads\GoogleAds\V3\Resources\FeedItem;
use Google\Ads\GoogleAds\V3\Resources\FeedMapping;
use Google\Ads\GoogleAds\V3\Services\FeedOperation;
use Google\Ads\GoogleAds\V3\Services\FeedItemOperation;
use Google\Ads\GoogleAds\V3\Services\FeedMappingOperation;
use Google\Ads\GoogleAds\V3\Resources\FeedItemAttributeValue;
use Google\Ads\GoogleAds\V3\Enums\AdCustomizerPlaceholderFieldEnum\AdCustomizerPlaceholderField;
use Google\Ads\GoogleAds\V3\Enums\PlaceholderTypeEnum\PlaceholderType;
use Google\Ads\GoogleAds\V3\Resources\AdGroupAd;
use Google\Ads\GoogleAds\V3\Resources\FeedItemTarget;
use Google\Ads\GoogleAds\V3\Services\AdGroupAdOperation;
use Google\Ads\GoogleAds\V3\Services\FeedItemTargetOperation;
use Google\Protobuf\StringValue;
use Google\ApiCore\ApiException;

/**
 * Adds an ad customizer feed and associates it with the customer. Then it adds an ad that uses the
 * feed to populate dynamic data.
 */
class AddAdCustomizer
{
    private const CUSTOMER_ID = 'INSERT_CUSTOMER_ID_HERE';
    private const AD_GROUP_ID_1 = 'INSERT_AD_GROUP_ID_1_HERE';
    private const AD_GROUP_ID_2 = 'INSERT_AD_GROUP_ID_2_HERE';

    // We're creating two different ad groups to be dynamically populated by the same feed.
    private const NUMBER_OF_AD_GROUPS = 2;

    // We're doing only searches by resource_name in this example, we can set page size = 1.
    private const PAGE_SIZE = 1;

    public static function main()
    {
        // Either pass the required parameters for this example on the command line, or insert them
        // into the constants above.
        $options = (new ArgumentParser())->parseCommandArguments([
            ArgumentNames::CUSTOMER_ID => GetOpt::REQUIRED_ARGUMENT,
            ArgumentNames::AD_GROUP_IDS => GetOpt::MULTIPLE_ARGUMENT
        ]);

        // Generate a refreshable OAuth2 credential for authentication.
        $oAuth2Credential = (new OAuth2TokenBuilder())->fromFile()->build();

        // Construct a Google Ads client configured from a properties file and the
        // OAuth2 credentials above.
        $googleAdsClient = (new GoogleAdsClientBuilder())->fromFile()
            ->withOAuth2Credential($oAuth2Credential)
            ->build();

        try {
            self::runExample(
                $googleAdsClient,
                $options[ArgumentNames::CUSTOMER_ID] ?: self::CUSTOMER_ID,
                $options[ArgumentNames::AD_GROUP_IDS] ?: [self::AD_GROUP_ID_1, self::AD_GROUP_ID_2]
            );
        } catch (GoogleAdsException $googleAdsException) {
            printf(
                "Request with ID '%s' has failed.%sGoogle Ads failure details:%s",
                $googleAdsException->getRequestId(),
                PHP_EOL,
                PHP_EOL
            );
            foreach ($googleAdsException->getGoogleAdsFailure()->getErrors() as $error) {
                /** @var GoogleAdsError $error */
                printf(
                    "\t%s: %s%s",
                    $error->getErrorCode()->getErrorCode(),
                    $error->getMessage(),
                    PHP_EOL
                );
            }
            exit(1);
        } catch (ApiException $apiException) {
            printf(
                "ApiException was thrown with message '%s'.%s",
                $apiException->getMessage(),
                PHP_EOL
            );
            exit(1);
        }
    }

    /**
     * Runs the example.
     *
     * @param GoogleAdsClient $googleAdsClient the Google Ads API client
     * @param int $customerId the customer ID
     * @param int $adGroupIds the ad group IDs
     */
    public static function runExample(
        GoogleAdsClient $googleAdsClient,
        int $customerId,
        array $adGroupIds
    ) {
        if (count($adGroupIds) != self::NUMBER_OF_AD_GROUPS) {
            throw new \InvalidArgumentException(
                'Please pass exactly ' . self::NUMBER_OF_AD_GROUPS .
                ' ad group IDs in the adGroupIds parameter.'
            );
        }

        $feedName = 'Ad Customizer example feed ' . uniqid();

        // Create a feed to be used for ad customization.
        $adCustomizerFeedResourceName = self::createAdCustomizerFeed(
            $googleAdsClient,
            $customerId,
            $feedName
        );

        // Retrieve the attributes of the feed.
        $adCustomizerFeedAttributes = self::getFeedAttributes(
            $googleAdsClient,
            $customerId,
            $adCustomizerFeedResourceName
        );

        // Map the feed to the ad customizer placeholder fields.
        self::createAdCustomizerMapping(
            $googleAdsClient,
            $customerId,
            $adCustomizerFeedResourceName,
            $adCustomizerFeedAttributes
        );

        // Create feed items to be used to customize ads.
        $feedItemResourceNames = self::createFeedItems(
            $googleAdsClient,
            $customerId,
            $adCustomizerFeedResourceName,
            $adCustomizerFeedAttributes
        );

        // Set the feed to be used only with the specified ad groups.
        self::createFeedItemTargets(
            $googleAdsClient,
            $customerId,
            $adGroupIds,
            $feedItemResourceNames
        );

        // Create ads that use the feed for customization.
        self::createAdsWithCustomizations(
            $googleAdsClient,
            $customerId,
            $adGroupIds,
            $feedName
        );
    }

   /**
    * Creates a feed to be used for ad customization.
    *
    * @param GoogleAdsClient googleAdsClient the Google Ads API client
    * @param int $customerId the customer ID in which to create the feed
    * @param string $feedName the name of the feed to create
    * @return string the resource name of the newly created feed
    */
    private static function createAdCustomizerFeed(
        GoogleAdsClient $googleAdsClient,
        int $customerId,
        string $feedName
    ) {
        // Creates three feed attributes: a name, a price and a date. The attribute names are
        // arbitrary choices and will be used as placeholders in the ad text fields.
        $nameAttribute = new FeedAttribute([
            'type' => FeedAttributeType::STRING,
            'name' => new StringValue(['value' => 'Name'])
        ]);
        
        $priceAttribute = new FeedAttribute([
            'type' => FeedAttributeType::STRING,
            'name' => new StringValue(['value' => 'Price'])
        ]);

        $dateAttribute = new FeedAttribute([
            'type' => FeedAttributeType::DATE_TIME,
            'name' => new StringValue(['value' => 'Date'])
        ]);

        // Creates the feed.
        $feed = new Feed([
            'name' => new StringValue(['value' => $feedName]),
            'attributes' => [$nameAttribute, $priceAttribute, $dateAttribute],
            'origin' => FeedOrigin::USER
        ]);

        // Creates a feed operation for creating a feed.
        $feedOperation = new FeedOperation();
        $feedOperation->setCreate($feed);

        // Issues a mutate request to add the feed.
        $feedServiceClient = $googleAdsClient->getFeedServiceClient();
        $feedResponse = $feedServiceClient->mutateFeeds($customerId, [$feedOperation]);

        $feedResourceName = $feedResponse->getResults()[0]->getResourceName();
        printf("Added feed with resource name '%s'.%s", $feedResourceName, PHP_EOL);

        return $feedResourceName;
    }

    /**
     * Retrieves attributes for a feed.
     *
     * @param GoogleAdsClient googleAdsClient the Google Ads API client
     * @param int customerId the customer ID
     * @param string feedResourceName the resource name of the feed
     * @return array the feed attributes, keyed by attribute name
     */
    private static function getFeedAttributes(
        GoogleAdsClient $googleAdsClient,
        int $customerId,
        string $feedResourceName
    ) {
        $query = "SELECT feed.attributes, feed.name FROM feed "
            . "WHERE feed.resource_name = '$feedResourceName'";

        $googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient();
        $response =
            $googleAdsServiceClient->search($customerId, $query, ['pageSize' => self::PAGE_SIZE]);

        $feed = $response->getIterator()->current()->getFeed();
        $feedDetails = [];
        printf(
            "Found the following attributes for feed with name %s:%s",
            $feed->getNameUnwrapped(),
            PHP_EOL
        );
        foreach ($feed->getAttributes() as $feedAttribute) {
            /** @var FeedAttribute $feedAttribute */
            $feedDetails[$feedAttribute->getNameUnwrapped()] = $feedAttribute->getIdUnwrapped();
            printf(
                "\t'%s' with id %d and type '%s'%s",
                $feedAttribute->getNameUnwrapped(),
                $feedAttribute->getIdUnwrapped(),
                FeedAttributeType::name($feedAttribute->getType()),
                PHP_EOL
            );
        }
        return $feedDetails;
    }

    /**
     * Creates a feed mapping for a given feed.
     *
     * @param GoogleAdsClient googleAdsClient the Google Ads API client
     * @param int customerId the customer ID
     * @param string $adCustomizerFeedResourceName the resource name of the ad customizer feed
     * @param array feedDetails an associative array from feed attribute names to their IDs
     */
    private static function createAdCustomizerMapping(
        GoogleAdsClient $googleAdsClient,
        int $customerId,
        string $adCustomizerFeedResourceName,
        array $feedDetails
    ) {
        // Maps the feed attribute IDs to the field ID constants.
        $nameFieldMapping = new AttributeFieldMapping([
            'feed_attribute_id' => $feedDetails['Name'],
            'ad_customizer_field' => AdCustomizerPlaceholderField::STRING
        ]);

        $priceFieldMapping = new AttributeFieldMapping([
            'feed_attribute_id' => $feedDetails['Price'],
            'ad_customizer_field' => AdCustomizerPlaceholderField::PRICE
        ]);

        $dateFieldMapping = new AttributeFieldMapping([
            'feed_attribute_id' => $feedDetails['Date'],
            'ad_customizer_field' => AdCustomizerPlaceholderField::DATE
        ]);

        // Creates the feed mapping.
        $feedMapping = new FeedMapping([
            'placeholder_type' => PlaceholderType::AD_CUSTOMIZER,
            'feed' => $adCustomizerFeedResourceName,
            'attribute_field_mappings' => [$nameFieldMapping, $priceFieldMapping, $dateFieldMapping]
        ]);

        // Creates the operation.
        $feedMappingOperation = new FeedMappingOperation();
        $feedMappingOperation->setCreate($feedMapping);

        // Issues a mutate request to add the feed mapping.
        $feedMappingServiceClient = $googleAdsClient->getFeedMappingServiceClient();
        $response = $feedMappingServiceClient->mutateFeedMappings(
            $customerId,
            [$feedMappingOperation]
        );

        // Displays the results.
        foreach ($response->getResults() as $result) {
            printf(
                "Created feed mapping with resource name '%s'.%s",
                $result->getResourceName(),
                PHP_EOL
            );
        }
    }

    /**
     * Creates two different feed items to enable two different ad customizations.
     *
     * @param GoogleAdsClient googleAdsClient the Google Ads API client
     * @param int $customerId the customer ID
     * @param string $adCustomizerFeedResourceName the resource name of the feed
     * @param array $adCustomizerFeedAttributes the attributes of the feed
     */
    private function createFeedItems(
        GoogleAdsClient $googleAdsClient,
        int $customerId,
        string $adCustomizerFeedResourceName,
        array $adCustomizerFeedAttributes
    ) {
        $feedItemOperations = [];
                
        $feedItemOperations[] = self::createFeedItemOperation(
            'Mars',
            '$1234.56',
            date_format(new DateTime('first day of this month'), 'Ymd His'),
            $adCustomizerFeedResourceName,
            $adCustomizerFeedAttributes
        );
        
        $feedItemOperations[] = self::createFeedItemOperation(
            'Venus',
            '$6543.21',
            // Set the date to the 15th of the current month.
            date_format(DateTime::createFromFormat('d', '15'), 'Ymd His'),
            $adCustomizerFeedResourceName,
            $adCustomizerFeedAttributes
        );

        // Adds the feed items.
        $feedItemServiceClient = $googleAdsClient->getFeedItemServiceClient();
        $response = $feedItemServiceClient->mutateFeedItems($customerId, $feedItemOperations);

        $feedItemResourceNames = [];
        // Displays the results.
        foreach ($response->getResults() as $result) {
            printf(
                "Created feed item with resource name '%s'.%s",
                $result->getResourceName(),
                PHP_EOL
            );
            $feedItemResourceNames[] = $result->getResourceName();
        }

        return $feedItemResourceNames;
    }

    /**
     * Creates a FeedItemOperation.
     *
     * @param string name the value of the Name attribute
     * @param string price the value of the Price attribute
     * @param string date the value of the Date attribute
     * @param string adCustomizerFeedResourceName the resource name of the feed
     * @param array adCustomizerFeedAttributes the attributes to be set on the feed
     * @return FeedItemOperation the feed item operation to create a feed item
     */
    private function createFeedItemOperation(
        string $name,
        string $price,
        string $date,
        string $adCustomizerFeedResourceName,
        array $adCustomizerFeedAttributes
    ) {
        $nameAttributeValue = new FeedItemAttributeValue([
            'feed_attribute_id' => $adCustomizerFeedAttributes['Name'],
            'string_value' => new StringValue(['value' => $name])
        ]);

        $priceAttributeValue = new FeedItemAttributeValue([
            'feed_attribute_id' => $adCustomizerFeedAttributes['Price'],
            'string_value' => new StringValue(['value' => $price])
        ]);
                
        $dateAttributeValue = new FeedItemAttributeValue([
            'feed_attribute_id' => $adCustomizerFeedAttributes['Date'],
            'string_value' => new StringValue(['value' => $date])
        ]);

        $feedItem = new FeedItem([
            'feed' => $adCustomizerFeedResourceName,
            'attribute_values' => [
                $nameAttributeValue,
                $priceAttributeValue,
                $dateAttributeValue
            ]
        ]);

        $feedItemOperation = new FeedItemOperation();
        $feedItemOperation->setCreate($feedItem);

        return $feedItemOperation;
    }

  /**
   * Restricts the feed items to work only with a specific ad group; this prevents the feed items
   * from being used elsewhere and makes sure they are used only for customizing a specific ad
   * group.
   *
   * @param GoogleAdsClient googleAdsClient the Google Ads API client
   * @param int $customerId the customer ID
   * @param array adGroupIds the ad group IDs to bind the feed items to
   * @param array feedItemResourceNames the resource names of the feed items
   */
    private function createFeedItemTargets(
        GoogleAdsClient $googleAdsClient,
        int $customerId,
        array $adGroupIds,
        array $feedItemResourceNames
    ) {
        // Bind each feed item to a specific ad group to make sure it will only be used to customize
        // ads inside that ad group; using the feed item elsewhere will result in an error.
        for ($i = 0; $i < count($feedItemResourceNames); $i++) {
            $feedItemResourceName = $feedItemResourceNames[$i];
            $adGroupId = $adGroupIds[$i];

            $feedItemTarget = new FeedItemTarget([
                'feed_item' => new StringValue(['value' => $feedItemResourceName]),
                'ad_group' => ResourceNames::forAdGroup($customerId, $adGroupId)
            ]);

            // Creates the operation.
            $feedItemTargetOperation = new FeedItemTargetOperation();
            $feedItemTargetOperation->setCreate($feedItemTarget);

            // Issues a mutate request to add the feed item target.
            $feedItemTargetServiceClient = $googleAdsClient->getFeedItemTargetServiceClient();
            $feedItemTargetResponse = $feedItemTargetServiceClient->mutateFeedItemTargets(
                $customerId,
                [$feedItemTargetOperation]
            );

            $feedItemTargetResourceName =
                $feedItemTargetResponse->getResults()[0]->getResourceName();
            printf(
                "Added feed item target with resource name '%s'.%s",
                $feedItemTargetResourceName,
                PHP_EOL
            );
        }
    }

    /**
     * Creates expanded text ads that use the ad customizer feed to populate the placeholders.
     *
     * @param GoogleAdsClient googleAdsClient the Google Ads API client
     * @param int customerId the client customer ID
     * @param array adGroupIds the ad group IDs in which to create the ads
     * @param string feedName the name of the feed
     */
    private function createAdsWithCustomizations(
        GoogleAdsClient $googleAdsClient,
        int $customerId,
        array $adGroupIds,
        string $feedName
    ) {
        $expandedTextAdInfo = new ExpandedTextAdInfo([
            'headline_part1' => new StringValue(['value' => "Luxury cruise to {=$feedName.Name}"]),
            'headline_part2' => new StringValue(['value' => "Only {=$feedName.Price}"]),
            'description' => new StringValue([
                'value' => "Offer ends in {=countdown($feedName.Date)}!"
            ])
        ]);

        $ad = new Ad([
            'expanded_text_ad' => $expandedTextAdInfo,
            'final_urls' => ['http://www.example.com']
        ]);

        $adGroupAdOperations = [];

        foreach ($adGroupIds as $adGroupId) {
            $adGroupAd = new AdGroupAd([
                'ad' => $ad,
                'ad_group' => new StringValue([
                    'value' => ResourceNames::forAdGroup($customerId, $adGroupId)
                ])
            ]);

            $adGroupAdOperation = new AdGroupAdOperation();
            $adGroupAdOperation->setCreate($adGroupAd);

            $adGroupAdOperations[] = $adGroupAdOperation;
        }

        // Issues a mutate request to add the ads.
        $adGroupAdServiceClient = $googleAdsClient->getAdGroupAdServiceClient();
        $adGroupAdResponse = $adGroupAdServiceClient->mutateAdGroupAds(
            $customerId,
            $adGroupAdOperations
        );

        printf('Added %d ads:%s', count($adGroupAdResponse->getResults()), PHP_EOL);
        foreach ($adGroupAdResponse->getResults() as $result) {
            printf("Added an ad with resource name '%s'.%s", $result->getResourceName(), PHP_EOL);
        }
    }
}

AddAdCustomizer::main();
Perl
#!/usr/bin/perl -w
#
# Copyright 2019, Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Adds an ad customizer feed and associates it with the customer. Then it adds an
# ad that uses the feed to populate dynamic data.

use strict;
use warnings;
use utf8;

use FindBin qw($Bin);
use lib "$Bin/../../lib";
use Google::Ads::GoogleAds::Client;
use Google::Ads::GoogleAds::Utils::GoogleAdsHelper;
use Google::Ads::GoogleAds::V3::Resources::FeedAttribute;
use Google::Ads::GoogleAds::V3::Resources::Feed;
use Google::Ads::GoogleAds::V3::Resources::AttributeFieldMapping;
use Google::Ads::GoogleAds::V3::Resources::FeedMapping;
use Google::Ads::GoogleAds::V3::Resources::FeedItemAttributeValue;
use Google::Ads::GoogleAds::V3::Resources::FeedItem;
use Google::Ads::GoogleAds::V3::Resources::FeedItemTarget;
use Google::Ads::GoogleAds::V3::Resources::Ad;
use Google::Ads::GoogleAds::V3::Resources::AdGroupAd;
use Google::Ads::GoogleAds::V3::Common::ExpandedTextAdInfo;
use Google::Ads::GoogleAds::V3::Enums::FeedAttributeTypeEnum
  qw(STRING DATE_TIME);
use Google::Ads::GoogleAds::V3::Enums::FeedOriginEnum qw(USER);
use Google::Ads::GoogleAds::V3::Enums::AdCustomizerPlaceholderFieldEnum;
use Google::Ads::GoogleAds::V3::Enums::PlaceholderTypeEnum qw(AD_CUSTOMIZER);
use Google::Ads::GoogleAds::V3::Services::FeedService::FeedOperation;
use
  Google::Ads::GoogleAds::V3::Services::FeedMappingService::FeedMappingOperation;
use Google::Ads::GoogleAds::V3::Services::FeedItemService::FeedItemOperation;
use
  Google::Ads::GoogleAds::V3::Services::FeedItemTargetService::FeedItemTargetOperation;
use Google::Ads::GoogleAds::V3::Services::AdGroupAdService::AdGroupAdOperation;
use Google::Ads::GoogleAds::V3::Utils::ResourceNames;

use Getopt::Long qw(:config auto_help);
use Pod::Usage;
use Cwd qw(abs_path);
use Data::Uniqid qw(uniqid);
use POSIX qw(strftime mktime);

# We're doing only searches by resource_name in this example, we can set page size = 1.
use constant PAGE_SIZE => 1;

# The following parameter(s) should be provided to run the example. You can
# either specify these by changing the INSERT_XXX_ID_HERE values below, or on
# the command line.
#
# Parameters passed on the command line will override any parameters set in
# code.
#
# Running the example with -h will print the command line usage.
my $customer_id   = "INSERT_CUSTOMER_ID_HERE";
my $ad_group_id_1 = "INSERT_AD_GROUP_ID_1_HERE";
my $ad_group_id_2 = "INSERT_AD_GROUP_ID_2_HERE";

sub add_ad_customizer {
  my ($api_client, $customer_id, $ad_group_id_1, $ad_group_id_2) = @_;

  my $feed_name = "Ad Customizer example feed " . uniqid();

  # Create a feed to be used for ad customization.
  my $ad_customizer_feed_resource_name =
    create_ad_customizer_feed($api_client, $customer_id, $feed_name);

  # Retrieve the attributes of the feed.
  my $ad_customizer_feed_attributes =
    get_feed_attributes($api_client, $customer_id,
    $ad_customizer_feed_resource_name);

  # Map the feed to the ad customizer placeholder fields.
  create_ad_customizer_mapping(
    $api_client, $customer_id,
    $ad_customizer_feed_resource_name,
    $ad_customizer_feed_attributes
  );

  # Create feed items to be used to customize ads.
  my $feed_item_resource_names = create_feed_items(
    $api_client, $customer_id,
    $ad_customizer_feed_resource_name,
    $ad_customizer_feed_attributes
  );

  # Set the feed to be used only with the specified ad groups.
  create_feed_item_targets($api_client, $customer_id,
    [$ad_group_id_1, $ad_group_id_2],
    $feed_item_resource_names);

  # Create ads that use the feed for customization.
  create_ads_with_customizations($api_client, $customer_id,
    [$ad_group_id_1, $ad_group_id_2], $feed_name);

  return 1;
}

# Creates a feed to be used for ad customization.
sub create_ad_customizer_feed {
  my ($api_client, $customer_id, $feed_name) = @_;

  # Create three feed attributes: a name, a price and a date. The attribute names
  # are arbitrary choices and will be used as placeholders in the ad text fields.
  my $name_attribute =
    Google::Ads::GoogleAds::V3::Resources::FeedAttribute->new({
      type => STRING,
      name => "Name"
    });

  my $price_attribute =
    Google::Ads::GoogleAds::V3::Resources::FeedAttribute->new({
      type => STRING,
      name => "Price"
    });

  my $date_attribute =
    Google::Ads::GoogleAds::V3::Resources::FeedAttribute->new({
      type => DATE_TIME,
      name => "Date"
    });

  # Create the feed.
  my $feed = Google::Ads::GoogleAds::V3::Resources::Feed->new({
    name       => $feed_name,
    attributes => [$name_attribute, $price_attribute, $date_attribute],
    origin     => USER
  });

  # Create a feed operation for creating a feed.
  my $feed_operation =
    Google::Ads::GoogleAds::V3::Services::FeedService::FeedOperation->new({
      create => $feed
    });

  # Issue a mutate request to add the feed.
  my $feed_response = $api_client->FeedService()->mutate({
      customerId => $customer_id,
      operations => [$feed_operation]});

  my $feed_resource_name = $feed_response->{results}[0]{resourceName};
  printf "Added feed with resource name '%s'.\n", $feed_resource_name;

  return $feed_resource_name;
}

# Retrieves attributes for a feed.
sub get_feed_attributes {
  my ($api_client, $customer_id, $feed_resource_name) = @_;

  my $search_query = "SELECT feed.attributes, feed.name FROM feed " .
    "WHERE feed.resource_name = '$feed_resource_name'";

  my $search_response = $api_client->GoogleAdsService()->search({
    customerId => $customer_id,
    query      => $search_query,
    pageSize   => PAGE_SIZE
  });

  my $feed         = $search_response->{results}[0]{feed};
  my $feed_details = {};
  printf "Found the following attributes for feed with name %s:\n",
    $feed->{name};

  foreach my $feed_attribute (@{$feed->{attributes}}) {
    $feed_details->{$feed_attribute->{name}} = $feed_attribute->{id};
    printf "\t'%s' with id %d and type '%s'\n", $feed_attribute->{name},
      $feed_attribute->{id}, $feed_attribute->{type};
  }
  return $feed_details;
}

# Creates a feed mapping for a given feed.
sub create_ad_customizer_mapping {
  my (
    $api_client, $customer_id,
    $ad_customizer_feed_resource_name,
    $ad_customizer_feed_attributes
  ) = @_;

  # Map the feed attribute IDs to the field ID constants.
  my $name_field_mapping =
    Google::Ads::GoogleAds::V3::Resources::AttributeFieldMapping->new({
      feedAttributeId => $ad_customizer_feed_attributes->{Name},
      adCustomizerField =>
        Google::Ads::GoogleAds::V3::Enums::AdCustomizerPlaceholderFieldEnum::STRING,
    });

  my $price_field_mapping =
    Google::Ads::GoogleAds::V3::Resources::AttributeFieldMapping->new({
      feedAttributeId => $ad_customizer_feed_attributes->{Price},
      adCustomizerField =>
        Google::Ads::GoogleAds::V3::Enums::AdCustomizerPlaceholderFieldEnum::PRICE,
    });

  my $date_field_mapping =
    Google::Ads::GoogleAds::V3::Resources::AttributeFieldMapping->new({
      feedAttributeId => $ad_customizer_feed_attributes->{Date},
      adCustomizerField =>
        Google::Ads::GoogleAds::V3::Enums::AdCustomizerPlaceholderFieldEnum::DATE,
    });

  # Create the feed mapping.
  my $feed_mapping = Google::Ads::GoogleAds::V3::Resources::FeedMapping->new({
      placeholderType => AD_CUSTOMIZER,
      feed            => $ad_customizer_feed_resource_name,
      attributeFieldMappings =>
        [$name_field_mapping, $price_field_mapping, $date_field_mapping]});

  # Create the operation.
  my $feed_mapping_operation =
    Google::Ads::GoogleAds::V3::Services::FeedMappingService::FeedMappingOperation
    ->new({
      create => $feed_mapping
    });

  # Issue a mutate request to add the feed mapping.
  my $feed_mapping_response = $api_client->FeedMappingService()->mutate({
      customerId => $customer_id,
      operations => [$feed_mapping_operation]});

  # Display the results.
  foreach my $result (@{$feed_mapping_response->{results}}) {
    printf "Created feed mapping with resource name '%s'.\n",
      $result->{resourceName};
  }
}

# Creates two different feed items to enable two different ad customizations.
sub create_feed_items {
  my (
    $api_client, $customer_id,
    $ad_customizer_feed_resource_name,
    $ad_customizer_feed_attributes
  ) = @_;

  my $feed_item_operations = [];

  my ($sec, $min, $hour, $mday, $mon, $year) = localtime(time);

  push @$feed_item_operations,
    create_feed_item_operation(
    "Mars",
    '$1234.56',
    strftime("%Y%m%d %H%M%S", localtime(mktime(0, 0, 0, 1, $mon, $year))),
    $ad_customizer_feed_resource_name,
    $ad_customizer_feed_attributes
    );

  push @$feed_item_operations, create_feed_item_operation(
    "Venus",
    '$6543.21',
    # Set the date to the 15th of the current month.
    strftime("%Y%m%d %H%M%S", localtime(mktime(0, 0, 0, 15, $mon, $year))),
    $ad_customizer_feed_resource_name,
    $ad_customizer_feed_attributes
  );

  # Add the feed items.
  my $feed_item_response = $api_client->FeedItemService()->mutate({
    customerId => $customer_id,
    operations => $feed_item_operations
  });

  my $feed_item_resource_names = [];
  # Displays the results.
  foreach my $result (@{$feed_item_response->{results}}) {
    printf "Created feed item with resource name '%s'.\n",
      $result->{resourceName};
    push @$feed_item_resource_names, $result->{resourceName};
  }

  return $feed_item_resource_names;
}

# Creates a FeedItemOperation.
sub create_feed_item_operation {
  my (
    $name, $price, $date,
    $ad_customizer_feed_resource_name,
    $ad_customizer_feed_attributes
  ) = @_;

  my $name_attribute_value =
    Google::Ads::GoogleAds::V3::Resources::FeedItemAttributeValue->new({
      feedAttributeId => $ad_customizer_feed_attributes->{Name},
      stringValue     => $name
    });

  my $price_attribute_value =
    Google::Ads::GoogleAds::V3::Resources::FeedItemAttributeValue->new({
      feedAttributeId => $ad_customizer_feed_attributes->{Price},
      stringValue     => $price
    });

  my $date_attribute_value =
    Google::Ads::GoogleAds::V3::Resources::FeedItemAttributeValue->new({
      feedAttributeId => $ad_customizer_feed_attributes->{Date},
      stringValue     => $date
    });

  my $feed_item = Google::Ads::GoogleAds::V3::Resources::FeedItem->new({
      feed => $ad_customizer_feed_resource_name,
      attributeValues =>
        [$name_attribute_value, $price_attribute_value, $date_attribute_value]}
  );

  return
    Google::Ads::GoogleAds::V3::Services::FeedItemService::FeedItemOperation->
    new({
      create => $feed_item
    });
}

# Restricts the feed items to work only with a specific ad group; this prevents
# the feed items from being used elsewhere and makes sure they are used only for
# customizing a specific ad group.
sub create_feed_item_targets {
  my ($api_client, $customer_id, $ad_group_ids, $feed_item_resource_names) = @_;

  # Bind each feed item to a specific ad group to make sure it will only be used
  # to customize ads inside that ad group; using the feed item elsewhere will
  # result in an error.
  for (my $i = 0 ; $i < scalar @$feed_item_resource_names ; $i++) {
    my $feed_item_resource_name = $feed_item_resource_names->[$i];
    my $ad_group_id             = $ad_group_ids->[$i];

    my $feed_item_target =
      Google::Ads::GoogleAds::V3::Resources::FeedItemTarget->new({
        feedItem => $feed_item_resource_name,
        adGroup  => Google::Ads::GoogleAds::V3::Utils::ResourceNames::ad_group(
          $customer_id, $ad_group_id
        )});

    # Create the operation.
    my $feed_item_target_operation =
      Google::Ads::GoogleAds::V3::Services::FeedItemTargetService::FeedItemTargetOperation
      ->new({
        create => $feed_item_target
      });

    # Issue a mutate request to add the feed item target.
    my $feed_item_target_response =
      $api_client->FeedItemTargetService()->mutate({
        customerId => $customer_id,
        operations => [$feed_item_target_operation]});

    my $feed_item_target_resource_name =
      $feed_item_target_response->{results}[0]{resourceName};
    printf "Added feed item target with resource name '%s'.\n",
      $feed_item_target_resource_name;
  }
}

# Creates expanded text ads that use the ad customizer feed to populate the placeholders.
sub create_ads_with_customizations {
  my ($api_client, $customer_id, $ad_group_ids, $feed_name) = @_;

  my $expanded_text_ad_info =
    Google::Ads::GoogleAds::V3::Common::ExpandedTextAdInfo->new({
      headlinePart1 => "Luxury cruise to {=$feed_name.Name}",
      headlinePart2 => "Only {=$feed_name.Price}",
      description   => "Offer ends in {=countdown($feed_name.Date)}!"
    });

  my $ad = Google::Ads::GoogleAds::V3::Resources::Ad->new({
      expandedTextAd => $expanded_text_ad_info,
      finalUrls      => ["http://www.example.com"]});

  my $ad_group_ad_operations = [];
  foreach my $ad_group_id (@$ad_group_ids) {
    my $ad_group_ad = Google::Ads::GoogleAds::V3::Resources::AdGroupAd->new({
        ad      => $ad,
        adGroup => Google::Ads::GoogleAds::V3::Utils::ResourceNames::ad_group(
          $customer_id, $ad_group_id
        )});

    push @$ad_group_ad_operations,
      Google::Ads::GoogleAds::V3::Services::AdGroupAdService::AdGroupAdOperation
      ->new({
        create => $ad_group_ad
      });
  }

  # Issue a mutate request to add the ads.
  my $ad_group_ad_response = $api_client->AdGroupAdService()->mutate({
    customerId => $customer_id,
    operations => $ad_group_ad_operations
  });

  my $ad_group_ad_results = $ad_group_ad_response->{results};
  printf "Added %d ads:\n", scalar @$ad_group_ad_results;
  foreach my $ad_group_ad_result (@$ad_group_ad_results) {
    printf "Added an ad with resource name '%s'.\n",
      $ad_group_ad_result->{resourceName};
  }
}

# Don't run the example if the file is being included.
if (abs_path($0) ne abs_path(__FILE__)) {
  return 1;
}

# Get Google Ads Client, credentials will be read from ~/googleads.properties.
my $api_client = Google::Ads::GoogleAds::Client->new({version => "V3"});

# By default examples are set to die on any server returned fault.
$api_client->set_die_on_faults(1);

# Parameters passed on the command line will override any parameters set in code.
GetOptions(
  "customer_id=s"   => \$customer_id,
  "ad_group_id_1=i" => \$ad_group_id_1,
  "ad_group_id_2=i" => \$ad_group_id_2
);

# Print the help message if the parameters are not initialized in the code nor
# in the command line.
pod2usage(2) if not check_params($customer_id, $ad_group_id_1, $ad_group_id_2);

# Call the example.
add_ad_customizer($api_client, $customer_id =~ s/-//gr,
  $ad_group_id_1, $ad_group_id_2);

=pod

=head1 NAME

add_ad_customizer

=head1 DESCRIPTION

Adds an ad customizer feed and associates it with the customer. Then it adds an
ad that uses the feed to populate dynamic data.

=head1 SYNOPSIS

add_ad_customizer.pl [options]

    -help                       Show the help message.
    -customer_id                The Google Ads customer ID.
    -ad_group_id_1              The ad group ID 1 to bind the feed items to.
    -ad_group_id_2              The ad group ID 2 to bind the feed items to.

=cut