Learn more about migrating to the Google Ads API by watching our latest webinar

Code example

Java

// Copyright 2021 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.

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.CodeSampleHelper;
import com.google.ads.googleads.examples.utils.CodeSampleParams;
import com.google.ads.googleads.lib.GoogleAdsClient;
import com.google.ads.googleads.lib.utils.FieldMasks;
import com.google.ads.googleads.v8.common.AdTextAsset;
import com.google.ads.googleads.v8.common.KeywordThemeInfo;
import com.google.ads.googleads.v8.common.LocationInfo;
import com.google.ads.googleads.v8.enums.AdGroupTypeEnum.AdGroupType;
import com.google.ads.googleads.v8.enums.AdTypeEnum.AdType;
import com.google.ads.googleads.v8.enums.AdvertisingChannelSubTypeEnum.AdvertisingChannelSubType;
import com.google.ads.googleads.v8.enums.AdvertisingChannelTypeEnum.AdvertisingChannelType;
import com.google.ads.googleads.v8.enums.BudgetDeliveryMethodEnum.BudgetDeliveryMethod;
import com.google.ads.googleads.v8.enums.BudgetTypeEnum.BudgetType;
import com.google.ads.googleads.v8.enums.CampaignStatusEnum.CampaignStatus;
import com.google.ads.googleads.v8.enums.CriterionTypeEnum.CriterionType;
import com.google.ads.googleads.v8.enums.DayOfWeekEnum.DayOfWeek;
import com.google.ads.googleads.v8.enums.MinuteOfHourEnum.MinuteOfHour;
import com.google.ads.googleads.v8.errors.GoogleAdsError;
import com.google.ads.googleads.v8.errors.GoogleAdsException;
import com.google.ads.googleads.v8.resources.Ad;
import com.google.ads.googleads.v8.resources.KeywordThemeConstant;
import com.google.ads.googleads.v8.resources.SmartCampaignSetting;
import com.google.ads.googleads.v8.services.GoogleAdsServiceClient;
import com.google.ads.googleads.v8.services.KeywordThemeConstantServiceClient;
import com.google.ads.googleads.v8.services.MutateGoogleAdsResponse;
import com.google.ads.googleads.v8.services.MutateOperation;
import com.google.ads.googleads.v8.services.MutateOperationResponse;
import com.google.ads.googleads.v8.services.SmartCampaignSuggestServiceClient;
import com.google.ads.googleads.v8.services.SuggestKeywordThemeConstantsRequest;
import com.google.ads.googleads.v8.services.SuggestKeywordThemeConstantsResponse;
import com.google.ads.googleads.v8.services.SuggestSmartCampaignBudgetOptionsRequest;
import com.google.ads.googleads.v8.services.SuggestSmartCampaignBudgetOptionsResponse;
import com.google.ads.googleads.v8.services.SuggestSmartCampaignBudgetOptionsResponse.BudgetOption;
import com.google.ads.googleads.v8.utils.ResourceNames;
import com.google.protobuf.Descriptors.OneofDescriptor;
import com.google.protobuf.Message;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

/** Demonstrates how to create a Smart Campaign. */
public class AddSmartCampaign {

  private static final String DEFAULT_KEYWORD = "travel";
  private static final long GEO_TARGET_CONSTANT = 1023191;
  private static final String COUNTRY_CODE = "US";
  private static final String LANGUAGE_CODE = "en";
  private static final String LANDING_PAGE_URL = "http://www.example.com";
  private static final String PHONE_NUMBER = "555-555-5555";
  private static final long BUDGET_TEMPORARY_ID = -1;
  private static final long SMART_CAMPAIGN_TEMPORARY_ID = -2;
  private static final long AD_GROUP_TEMPORARY_ID = -3;

  private static class AddSmartCampaignParams extends CodeSampleParams {

    @Parameter(
        names = ArgumentNames.CUSTOMER_ID,
        description = "The Google Ads customer ID",
        required = true)
    private long customerId;

    @Parameter(
        names = ArgumentNames.KEYWORD_TEXT,
        description =
            "A keyword text used to generate a set of keyword themes, which are used to improve"
                + " the budget suggestion and performance of the Smart campaign. Default value is"
                + " 'travel'.")
    private String keyword = DEFAULT_KEYWORD;

    @Parameter(
        names = ArgumentNames.BUSINESS_LOCATION_ID,
        description =
            "The ID of a Google My Business (GMB) location. This is required if a business name is"
                + " not provided. It can be retrieved using the GMB API, for details see:"
                + " https://developers.google.com/my-business/reference/rest/v4/accounts.locations")
    private Long locationId;

    @Parameter(
        names = ArgumentNames.BUSINESS_NAME,
        description =
            "The name of a Google My Business (GMB) business. This is required if a business"
                + " location ID is not provided.")
    private String businessName;
  }

  public static void main(String[] args) throws IOException {
    AddSmartCampaignParams params = new AddSmartCampaignParams();
    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.keyword = "INSERT_KEYWORD HERE";

      // Must specify one of location ID or business name.
      params.locationId = Long.valueOf("INSERT_BUSINESS_LOCATION_ID_HERE");
      params.businessName = "INSERT_BUSINESS_NAME";
    }

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

    try {
      new AddSmartCampaign()
          .runExample(
              googleAdsClient,
              params.customerId,
              params.keyword,
              params.locationId,
              params.businessName);
    } 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);
      }
      System.exit(1);
    }
  }

  private void runExample(
      GoogleAdsClient googleAdsClient,
      long customerId,
      String keyword,
      Long locationId,
      String businessName) {
    if (locationId == null && businessName == null) {
      throw new RuntimeException("Must provider either --locationId or --businessName");
    }

    // Generates some suggested keyword theme constants based on the --keywordText parameter.
    List<KeywordThemeConstant> keywordThemeConstants =
        getKeywordThemeConstants(googleAdsClient, keyword);

    // Converts the KeywordThemeConstants to KeywordThemeInfos.
    List<KeywordThemeInfo> keywordThemeInfos = getKeywordThemeInfos(keywordThemeConstants);

    // Generates a suggested daily budget.
    long suggestedDailyBudgetMicros =
        getBudgetSuggestions(googleAdsClient, customerId, locationId, keywordThemeInfos);

    // Creates an array of operations which will create the campaign and related entities.
    List<MutateOperation> operations =
        new ArrayList(
            Arrays.asList(
                createCampaignBudgetOperation(customerId, suggestedDailyBudgetMicros),
                createSmartCampaignOperation(customerId),
                createSmartCampaignSettingOperation(customerId, locationId, businessName),
                createAdGroupOperation(customerId),
                createAdGroupAdOperation(customerId)));
    operations.addAll(createCampaignCriterionOperations(customerId, keywordThemeInfos));

    // Issues a mutate request to add the various entities required for a smart campaign.
    sendMutateRequest(googleAdsClient, customerId, operations);
  }

  /** Retrieves KeywordThemeConstants for the given criteria. */
  private List<KeywordThemeConstant> getKeywordThemeConstants(
      GoogleAdsClient googleAdsClient, String keyword) {
    try (KeywordThemeConstantServiceClient client =
        googleAdsClient.getLatestVersion().createKeywordThemeConstantServiceClient()) {
      SuggestKeywordThemeConstantsRequest request =
          SuggestKeywordThemeConstantsRequest.newBuilder()
              .setQueryText(keyword)
              .setCountryCode(COUNTRY_CODE)
              .setLanguageCode(LANGUAGE_CODE)
              .build();
      SuggestKeywordThemeConstantsResponse response = client.suggestKeywordThemeConstants(request);
      return response.getKeywordThemeConstantsList();
    }
  }

  /**
   * Retrieves a suggested budget amount for a new budget.
   *
   * <p>Using the SmartCampaignSuggestService to determine a daily budget for new and existing Smart
   * campaigns is highly recommended because it helps the campaigns achieve optimal performance.
   *
   * @return the recommended budget amount in micros ($1 = 1_000_000 micros).
   */
  private long getBudgetSuggestions(
      GoogleAdsClient googleAdsClient,
      long customerId,
      Long locationId,
      List<KeywordThemeInfo> keywordThemeInfos) {
    SuggestSmartCampaignBudgetOptionsRequest.Builder request =
        SuggestSmartCampaignBudgetOptionsRequest.newBuilder()
            .setCustomerId(String.valueOf(customerId));

    // You can retrieve suggestions for an existing campaign by setting the
    // "campaign" field of the request equal to the resource name of a campaign
    // and leaving the rest of the request fields below unset:
    // request.setCampaign("INSERT_CAMPAIGN_RESOURCE_NAME_HERE");

    // Uses the suggestion_info field instead, since these suggestions are for a new campaign.
    request
        .getSuggestionInfoBuilder()
        // Adds the URL of the campaign's landing page.
        .setFinalUrl(LANDING_PAGE_URL)
        // Appends keyword theme constants by mapping them to KeywordThemeInfos.
        .addAllKeywordThemes(keywordThemeInfos);

    // Constructs location information using the given geo target constant. It's
    // also possible to provide a geographic proximity using the "proximity"
    // field on suggestion_info, for example:
    // request
    //     .getSuggestionInfoBuilder()
    //     .getProximityBuilder()
    //     .getAddressBuilder()
    //     .setPostalCode("INSERT_POSTAL_CODE")
    //     .setProvinceCode("INSERT_PROVINCE_CODE")
    //     .setCountryCode("INSERT_COUNTRY_CODE")
    //     .setProvinceName("INSERT_PROVINCE_NAME")
    //     .setStreetAddress("INSERT_STREET_ADDRESS")
    //     .setStreetAddress2("INSERT_STREET_ADDRESS_2")
    //     .setCityName("INSERT_CITY_NAME");
    // request
    //     .getSuggestionInfoBuilder()
    //     .getProximityBuilder()
    //     .setRadius(INSERT_RADIUS)
    //     .setradiusunits(RADIUS_UNITS);
    //
    // For more information on proximities see:
    // https://developers.google.com/google-ads/api/reference/rpc/latest/ProximityInfo
    request
        .getSuggestionInfoBuilder()
        .getLocationListBuilder()
        .addLocations(
            LocationInfo.newBuilder()
                // Sets the location to the resource name of the given geo target constant.
                .setGeoTargetConstant(ResourceNames.geoTargetConstant(GEO_TARGET_CONSTANT))
                .build());

    // Adds a schedule detailing which days of the week the business is open.
    // The schedule below is for a business that is open on Mondays from 9am to 5pm.
    request
        .getSuggestionInfoBuilder()
        .addAdSchedulesBuilder()
        .setDayOfWeek(DayOfWeek.MONDAY)
        .setStartHour(9)
        .setEndHour(17)
        .setStartMinute(MinuteOfHour.ZERO)
        .setEndMinute(MinuteOfHour.ZERO);

    // Adds the GMB location ID, if provided.
    if (locationId != null) {
      request.getSuggestionInfoBuilder().setBusinessLocationId(locationId);
    }

    // Issues a request to retrieve a budget suggestion.
    try (SmartCampaignSuggestServiceClient client =
        googleAdsClient.getLatestVersion().createSmartCampaignSuggestServiceClient()) {
      SuggestSmartCampaignBudgetOptionsResponse response =
          client.suggestSmartCampaignBudgetOptions(request.build());
      BudgetOption recommendation = response.getRecommended();
      System.out.printf(
          "A daily budget amount of %d micros was suggested, garnering an estimated minimum of %d"
              + " clicks and an estimated maximum of %d per day.%n",
          recommendation.getDailyAmountMicros(),
          recommendation.getMetrics().getMinDailyClicks(),
          recommendation.getMetrics().getMaxDailyClicks());
      return recommendation.getDailyAmountMicros();
    }
  }

  /**
   * Creates a MutateOperation that creates a new CampaignBudget.
   *
   * <p>A temporary ID will be assigned to this campaign budget so that it can be referenced by
   * other objects being created in the same Mutate request.
   */
  private MutateOperation createCampaignBudgetOperation(long customerId, long dailyBudgetMicros) {
    MutateOperation.Builder builder = MutateOperation.newBuilder();
    builder
        .getCampaignBudgetOperationBuilder()
        .getCreateBuilder()
        .setName("Smart campaign budget " + CodeSampleHelper.getShortPrintableDateTime())
        .setDeliveryMethod(BudgetDeliveryMethod.STANDARD)
        // A budget used for Smart campaigns must have the type SMART_CAMPAIGN.
        .setType(BudgetType.SMART_CAMPAIGN)
        // The suggested budget amount from the SmartCampaignSuggestService is for a _daily_ budget.
        // We don't need to specify that here, because the budget period already defaults to DAILY.
        .setAmountMicros(dailyBudgetMicros)
        // Sets a temporary ID in the budget's resource name so it can be referenced by the campaign
        // in later steps.
        .setResourceName(ResourceNames.campaignBudget(customerId, BUDGET_TEMPORARY_ID));
    return builder.build();
  }

  /**
   * Creates a MutateOperation that creates a new Smart campaign.
   *
   * <p>A temporary ID will be assigned to this campaign so that it can be referenced by other
   * objects being created in the same Mutate request.
   *
   * @return
   */
  private MutateOperation createSmartCampaignOperation(long customerId) {
    MutateOperation.Builder builder = MutateOperation.newBuilder();
    builder
        .getCampaignOperationBuilder()
        .getCreateBuilder()
        .setName("Smart campaign " + CodeSampleHelper.getShortPrintableDateTime())
        .setStatus(CampaignStatus.PAUSED)
        .setAdvertisingChannelType(AdvertisingChannelType.SMART)
        .setAdvertisingChannelSubType(AdvertisingChannelSubType.SMART_CAMPAIGN)
        // Assigns the resource name with a temporary ID.
        .setResourceName(ResourceNames.campaign(customerId, SMART_CAMPAIGN_TEMPORARY_ID))
        .setCampaignBudget(ResourceNames.campaignBudget(customerId, BUDGET_TEMPORARY_ID));
    return builder.build();
  }

  /**
   * Creates a MutateOperation to create a new SmartCampaignSetting.
   *
   * <p>SmartCampaignSettings are unique in that they only support UPDATE operations, which are used
   * to update and create them. Below we will use a temporary ID in the resource name to associate
   * it with the campaign created in the previous step.
   */
  private MutateOperation createSmartCampaignSettingOperation(
      long customerId, Long locationId, String businessName) {
    MutateOperation.Builder builder = MutateOperation.newBuilder();
    SmartCampaignSetting.Builder settingBuilder =
        builder
            .getSmartCampaignSettingOperationBuilder()
            .getUpdateBuilder()
            .setResourceName(ResourceNames.smartCampaignSetting(customerId, SMART_CAMPAIGN_TEMPORARY_ID))
            .setFinalUrl(LANDING_PAGE_URL)
            .setAdvertisingLanguageCode(LANGUAGE_CODE);
    settingBuilder
        .getPhoneNumberBuilder()
        .setCountryCode(COUNTRY_CODE)
        .setPhoneNumber(PHONE_NUMBER);
    if (locationId != null) {
      settingBuilder.setBusinessLocationId(locationId);
    } else {
      settingBuilder.setBusinessName(businessName);
    }
    builder
        .getSmartCampaignSettingOperationBuilder()
        .setUpdateMask(FieldMasks.allSetFieldsOf(settingBuilder.build()));
    return builder.build();
  }

  /**
   * Creates a MutateOperation that creates a new ad group.
   *
   * <p>A temporary ID will be used in the campaign resource name for this ad group to associate it
   * with the Smart campaign created in earlier steps. A temporary ID will also be used for its own
   * resource name so that we can associate an ad group ad with it later in the process.
   *
   * <p>Only one ad group can be created for a given Smart campaign.
   */
  private MutateOperation createAdGroupOperation(long customerId) {
    MutateOperation.Builder builder = MutateOperation.newBuilder();
    builder
        .getAdGroupOperationBuilder()
        .getCreateBuilder()
        .setResourceName(ResourceNames.adGroup(customerId, AD_GROUP_TEMPORARY_ID))
        .setName("Smart campaign ad group " + CodeSampleHelper.getShortPrintableDateTime())
        .setCampaign(ResourceNames.campaign(customerId, SMART_CAMPAIGN_TEMPORARY_ID))
        .setType(AdGroupType.SMART_CAMPAIGN_ADS);
    return builder.build();
  }

  /**
   * Creates a MutateOperation that creates a new ad group ad.
   *
   * <p>A temporary ID will be used in the ad group resource name for this ad group ad to associate
   * it with the ad group created in earlier steps.
   */
  private MutateOperation createAdGroupAdOperation(long customerId) {
    MutateOperation.Builder opBuilder = MutateOperation.newBuilder();

    // Constructs an Ad instance containing a SmartCampaignAd.
    Ad.Builder adBuilder = Ad.newBuilder();
    adBuilder
        .setType(AdType.SMART_CAMPAIGN_AD)
        .getSmartCampaignAdBuilder()
        .addHeadlines(AdTextAsset.newBuilder().setText("Headline number one"))
        .addHeadlines(AdTextAsset.newBuilder().setText("Headline number two"))
        .addHeadlines(AdTextAsset.newBuilder().setText("Headline number three"))
        .addDescriptions(AdTextAsset.newBuilder().setText("Description number one"))
        .addDescriptions(AdTextAsset.newBuilder().setText("Description number two"));

    opBuilder
        .getAdGroupAdOperationBuilder()
        .getCreateBuilder()
        .setAdGroup(ResourceNames.adGroup(customerId, AD_GROUP_TEMPORARY_ID))
        .setAd(adBuilder);
    return opBuilder.build();
  }

  /**
   * Creates {@link com.google.ads.googleads.v8.resources.CampaignCriterion} operations for add each
   * {@link KeywordThemeInfo}.
   */
  private Collection<? extends MutateOperation> createCampaignCriterionOperations(
      long customerId, List<KeywordThemeInfo> keywordThemeInfos) {
    return keywordThemeInfos.stream()
        .map(
            keywordTheme -> {
              MutateOperation.Builder builder = MutateOperation.newBuilder();
              builder
                  .getCampaignCriterionOperationBuilder()
                  .getCreateBuilder()
                  .setCampaign(ResourceNames.campaign(customerId, SMART_CAMPAIGN_TEMPORARY_ID))
                  .setType(CriterionType.KEYWORD_THEME)
                  .setKeywordTheme(keywordTheme);
              return builder.build();
            })
        .collect(Collectors.toList());
  }

  /**
   * Sends a mutate request with a group of mutate operations.
   *
   * <p>The {@link GoogleAdsServiceClient} allows batching together a list of operations. These are
   * executed sequentially, and later operations my refer to previous operations via temporary IDs.
   * For more detail on this, please refer to
   * https://developers.google.com/google-ads/api/docs/batch-processing/temporary-ids.
   */
  private void sendMutateRequest(
      GoogleAdsClient googleAdsClient, long customerId, List<MutateOperation> operations) {
    try (GoogleAdsServiceClient client =
        googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) {
      MutateGoogleAdsResponse outerResponse = client.mutate(String.valueOf(customerId), operations);
      for (MutateOperationResponse innerResponse :
          outerResponse.getMutateOperationResponsesList()) {
        OneofDescriptor oneofDescriptor =
            innerResponse.getDescriptorForType().getOneofs().stream()
                .filter(o -> o.getName().equals("response"))
                .findFirst()
                .get();
        Message createdEntity =
            (Message)
                innerResponse.getField(innerResponse.getOneofFieldDescriptor(oneofDescriptor));
        String resourceName =
            (String)
                createdEntity.getField(
                    createdEntity.getDescriptorForType().findFieldByName("resource_name"));
        System.out.printf(
            "Created a(n) %s with resource name: '%s'.%n",
            createdEntity.getClass().getSimpleName(), resourceName);
      }
    }
  }

  /**
   * Provides a helper method to convert {@link KeywordThemeConstant} to {@link KeywordThemeInfo}.
   */
  private List<KeywordThemeInfo> getKeywordThemeInfos(
      List<KeywordThemeConstant> keywordThemeConstants) {
    return keywordThemeConstants.stream()
        .map(
            k -> KeywordThemeInfo.newBuilder().setKeywordThemeConstant(k.getResourceName()).build())
        .collect(Collectors.toList());
  }
}

      
      

C#

// Copyright 2021 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 System;
using System.Collections.Generic;
using System.Linq;
using CommandLine;
using Google.Ads.GoogleAds.Lib;
using Google.Ads.GoogleAds.Util;
using Google.Ads.GoogleAds.V8.Common;
using Google.Ads.GoogleAds.V8.Enums;
using Google.Ads.GoogleAds.V8.Errors;
using Google.Ads.GoogleAds.V8.Resources;
using Google.Ads.GoogleAds.V8.Services;
using static Google.Ads.GoogleAds.V8.Enums.AdGroupTypeEnum.Types;
using static Google.Ads.GoogleAds.V8.Enums.AdvertisingChannelTypeEnum.Types;
using static Google.Ads.GoogleAds.V8.Enums.AdvertisingChannelSubTypeEnum.Types;
using static Google.Ads.GoogleAds.V8.Enums.BudgetTypeEnum.Types;
using static Google.Ads.GoogleAds.V8.Enums.CampaignStatusEnum.Types;
using static Google.Ads.GoogleAds.V8.Enums.CriterionTypeEnum.Types;

namespace Google.Ads.GoogleAds.Examples.V8
{
    /// <summary>
    /// This example shows how to create a Smart Campaign.
    ///
    /// More details on Smart Campaigns can be found here:
    /// https://support.google.com/google-ads/answer/7652860
    /// </summary>
    public class AddSmartCampaign : ExampleBase
    {
        /// <summary>
        /// Command line options for running the <see cref="AddSmartCampaign"/> example.
        /// </summary>
        public class Options : OptionsBase
        {
            /// <summary>
            /// The Google Ads customer ID.
            /// </summary>
            [Option("customerId", Required = true, HelpText =
                "The Google Ads customer ID.")]
            public long CustomerId { get; set; }

            /// <summary>
            /// A keyword text used to generate a set of keyword themes, which are used to improve
            /// the budget recommendation and performance of the Smart Campaign. Default value is
            /// defined in the DEFAULT_KEYWORD_TEXT constant.
            /// </summary>
            [Option("keywordText", Required = false, HelpText =
                    "A keyword text used to generate a set of keyword themes, which are used to " +
                    "improve the budget recommendation and performance of the Smart Campaign." +
                    "\nDefault value is defined in the DEFAULT_KEYWORD_TEXT constant.",
                Default = DEFAULT_KEYWORD_TEXT)]
            public string Keyword { get; set; }

            /// <summary>
            /// The ID of a Google My Business (GMB) location.  This is required if a business name
            /// is not provided. This ID can be retrieved using the GMB API, for details see:
            /// https://developers.google.com/my-business/reference/rest/v4/accounts.locations
            /// </summary>
            [Option("businessLocationId", Required = false, HelpText =
                "The ID of a Google My Business (GMB) location. This is required if a business " +
                "name is not provided.\n" +
                "This ID can be retrieved using the GMB API; for details see: " +
                "https://developers.google.com/my-business/reference/rest/v4/accounts.locations")]
            public long? BusinessLocationId { get; set; }

            /// <summary>
            /// The name of a Google My Business (GMB) business. This is required if a business
            /// location ID is not provided.
            /// </summary>
            [Option("businessName", Required = false, HelpText =
                "The name of a Google My Business (GMB) business. This is required if a business " +
                "location ID is not provided.")]
            public string BusinessName { get; set; }
        }

        /// <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)
        {
            Options options = new Options();
            Parser.Default.ParseArguments<Options>(args).MapResult(
                delegate(Options o)
                {
                    options = o;
                    return 0;
                }, delegate(IEnumerable<Error> errors)
                {
                    // The Google Ads customer ID.
                    options.CustomerId = long.Parse("INSERT_CUSTOMER_ID_HERE");

                    // A keyword text used to generate a set of keyword themes, which are used to
                    // improve the budget recommendation and performance of the Smart Campaign.
                    options.Keyword = DEFAULT_KEYWORD_TEXT;

                    // The ID of a Google My Business (GMB) location. This is required if a business
                    // name is not provided.
                    options.BusinessLocationId = long.Parse("INSERT_BUSINESS_LOCATION_ID_HERE");

                    // The name of a Google My Business (GMB) business. This is required if a
                    // business location ID is not provided.
                    options.BusinessName = "INSERT_BUSINESS_NAME_HERE";

                    return 0;
                });

            AddSmartCampaign codeExample = new AddSmartCampaign();
            Console.WriteLine(codeExample.Description);
            codeExample.Run(new GoogleAdsClient(), options.CustomerId, options.Keyword,
                options.BusinessLocationId, options.BusinessName);
        }

        private const string DEFAULT_KEYWORD_TEXT = "travel";

        // Geo target constant for New York City.
        private const long GEO_TARGET_CONSTANT = 1023191;
        private const string COUNTRY_CODE = "US";
        private const string LANGUAGE_CODE = "en";
        private const string LANDING_PAGE_URL = "http://www.example.com";
        private const string PHONE_NUMBER = "555-555-5555";
        private const long BUDGET_TEMPORARY_ID = -1;
        private const long SMART_CAMPAIGN_TEMPORARY_ID = -2;
        private const long AD_GROUP_TEMPORARY_ID = -3;

        /// <summary>
        /// Returns a description about the code example.
        /// </summary>
        public override string Description =>
            "This example shows how to create a Smart Campaign.\nMore details on Smart Campaigns " +
            "can be found here: https: //support.google.com/google-ads/answer/7652860";

        /// <summary>
        /// Runs the code example.
        /// </summary>
        /// <param name="client">The Google Ads client.</param>
        /// <param name="customerId">The Google Ads customer ID.</param>
        /// <param name="keywordText">A keyword string used for generating keyword themes.</param>
        /// <param name="businessLocationId">The ID of a Google My Business location.</param>
        /// <param name="businessName">The name of a Google My Business business.</param>
        public void Run(GoogleAdsClient client, long customerId, string keywordText,
            long? businessLocationId, string businessName)
        {
            GoogleAdsServiceClient googleAdsServiceClient =
                client.GetService(Services.V8.GoogleAdsService);

            try
            {
                IEnumerable<KeywordThemeConstant> keywordThemeConstants =
                    GetKeywordThemeConstants(client, keywordText);

                // Map the KeywordThemeConstants to KeywordThemeInfo objects.
                IEnumerable<KeywordThemeInfo> keywordThemeInfos = keywordThemeConstants.Select(
                        constant =>
                            new KeywordThemeInfo {KeywordThemeConstant = constant.ResourceName})
                    .ToList();

                long suggestedBudgetAmount = GetBudgetSuggestion(client, customerId,
                    businessLocationId, keywordThemeInfos);

                // The below methods create and return MutateOperations that we later provide to the
                // GoogleAdsService.Mutate method in order to create the entities in a single
                // request. Since the entities for a Smart campaign are closely tied to one-another
                // it's considered a best practice to create them in a single Mutate request; the
                // entities will either all complete successfully or fail entirely, leaving no
                // orphaned entities. See:
                // https://developers.google.com/google-ads/api/docs/mutating/overview
                MutateOperation campaignBudgetOperation =
                    CreateCampaignBudgetOperation(customerId, suggestedBudgetAmount);
                MutateOperation smartCampaignOperation =
                    CreateSmartCampaignOperation(customerId);
                MutateOperation smartCampaignSettingOperation =
                    CreateSmartCampaignSettingOperation(customerId, businessLocationId,
                        businessName);
                IEnumerable<MutateOperation> campaignCriterionOperations =
                    CreateCampaignCriterionOperations(customerId, keywordThemeInfos);
                MutateOperation adGroupOperation = CreateAdGroupOperation(customerId);
                MutateOperation adGroupAdOperation = CreateAdGroupAdOperation(customerId);

                // Send the operations in a single mutate request.
                MutateGoogleAdsRequest mutateGoogleAdsRequest = new MutateGoogleAdsRequest
                {
                    CustomerId = customerId.ToString()
                };
                // It's important to create these entities in this order because they depend on each
                // other, for example the SmartCampaignSetting and ad group depend on the campaign,
                // and the ad group ad depends on the ad group.
                mutateGoogleAdsRequest.MutateOperations.Add(campaignBudgetOperation);
                mutateGoogleAdsRequest.MutateOperations.Add(smartCampaignOperation);
                mutateGoogleAdsRequest.MutateOperations.Add(smartCampaignSettingOperation);
                mutateGoogleAdsRequest.MutateOperations.Add(campaignCriterionOperations);
                mutateGoogleAdsRequest.MutateOperations.Add(adGroupOperation);
                mutateGoogleAdsRequest.MutateOperations.Add(adGroupAdOperation);

                MutateGoogleAdsResponse response =
                    googleAdsServiceClient.Mutate(mutateGoogleAdsRequest);

                PrintResponseDetails(response);
            }
            catch (GoogleAdsException e)
            {
                Console.WriteLine("Failure:");
                Console.WriteLine($"Message: {e.Message}");
                Console.WriteLine($"Failure: {e.Failure}");
                Console.WriteLine($"Request ID: {e.RequestId}");
                throw;
            }
        }

        /// <summary>
        /// Retrieves keyword theme constants for the given criteria.
        /// </summary>
        /// <param name="client">The Google Ads client.</param>
        /// <param name="keywordText">A keyword used for generating keyword themes.</param>
        /// <returns>A list of KeywordThemeConstants.</returns>
        private IEnumerable<KeywordThemeConstant> GetKeywordThemeConstants(GoogleAdsClient client,
            string keywordText)
        {
            KeywordThemeConstantServiceClient keywordThemeConstantServiceClient =
                client.GetService(Services.V8.KeywordThemeConstantService);

            SuggestKeywordThemeConstantsRequest request = new SuggestKeywordThemeConstantsRequest
            {
                QueryText = keywordText,
                CountryCode = COUNTRY_CODE,
                LanguageCode = LANGUAGE_CODE
            };

            SuggestKeywordThemeConstantsResponse response =
                keywordThemeConstantServiceClient.SuggestKeywordThemeConstants(request);

            Console.WriteLine($"Retrieved {response.KeywordThemeConstants.Count} keyword theme " +
                $"constants using the keyword '{keywordText}'.");
            return response.KeywordThemeConstants.ToList();
        }

        /// <summary>
        /// Retrieves a suggested budget amount for a new budget.
        /// Using the SmartCampaignSuggestService to determine a daily budget for new and existing
        /// Smart campaigns is highly recommended because it helps the campaigns achieve optimal
        /// performance.
        /// </summary>
        /// <param name="client">The Google Ads client.</param>
        /// <param name="customerId">The Google Ads customer ID.</param>
        /// <param name="businessLocationId">The ID of a Google My Business location.</param>
        /// <param name="keywordThemeInfos">A list of KeywordThemeInfos.</param>
        /// <returns>A daily budget amount in micros.</returns>
        private long GetBudgetSuggestion(GoogleAdsClient client, long customerId, long?
            businessLocationId, IEnumerable<KeywordThemeInfo> keywordThemeInfos)
        {
            SmartCampaignSuggestServiceClient smartCampaignSuggestServiceClient = client.GetService
                (Services.V8.SmartCampaignSuggestService);

            SuggestSmartCampaignBudgetOptionsRequest request =
                new SuggestSmartCampaignBudgetOptionsRequest
                {
                    CustomerId = customerId.ToString(),
                    // You can retrieve suggestions for an existing campaign by setting the
                    // "Campaign" field of the request to the resource name of a campaign and
                    // leaving the rest of the request fields below unset:
                    // Campaign = "INSERT_CAMPAIGN_RESOURCE_NAME_HERE",

                    // Since these suggestions are for a new campaign, we're going to use the
                    // SuggestionInfo field instead.
                    SuggestionInfo = new SmartCampaignSuggestionInfo
                    {
                        // Add the URL of the campaign's landing page.
                        FinalUrl = LANDING_PAGE_URL,
                        // Construct location information using the given geo target constant. It's
                        // also possible to provide a geographic proximity using the "proximity"
                        // field on suggestion_info, for example:
                        // Proximity = new ProximityInfo
                        // {
                        //     Address = new AddressInfo
                        //     {
                        //         PostalCode = "INSERT_POSTAL_CODE",
                        //         ProvinceCode = "INSERT_PROVINCE_CODE",
                        //         CountryCode = "INSERT_COUNTRY_CODE",
                        //         ProvinceName = "INSERT_PROVINCE_NAME",
                        //         StreetAddress = "INSERT_STREET_ADDRESS",
                        //         StreetAddress2 = "INSERT_STREET_ADDRESS_2",
                        //         CityName = "INSERT_CITY_NAME"
                        //     },
                        //     Radius = Double.Parse("INSERT_RADIUS"),
                        //     RadiusUnits = ProximityRadiusUnits.Kilometers
                        // }
                        // For more information on proximities see:
                        // https://developers.google.com/google-ads/api/reference/rpc/latest/ProximityInfo
                        LocationList = new SmartCampaignSuggestionInfo.Types.LocationList()
                    }
                };

            LocationInfo locationInfo = new LocationInfo
            {
                // Set the location to the resource name of the given geo target constant.
                GeoTargetConstant = ResourceNames.GeoTargetConstant(GEO_TARGET_CONSTANT)
            };

            // Add the LocationInfo object to the list of locations on the SuggestionInfo object.
            // You have the option of providing multiple locations when using location-based
            // suggestions.
            request.SuggestionInfo.LocationList.Locations.Add(locationInfo);

            // Add the KeywordThemeInfo objects to the SuggestionInfo object.
            request.SuggestionInfo.KeywordThemes.Add(keywordThemeInfos);

            // Add the GMB location ID if provided.
            if (businessLocationId.HasValue)
            {
                request.SuggestionInfo.BusinessLocationId = businessLocationId.Value;
            }

            // Add a schedule detailing which days of the week the business is open. This schedule
            // describes a business that is open on Mondays from 9:00 AM to 5:00 PM.
            AdScheduleInfo adScheduleInfo = new AdScheduleInfo
            {
                // Set the day of this schedule as Monday.
                DayOfWeek = DayOfWeekEnum.Types.DayOfWeek.Monday,
                // Set the start hour to 9 AM.
                StartHour = 9,
                // Set the end hour to 5 PM.
                EndHour = 17,
                // Set the start and end minutes to zero.
                StartMinute = MinuteOfHourEnum.Types.MinuteOfHour.Zero,
                EndMinute = MinuteOfHourEnum.Types.MinuteOfHour.Zero
            };

            request.SuggestionInfo.AdSchedules.Add(adScheduleInfo);

            // Issue a request to retrieve a budget suggestion.
            SuggestSmartCampaignBudgetOptionsResponse response =
                smartCampaignSuggestServiceClient.SuggestSmartCampaignBudgetOptions(request);

            // Three tiers of options will be returned: "low", "high", and "recommended".
            // Here we will use the "recommended" option. The amount is specified in micros, where
            // one million is equivalent to one currency unit.
            Console.WriteLine($"A daily budget amount of {response.Recommended.DailyAmountMicros}" +
                $" was suggested, garnering an estimated minimum of " +
                $"{response.Recommended.Metrics.MinDailyClicks} clicks and an estimated maximum " +
                $"of {response.Recommended.Metrics.MaxDailyClicks} clicks per day.");

            return response.Recommended.DailyAmountMicros;
        }

        /// <summary>
        /// Creates a MutateOperation that creates a new CampaignBudget.
        /// A temporary ID will be assigned to this campaign budget so that it can be referenced by
        /// other objects being created in the same Mutate request.
        /// </summary>
        /// <param name="customerId">The Google Ads customer ID.</param>
        /// <param name="suggestedBudgetAmount">A daily amount budget in micros.</param>
        /// <returns>A MutateOperation that creates a CampaignBudget</returns>
        private MutateOperation CreateCampaignBudgetOperation(long customerId,
            long suggestedBudgetAmount)
        {
            return new MutateOperation
            {
                CampaignBudgetOperation = new CampaignBudgetOperation
                {
                    Create = new CampaignBudget
                    {
                        Name = $"Smart campaign budget #{ExampleUtilities.GetRandomString()}",
                        // A budget used for Smart campaigns must have the type SMART_CAMPAIGN.
                        Type = BudgetType.SmartCampaign,
                        // The suggested budget amount from the SmartCampaignSuggestService is a
                        // daily budget. We don't need to specify that here, because the budget
                        // period already defaults to DAILY.
                        AmountMicros = suggestedBudgetAmount,
                        // Set a temporary ID in the budget's resource name so it can be referenced
                        // by the campaign in later steps.
                        ResourceName = ResourceNames.CampaignBudget(customerId, BUDGET_TEMPORARY_ID)
                    }
                }
            };
        }

        /// <summary>
        /// Creates a MutateOperation that creates a new Smart campaign.
        /// A temporary ID will be assigned to this campaign so that it can be referenced by other
        /// objects being created in the same Mutate request.
        /// </summary>
        /// <param name="customerId">The Google Ads customer ID.</param>
        /// <returns>A MutateOperation that creates a campaign.</returns>
        private MutateOperation CreateSmartCampaignOperation(long customerId)
        {
            return new MutateOperation
            {
                CampaignOperation = new CampaignOperation
                {
                    Create = new Campaign
                    {
                        Name = $"Smart campaign #{ExampleUtilities.GetRandomString()}",
                        // Set the campaign status as PAUSED. The campaign is the only entity in the
                        // mutate request that should have its status set.
                        Status = CampaignStatus.Paused,
                        // AdvertisingChannelType must be SMART.
                        AdvertisingChannelType = AdvertisingChannelType.Smart,
                        // AdvertisingChannelSubType must be SMART_CAMPAIGN.
                        AdvertisingChannelSubType = AdvertisingChannelSubType.SmartCampaign,
                        // Assign the resource name with a temporary ID.
                        ResourceName =
                            ResourceNames.Campaign(customerId, SMART_CAMPAIGN_TEMPORARY_ID),
                        // Set the budget using the given budget resource name.
                        CampaignBudget =
                            ResourceNames.CampaignBudget(customerId, BUDGET_TEMPORARY_ID)
                    }
                }
            };
        }

        /// <summary>
        /// Creates a MutateOperation to create a new SmartCampaignSetting. SmartCampaignSettings
        /// are unique in that they only support UPDATE operations, which are used to update and
        /// create them. Below we will use a temporary ID in the resource name to associate it with
        /// the campaign created in the previous step.
        /// </summary>
        /// <param name="customerId">The Google Ads customer ID.</param>
        /// <param name="businessLocationId">The ID of a Google My Business location.</param>
        /// <param name="businessName">The name of a Google My Business business.</param>
        /// <returns>A MutateOperation that creates a SmartCampaignSetting.</returns>
        private MutateOperation CreateSmartCampaignSettingOperation(long customerId,
            long? businessLocationId, string businessName)
        {
            SmartCampaignSetting smartCampaignSetting = new SmartCampaignSetting
            {
                // Set a temporary ID in the campaign setting's resource name to associate it with
                // the campaign created in the previous step.
                ResourceName =
                    ResourceNames.SmartCampaignSetting(customerId, SMART_CAMPAIGN_TEMPORARY_ID),
                // Below we configure the SmartCampaignSetting using many of the same details used
                // to generate a budget suggestion.
                PhoneNumber = new SmartCampaignSetting.Types.PhoneNumber
                {
                    CountryCode = COUNTRY_CODE,
                    PhoneNumber_ = PHONE_NUMBER
                },
                FinalUrl = LANDING_PAGE_URL,
                AdvertisingLanguageCode = LANGUAGE_CODE
            };

            // Either a business location ID or a business name must be added to the
            // SmartCampaignSetting.
            if (businessLocationId.HasValue)
            {
                smartCampaignSetting.BusinessLocationId = businessLocationId.Value;
            }
            else
            {
                smartCampaignSetting.BusinessName = businessName;
            }

            return new MutateOperation
            {
                SmartCampaignSettingOperation = new SmartCampaignSettingOperation
                {
                    Update = smartCampaignSetting,
                    // Set the update mask on the operation. This is required since the smart
                    // campaign setting is created in an UPDATE operation. Here the update mask will
                    // be a list of all the fields that were set on the SmartCampaignSetting.
                    UpdateMask = FieldMasks.AllSetFieldsOf(smartCampaignSetting)
                }
            };
        }

        /// <summary>
        /// Creates a list of MutateOperations that create new campaign criteria.
        /// </summary>
        /// <param name="customerId">The Google Ads customer ID.</param>
        /// <param name="keywordThemeInfos">A list of KeywordThemeInfos.</param>
        /// <returns>A list of MutateOperations that create new campaign criteria.</returns>
        private IEnumerable<MutateOperation> CreateCampaignCriterionOperations(long customerId,
            IEnumerable<KeywordThemeInfo> keywordThemeInfos)
        {
            return keywordThemeInfos.Select(keywordThemeInfo => new MutateOperation
            {
                CampaignCriterionOperation = new CampaignCriterionOperation
                {
                    Create = new CampaignCriterion
                    {
                        // Set the campaign ID to a temporary ID.
                        Campaign = ResourceNames.Campaign(customerId, SMART_CAMPAIGN_TEMPORARY_ID),
                        // Set the criterion type to KeywordTheme.
                        Type = CriterionType.KeywordTheme,
                        // Set the keyword theme to each KeywordThemeInfo in turn.
                        KeywordTheme = keywordThemeInfo,
                    }
                }
            }).ToList();
        }

        /// <summary>
        /// Creates a MutateOperation that creates a new ad group.
        /// A temporary ID will be used in the campaign resource name for this ad group to associate
        /// it with the Smart campaign created in earlier steps. A temporary ID will also be used
        /// for its own resource name so that we can associate an ad group ad with it later in the
        /// process.
        /// Only one ad group can be created for a given Smart campaign.
        /// </summary>
        /// <param name="customerId">The Google Ads customer ID.</param>
        /// <returns>A MutateOperation that creates a new ad group.</returns>
        private MutateOperation CreateAdGroupOperation(long customerId)
        {
            return new MutateOperation
            {
                AdGroupOperation = new AdGroupOperation
                {
                    Create = new AdGroup
                    {
                        // Set the ad group ID to a temporary ID.
                        ResourceName = ResourceNames.AdGroup(customerId, AD_GROUP_TEMPORARY_ID),
                        Name = $"Smart campaign ad group #{ExampleUtilities.GetRandomString()}",
                        // Set the campaign ID to a temporary ID.
                        Campaign = ResourceNames.Campaign(customerId, SMART_CAMPAIGN_TEMPORARY_ID),
                        // The ad group type must be SmartCampaignAds.
                        Type = AdGroupType.SmartCampaignAds
                    }
                }
            };
        }

        /// <summary>
        /// Creates a MutateOperation that creates a new ad group ad.
        /// A temporary ID will be used in the ad group resource name for this ad group ad to
        /// associate it with the ad group created in earlier steps.
        /// </summary>
        /// <param name="customerId">The Google Ads customer ID.</param>
        /// <returns>A MutateOperation that creates a new ad group ad.</returns>
        private MutateOperation CreateAdGroupAdOperation(long customerId)
        {
            AdGroupAd adGroupAd = new AdGroupAd
            {
                AdGroup = ResourceNames.AdGroup(customerId, AD_GROUP_TEMPORARY_ID),
                Ad = new Ad
                {
                    SmartCampaignAd = new SmartCampaignAdInfo()
                }
            };

            // At most, three headlines can be specified for a Smart campaign ad.
            adGroupAd.Ad.SmartCampaignAd.Headlines.Add(new[]
                {
                    new AdTextAsset
                    {
                        Text = "Headline number one"
                    },
                    new AdTextAsset
                    {
                        Text = "Headline number two"
                    },
                    new AdTextAsset
                    {
                        Text = "Headline number three"
                    }
                }
            );

            // At most, two descriptions can be specified for a Smart campaign ad.
            adGroupAd.Ad.SmartCampaignAd.Descriptions.Add(new[]
                {
                    new AdTextAsset
                    {
                        Text = "Description number one"
                    },
                    new AdTextAsset
                    {
                        Text = "Description number two"
                    },
                }
            );

            return new MutateOperation
            {
                AdGroupAdOperation = new AdGroupAdOperation
                {
                    Create = adGroupAd
                }
            };
        }

        /// <summary>
        /// Prints the details of a MutateGoogleAdsResponse. Parses the "response" oneof field name
        /// and uses it to extract the new entity's name and resource name.
        /// </summary>
        /// <param name="response">A MutateGoogleAdsResponse instance.</param>
        private void PrintResponseDetails(MutateGoogleAdsResponse response)
        {
            // Parse the Mutate response to print details about the entities that were created
            // in the request.
            foreach (MutateOperationResponse operationResponse in response.MutateOperationResponses)
            {
                string resourceName = "<not found>";

                string entityName = operationResponse.ResponseCase.ToString();
                // Trim the substring "Result" from the end of the entity name.
                entityName = entityName.Remove(entityName.Length - 6);

                switch (operationResponse.ResponseCase)
                {
                    case MutateOperationResponse.ResponseOneofCase.AdGroupResult:
                        resourceName = operationResponse.AdGroupResult.ResourceName;
                        break;
                    case MutateOperationResponse.ResponseOneofCase.AdGroupAdResult:
                        resourceName = operationResponse.AdGroupAdResult.ResourceName;
                        break;
                    case MutateOperationResponse.ResponseOneofCase.CampaignResult:
                        resourceName = operationResponse.CampaignResult.ResourceName;
                        break;
                    case MutateOperationResponse.ResponseOneofCase.CampaignBudgetResult:
                        resourceName = operationResponse.CampaignBudgetResult.ResourceName;
                        break;
                    case MutateOperationResponse.ResponseOneofCase.CampaignCriterionResult:
                        resourceName = operationResponse.CampaignCriterionResult.ResourceName;
                        break;
                    case MutateOperationResponse.ResponseOneofCase.SmartCampaignSettingResult:
                        resourceName = operationResponse.SmartCampaignSettingResult.ResourceName;
                        break;
                }

                Console.WriteLine(
                    $"Created a(n) {entityName} with resource name: '{resourceName}'.");
            }
        }
    }
}
      
      

PHP

<?php

/**
 * Copyright 2021 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 GetOpt\GetOpt;
use Google\Ads\GoogleAds\Examples\Utils\ArgumentNames;
use Google\Ads\GoogleAds\Examples\Utils\ArgumentParser;
use Google\Ads\GoogleAds\Examples\Utils\Helper;
use Google\Ads\GoogleAds\Lib\OAuth2TokenBuilder;
use Google\Ads\GoogleAds\Lib\V8\GoogleAdsClient;
use Google\Ads\GoogleAds\Lib\V8\GoogleAdsClientBuilder;
use Google\Ads\GoogleAds\Lib\V8\GoogleAdsException;
use Google\Ads\GoogleAds\Util\FieldMasks;
use Google\Ads\GoogleAds\Util\V8\ResourceNames;
use Google\Ads\GoogleAds\V8\Common\AdScheduleInfo;
use Google\Ads\GoogleAds\V8\Common\AdTextAsset;
use Google\Ads\GoogleAds\V8\Common\KeywordThemeInfo;
use Google\Ads\GoogleAds\V8\Common\LocationInfo;
use Google\Ads\GoogleAds\V8\Common\SmartCampaignAdInfo;
use Google\Ads\GoogleAds\V8\Enums\AdGroupTypeEnum\AdGroupType;
use Google\Ads\GoogleAds\V8\Enums\AdTypeEnum\AdType;
use Google\Ads\GoogleAds\V8\Enums\AdvertisingChannelSubTypeEnum\AdvertisingChannelSubType;
use Google\Ads\GoogleAds\V8\Enums\AdvertisingChannelTypeEnum\AdvertisingChannelType;
use Google\Ads\GoogleAds\V8\Enums\BudgetDeliveryMethodEnum\BudgetDeliveryMethod;
use Google\Ads\GoogleAds\V8\Enums\BudgetTypeEnum\BudgetType;
use Google\Ads\GoogleAds\V8\Enums\CampaignStatusEnum\CampaignStatus;
use Google\Ads\GoogleAds\V8\Enums\CriterionTypeEnum\CriterionType;
use Google\Ads\GoogleAds\V8\Enums\DayOfWeekEnum\DayOfWeek;
use Google\Ads\GoogleAds\V8\Enums\MinuteOfHourEnum\MinuteOfHour;
use Google\Ads\GoogleAds\V8\Errors\GoogleAdsError;
use Google\Ads\GoogleAds\V8\Resources\Ad;
use Google\Ads\GoogleAds\V8\Resources\AdGroup;
use Google\Ads\GoogleAds\V8\Resources\AdGroupAd;
use Google\Ads\GoogleAds\V8\Resources\Campaign;
use Google\Ads\GoogleAds\V8\Resources\CampaignBudget;
use Google\Ads\GoogleAds\V8\Resources\CampaignCriterion;
use Google\Ads\GoogleAds\V8\Resources\KeywordThemeConstant;
use Google\Ads\GoogleAds\V8\Resources\SmartCampaignSetting;
use Google\Ads\GoogleAds\V8\Resources\SmartCampaignSetting\PhoneNumber;
use Google\Ads\GoogleAds\V8\Services\AdGroupAdOperation;
use Google\Ads\GoogleAds\V8\Services\AdGroupOperation;
use Google\Ads\GoogleAds\V8\Services\CampaignBudgetOperation;
use Google\Ads\GoogleAds\V8\Services\CampaignCriterionOperation;
use Google\Ads\GoogleAds\V8\Services\CampaignOperation;
use Google\Ads\GoogleAds\V8\Services\MutateGoogleAdsResponse;
use Google\Ads\GoogleAds\V8\Services\MutateOperation;
use Google\Ads\GoogleAds\V8\Services\MutateOperationResponse;
use Google\Ads\GoogleAds\V8\Services\SmartCampaignSettingOperation;
use Google\Ads\GoogleAds\V8\Services\SmartCampaignSuggestionInfo;
use Google\Ads\GoogleAds\V8\Services\SmartCampaignSuggestionInfo\LocationList;
use Google\ApiCore\ApiException;
use InvalidArgumentException;

/**
 * This example adds a Smart campaign.
 *
 * More details on Smart campaigns can be found here:
 * https://support.google.com/google-ads/answer/7652860
 */
class AddSmartCampaign
{
    private const CUSTOMER_ID = 'INSERT_CUSTOMER_ID_HERE';
    // Optional: Specify the keyword text used to generate a set of keyword themes, which are used
    // to improve the budget suggestion and performance of the Smart campaign.
    private const KEYWORD_TEXT = 'travel';
    // Optional: Specify the ID of a Google My Business (GMB) location. This is required if a
    // business name is not provided. It can be retrieved using the GMB API, see:
    // https://developers.google.com/my-business/reference/rest/v4/accounts.locations.
    private const BUSINESS_LOCATION_ID = null;
    // Optional: Specify the name of a Google My Business (GMB) business. This is required if a
    // business location ID is not provided.
    private const BUSINESS_NAME = null;

    // Geo target constant for New York City.
    private const GEO_TARGET_CONSTANT = '1023191';
    // Country code is a two-letter ISO-3166 code, for a list of all codes see:
    // https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-16
    private const COUNTRY_CODE = 'US';
    // For a list of all language codes, see:
    // https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-7
    private const LANGUAGE_CODE = 'en';
    private const LANDING_PAGE_URL = 'http://www.example.com';
    private const PHONE_NUMBER = '555-555-5555';
    private const BUDGET_TEMPORARY_ID = '-1';
    private const SMART_CAMPAIGN_TEMPORARY_ID = '-2';
    private const AD_GROUP_TEMPORARY_ID = '-3';

    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::KEYWORD_TEXT => GetOpt::OPTIONAL_ARGUMENT,
            ArgumentNames::BUSINESS_LOCATION_ID => GetOpt::OPTIONAL_ARGUMENT,
            ArgumentNames::BUSINESS_NAME => GetOpt::OPTIONAL_ARGUMENT
        ]);
        $businessLocationId = $options[ArgumentNames::BUSINESS_LOCATION_ID] ?:
            self::BUSINESS_LOCATION_ID;
        $businessName = $options[ArgumentNames::BUSINESS_NAME] ?: self::BUSINESS_NAME;
        if ($businessLocationId && $businessName) {
            throw new InvalidArgumentException(
                'Both the business location ID and business name are provided but they are '
                . 'mutually exclusive. Please only set a value for one of them.'
            );
        }
        if (!$businessLocationId && !$businessName) {
            throw new InvalidArgumentException(
                'Neither the business location ID nor the business name are provided. Please set '
                . 'a value for one of them.'
            );
        }

        // 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::KEYWORD_TEXT] ?: self::KEYWORD_TEXT,
                $businessLocationId,
                $businessName
            );
        } 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 string $keywordText a keyword text used for generating keyword themes
     * @param string|null $businessLocationId the ID of a Google My Business location
     * @param string|null $businessName the name of a Google My Business
     */
    public static function runExample(
        GoogleAdsClient $googleAdsClient,
        int $customerId,
        string $keywordText,
        ?string $businessLocationId,
        ?string $businessName
    ) {
        $keywordThemeConstants = self::getKeywordThemeConstants($googleAdsClient, $keywordText);
        // Maps the list of KeywordThemeConstants to KeywordThemeInfos.
        $keywordThemeInfos = array_map(function (KeywordThemeConstant $constant) {
            return new KeywordThemeInfo([
                'keyword_theme_constant' => $constant->getResourceName()
            ]);
        }, $keywordThemeConstants);
        $suggestedBudgetAmount = self::getBudgetSuggestion(
            $googleAdsClient,
            $customerId,
            $businessLocationId,
            $keywordThemeInfos
        );

        // The below methods create and return MutateOperations that we later provide to the
        // GoogleAdsService.Mutate method in order to create the entities in a single request.
        // Since the entities for a Smart campaign are closely tied to one-another it's considered
        // a best practice to create them in a single Mutate request so they all complete
        // successfully or fail entirely, leaving no orphaned entities.
        // See: https://developers.google.com/google-ads/api/docs/mutating/overview.
        $campaignBudgetOperation = self::createCampaignBudgetOperation(
            $customerId,
            $suggestedBudgetAmount
        );
        $smartCmpaignOperation = self::createSmartCampaignOperation($customerId);
        $smartCampaignSettingOperation = self::createSmartCampaignSettingOperation(
            $customerId,
            $businessLocationId,
            $businessName
        );
        $campaignCriterionOperations = self::createCampaignCriterionOperations(
            $customerId,
            $keywordThemeInfos
        );
        $adGroupOperation = self::createAdGroupOperation($customerId);
        $adGroupAdOperation = self::createAdGroupAdOperation($customerId);

        // Issues a single mutate request to add the entities.
        $googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient();
        $response = $googleAdsServiceClient->mutate(
            $customerId,
            // It's important to create these entities in this order because they depend on
            // each other, for example the SmartCampaignSetting and ad group depend on the
            // campaign, and the ad group ad depends on the ad group.
            array_merge(
                [
                    $campaignBudgetOperation,
                    $smartCmpaignOperation,
                    $smartCampaignSettingOperation,
                ],
                $campaignCriterionOperations,
                [
                    $adGroupOperation,
                    $adGroupAdOperation
                ]
            )
        );

        self::printResponseDetails($response);
    }

    /**
     * Retrieves KeywordThemeConstants for the given criteria.
     *
     * @param GoogleAdsClient $googleAdsClient the Google Ads API client
     * @param string $keywordText a keyword text used for generating keyword themes
     * @return KeywordThemeConstant[] a list of KeywordThemeConstants
     */
    private static function getKeywordThemeConstants(
        GoogleAdsClient $googleAdsClient,
        string $keywordText
    ): array {
        $keywordThemeConstantService = $googleAdsClient->getKeywordThemeConstantServiceClient();

        // Issues a request to retrieve the keyword theme constants.
        $response = $keywordThemeConstantService->suggestKeywordThemeConstants([
            'queryText' => $keywordText,
            'countryCode' => self::COUNTRY_CODE,
            'languageCode' => self::LANGUAGE_CODE
        ]);

        printf(
            "Retrieved %d keyword theme constants using the keyword: '%s'.%s",
            $response->getKeywordThemeConstants()->count(),
            $keywordText,
            PHP_EOL
        );
        return iterator_to_array($response->getKeywordThemeConstants()->getIterator());
    }

    /**
     * Retrieves a suggested budget amount for a new budget.
     *
     * Using the SmartCampaignSuggestService to determine a daily budget for new and existing
     * Smart campaigns is highly recommended because it helps the campaigns achieve optimal
     * performance.
     *
     * @param GoogleAdsClient $googleAdsClient the Google Ads API client
     * @param int $customerId the customer ID
     * @param string|null $businessLocationId the ID of a Google My Business location
     * @param KeywordThemeInfo[] $keywordThemeInfos a list of KeywordThemeInfos
     * @return int a daily budget amount in micros
     */
    private static function getBudgetSuggestion(
        GoogleAdsClient $googleAdsClient,
        int $customerId,
        ?string $businessLocationId,
        array $keywordThemeInfos
    ): int {
        $suggestionData = [
            // You can retrieve suggestions for an existing campaign by setting the "campaign"
            // field equal to the resource name of a campaign and leaving the rest below unset:
            // 'campaign' => 'INSERT_CAMPAIGN_RESOURCE_NAME_HERE'

            // Since these suggestions are for a new campaign, we're going to use the
            // suggestion_info field instead.
            'suggestion_info' => new SmartCampaignSuggestionInfo([
                // Adds the URL of the campaign's landing page.
                'final_url' => self::LANDING_PAGE_URL,

                // Constructs location information using the given geo target constant. It's also
                // possible to provide a geographic proximity using the "proximity" field,
                // for example:
                //
                // 'proximity' => new ProximityInfo([
                //     'address' => mew AddressInfo([
                //         'post_code' => INSERT_POSTAL_CODE,
                //         'province_code' => INSERT_PROVINCE_CODE,
                //         'country_code' => INSERT_COUNTRY_CODE,
                //         'province_name' => INSERT_PROVINCE_NAME,
                //         'street_address' => INSERT_STREET_ADDRESS,
                //         'street_address2' => INSERT_STREET_ADDRESS_2,
                //         'city_name' => INSERT_CITY_NAME
                //     ]),
                //     'radius' => INSERT_RADIUS,
                //     'radius_units' => INSERT_RADIUS_UNITS
                // ])
                //
                // For more information on proximities see:
                // https://developers.google.com/google-ads/api/reference/rpc/latest/ProximityInfo

                // Adds LocationInfo objects to the list of locations. You have the option of
                // providing multiple locations when using location-based suggestions.
                'location_list' => new LocationList([
                    // Sets one location to the resource name of the given geo target constant.
                    'locations' => [new LocationInfo([
                        'geo_target_constant' => ResourceNames::forGeoTargetConstant(
                            self::GEO_TARGET_CONSTANT
                        )
                    ])]
                ]),

                // Adds the KeywordThemeInfo objects.
                'keyword_themes' => $keywordThemeInfos,

                // Adds a schedule detailing which days of the week the business is open.
                // This schedule describes a schedule in which the business is open on
                // Mondays from 9am to 5pm.
                'ad_schedules' => [new AdScheduleInfo([
                    // Sets the day of this schedule as Monday.
                    'day_of_week' => DayOfWeek::MONDAY,
                    // Sets the start hour to 9am.
                    'start_hour' => 9,
                    // Sets the end hour to 5pm.
                    'end_hour' => 17,
                    // Sets the start and end minute of zero, for example: 9:00 and 5:00.
                    'start_minute' => MinuteOfHour::ZERO,
                    'end_minute' => MinuteOfHour::ZERO
                ])]
            ])
        ];

        // Adds the GMB location ID if it is provided and if the "suggestion_info" field is set.
        if (array_key_exists('suggestion_info', $suggestionData) && $businessLocationId) {
            $suggestionData['suggestion_info']->setBusinessLocationId($businessLocationId);
        }

        // Issues a request to retrieve a budget suggestion.
        $smartCampaignSuggestService = $googleAdsClient->getSmartCampaignSuggestServiceClient();
        $response = $smartCampaignSuggestService->suggestSmartCampaignBudgetOptions(
            $customerId,
            $suggestionData
        );

        // Three tiers of options will be returned, a "low", "high" and "recommended". Here we will
        // use the "recommended" option. The amount is specified in micros, where one million is
        // equivalent to one currency unit.
        $recommendation = $response->getRecommended();
        printf(
            "A daily budget amount of %d micros was suggested, garnering an estimated minimum of "
            . "%d clicks and an estimated maximum of %d per day.%s",
            $recommendation->getDailyAmountMicros(),
            $recommendation->getMetrics()->getMinDailyClicks(),
            $recommendation->getMetrics()->getMaxDailyClicks(),
            PHP_EOL
        );

        return $recommendation->getDailyAmountMicros();
    }

    /**
     * Creates a MutateOperation that creates a new CampaignBudget.
     *
     * A temporary ID will be assigned to this campaign budget so that it can be referenced by
     * other objects being created in the same Mutate request.
     *
     * @param int $customerId the customer ID
     * @param int $suggestedBudgetAmount a daily budget amount in micros
     * @return MutateOperation a MutateOperation that creates a CampaignBudget
     */
    private static function createCampaignBudgetOperation(
        int $customerId,
        int $suggestedBudgetAmount
    ): MutateOperation {
        // Creates the campaign budget object.
        $campaignBudget = new CampaignBudget([
            'name' => "Smart campaign budget #" . Helper::getPrintableDatetime(),
            // A budget used for Smart campaigns must have the type SMART_CAMPAIGN.
            'type' => BudgetType::SMART_CAMPAIGN,
            // The suggested budget amount from the SmartCampaignSuggestService is a daily budget.
            // We don't need to specify that here, because the budget period already defaults to
            // DAILY.
            'amount_micros' => $suggestedBudgetAmount,
            // Sets a temporary ID in the budget's resource name so it can be referenced by the
            // campaign in later steps.
            'resource_name' =>
                ResourceNames::forCampaignBudget($customerId, self::BUDGET_TEMPORARY_ID)
        ]);

        // Creates the MutateOperation that creates the campaign budget.
        return new MutateOperation([
            'campaign_budget_operation' => new CampaignBudgetOperation([
                'create' => $campaignBudget
            ])
        ]);
    }

    /**
     * Creates a MutateOperation that creates a new Smart campaign.
     *
     * A temporary ID will be assigned to this campaign so that it can be referenced by other
     * objects being created in the same Mutate request.
     *
     * @param int $customerId the customer ID
     * @return MutateOperation a MutateOperation that creates a campaign
     */
    private static function createSmartCampaignOperation(int $customerId): MutateOperation
    {
        // Creates the campaign object.
        $campaign = new Campaign([
            'name' => "Smart campaign #" . Helper::getPrintableDatetime(),
            // Sets the campaign status as PAUSED. The campaign is the only entity in the mutate
            // request that should have its' status set.
            'status' => CampaignStatus::PAUSED,
            // The advertising channel type is required to be SMART.
            'advertising_channel_type' => AdvertisingChannelType::SMART,
            // The advertising channel sub type is required to be SMART_CAMPAIGN.
            'advertising_channel_sub_type' => AdvertisingChannelSubType::SMART_CAMPAIGN,
            // Assigns the resource name with a temporary ID.
            'resource_name' =>
                ResourceNames::forCampaign($customerId, self::SMART_CAMPAIGN_TEMPORARY_ID),
            // Sets the budget using the given budget resource name.
            'campaign_budget' =>
                ResourceNames::forCampaignBudget($customerId, self::BUDGET_TEMPORARY_ID)
        ]);

        // Creates the MutateOperation that creates the campaign.
        return new MutateOperation([
            'campaign_operation' => new CampaignOperation(['create' => $campaign])
        ]);
    }

    /**
     * Creates a MutateOperation to create a new SmartCampaignSetting.
     *
     * SmartCampaignSettings are unique in that they only support UPDATE operations, which are
     * used to update and create them. Below we will use a temporary ID in the resource name to
     * associate it with the campaign created in the previous step.
     *
     * @param int $customerId the customer ID
     * @param string|null $businessLocationId the ID of a Google My Business location
     * @param string|null $businessName the name of a Google My Business
     * @return MutateOperation a MutateOperation that creates a SmartCampaignSetting
     */
    private static function createSmartCampaignSettingOperation(
        int $customerId,
        ?string $businessLocationId,
        ?string $businessName
    ): MutateOperation {
        // Creates the smart campaign setting object.
        $smartCampaignSetting = new SmartCampaignSetting([
            // Sets a temporary ID in the campaign setting's resource name to associate it with
            // the campaign created in the previous step.
            'resource_name' => ResourceNames::forSmartCampaignSetting(
                $customerId,
                self::SMART_CAMPAIGN_TEMPORARY_ID
            ),
            // Below we configure the SmartCampaignSetting using many of the same details used to
            // generate a budget suggestion.
            'phone_number' => new PhoneNumber([
                'country_code' => self::COUNTRY_CODE,
                'phone_number' => self::PHONE_NUMBER
            ]),
            'final_url' => self::LANDING_PAGE_URL,
            'advertising_language_code' => self::LANGUAGE_CODE,
        ]);

        // It's required that either a business location ID or a business name is added to the
        // SmartCampaignSetting.
        if ($businessLocationId) {
            $smartCampaignSetting->setBusinessLocationId($businessLocationId);
        } else {
            $smartCampaignSetting->setBusinessName($businessName);
        }

        // Creates the MutateOperation that creates the smart campaign setting with an update.
        return new MutateOperation([
            'smart_campaign_setting_operation' => new SmartCampaignSettingOperation([
                'update' => $smartCampaignSetting,
                // Sets the update mask on the operation. This is required since the smart campaign
                // setting is created in an UPDATE operation. Here the update mask will be a list
                // of all the fields that were set on the SmartCampaignSetting.
                'update_mask' => FieldMasks::allSetFieldsOf($smartCampaignSetting)
            ])
        ]);
    }

    /**
     * Creates a list of MutateOperations that create new campaign criteria.
     *
     * @param int $customerId the customer ID
     * @param KeywordThemeInfo[] $keywordThemeInfos a list of KeywordThemeInfos
     * @return MutateOperation[] a list of MutateOperations that create new campaign criteria
     */
    private static function createCampaignCriterionOperations(
        int $customerId,
        array $keywordThemeInfos
    ): array {
        $operations = [];
        foreach ($keywordThemeInfos as $info) {
            // Creates the campaign criterion object.
            $campaignCriterion = new CampaignCriterion([
                // Sets the campaign ID to a temporary ID.
                'campaign' =>
                    ResourceNames::forCampaign($customerId, self::SMART_CAMPAIGN_TEMPORARY_ID),
                // Sets the criterion type to KEYWORD_THEME.
                'type' => CriterionType::KEYWORD_THEME,
                // Sets the keyword theme to the given KeywordThemeInfo.
                'keyword_theme' => $info
            ]);

            // Creates the MutateOperation that creates the campaign criterion and adds it to the
            // list of operations.
            $operations[] = new MutateOperation([
                'campaign_criterion_operation' => new CampaignCriterionOperation([
                    'create' => $campaignCriterion
                ])
            ]);
        }

        return $operations;
    }

    /**
     * Creates a MutateOperation that creates a new ad group.
     *
     * A temporary ID will be used in the campaign resource name for this ad group to associate
     * it with the Smart campaign created in earlier steps. A temporary ID will also be used for
     * its own resource name so that we can associate an ad group ad with it later in the process.
     *
     * Only one ad group can be created for a given Smart campaign.
     *
     * @param int $customerId the customer ID
     * @return MutateOperation a MutateOperation that creates a new ad group
     */
    private static function createAdGroupOperation(int $customerId): MutateOperation
    {
        // Creates the ad group object.
        $adGroup = new AdGroup([
            // Sets the ad group ID to a temporary ID.
            'resource_name' => ResourceNames::forAdGroup($customerId, self::AD_GROUP_TEMPORARY_ID),
            'name' => "Smart campaign ad group #" . Helper::getPrintableDatetime(),
            // Sets the campaign ID to a temporary ID.
            'campaign' =>
                ResourceNames::forCampaign($customerId, self::SMART_CAMPAIGN_TEMPORARY_ID),
            // The ad group type must be set to SMART_CAMPAIGN_ADS.
            'type' => AdGroupType::SMART_CAMPAIGN_ADS
        ]);

        // Creates the MutateOperation that creates the ad group.
        return new MutateOperation([
            'ad_group_operation' => new AdGroupOperation(['create' => $adGroup])
        ]);
    }

    /**
     * Creates a MutateOperation that creates a new ad group ad.
     *
     * A temporary ID will be used in the ad group resource name for this ad group ad to associate
     * it with the ad group created in earlier steps.
     *
     * @param int $customerId the customer ID
     * @return MutateOperation a MutateOperation that creates a new ad group ad
     */
    private static function createAdGroupAdOperation(int $customerId): MutateOperation
    {
        // Creates the ad group ad object.
        $adGroupAd = new AdGroupAd([
            // Sets the ad group ID to a temporary ID.
            'ad_group' => ResourceNames::forAdGroup($customerId, self::AD_GROUP_TEMPORARY_ID),
            'ad' => new Ad([
                // Sets the type to SMART_CAMPAIGN_AD.
                'type' => AdType::SMART_CAMPAIGN_AD,
                'smart_campaign_ad' => new SmartCampaignAdInfo([
                    // At most, three headlines can be specified for a Smart campaign ad.
                    'headlines' => [
                        new AdTextAsset(['text' => 'Headline number one']),
                        new AdTextAsset(['text' => 'Headline number two']),
                        new AdTextAsset(['text' => 'Headline number three'])
                    ],
                    // At most, two descriptions can be specified for a Smart campaign ad.
                    'descriptions' => [
                        new AdTextAsset(['text' => 'Description number one']),
                        new AdTextAsset(['text' => 'Description number two'])
                    ]
                ])
            ])
        ]);

        // Creates the MutateOperation that creates the ad group ad.
        return new MutateOperation([
            'ad_group_ad_operation' => new AdGroupAdOperation(['create' => $adGroupAd])
        ]);
    }

    /**
     * Prints the details of a MutateGoogleAdsResponse.
     *
     * It only covers the expected types of operation result.
     *
     * @param MutateGoogleAdsResponse $response a MutateGoogleAdsResponse object
     */
    private static function printResponseDetails(MutateGoogleAdsResponse $response)
    {
        // Parses the Mutate response to print details about the entities that were created by the
        // request.
        /** @var MutateOperationResponse $result */
        foreach ($response->getMutateOperationResponses() as $result) {
            $resourceType = "unrecognized";
            $resourceName = "not found";
            if ($result->hasCampaignBudgetResult()) {
                $resourceType = "CampaignBudget";
                $resourceName = $result->getCampaignBudgetResult()->getResourceName();
            } else if ($result->hasCampaignResult()) {
                $resourceType = "Campaign";
                $resourceName = $result->getCampaignResult()->getResourceName();
            } else if ($result->hasSmartCampaignSettingResult()) {
                $resourceType = "SmartCampaignSetting";
                $resourceName = $result->getSmartCampaignSettingResult()->getResourceName();
            } else if ($result->getAdGroupResult()) {
                $resourceType = "AdGroup";
                $resourceName = $result->getAdGroupResult()->getResourceName();
            } else if ($result->getAdGroupAdResult()) {
                $resourceType = "AdGroupAd";
                $resourceName = $result->getAdGroupAdResult()->getResourceName();
            } else if ($result->getCampaignCriterionResult()) {
                $resourceType = "CampaignCriterion";
                $resourceName = $result->getCampaignCriterionResult()->getResourceName();
            }
            printf(
                "Created a(n) '%s' entity with resource_name: '%s'.%s",
                $resourceType,
                $resourceName,
                PHP_EOL
            );
        }
    }
}

AddSmartCampaign::main();

      
      

Python

#!/usr/bin/env python
# Copyright 2021 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.
"""This example shows how to create a Smart campaign.

More details on Smart campaigns can be found here:
https://support.google.com/google-ads/answer/7652860
"""


import argparse
import sys
from uuid import uuid4

from google.ads.googleads.client import GoogleAdsClient
from google.ads.googleads.errors import GoogleAdsException

from google.api_core import protobuf_helpers

_DEFAULT_KEYWORD = "travel"

# Geo target constant for New York City.
_GEO_TARGET_CONSTANT = "1023191"
# Country code is a two-letter ISO-3166 code, for a list of all codes see:
# https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-16
_COUNTRY_CODE = "US"
# For a list of all language codes, see:
# https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-7
_LANGUAGE_CODE = "en"
_LANDING_PAGE_URL = "http://www.example.com"
_PHONE_NUMBER = "555-555-5555"
_BUDGET_TEMPORARY_ID = "-1"
_SMART_CAMPAIGN_TEMPORARY_ID = "-2"
_AD_GROUP_TEMPORARY_ID = "-3"


def main(
    client, customer_id, keyword_text, business_location_id, business_name
):
    """The main method that creates all necessary entities for the example.

    Args:
        client: an initialized GoogleAdsClient instance.
        customer_id: a client customer ID.
        keyword_text: a keyword used for generating keyword themes.
        business_location_id: the ID of a Google My Business location.
        business_name: the name of a Google My Business.
    """
    keyword_theme_constants = _get_keyword_theme_constants(client, keyword_text)
    keyword_theme_infos = _map_keyword_theme_constants_to_infos(
        client, keyword_theme_constants
    )
    suggested_budget_amount = _get_budget_suggestion(
        client,
        customer_id,
        business_location_id,
        keyword_theme_infos,
    )
    # The below methods create and return MutateOperations that we later
    # provide to the GoogleAdsService.Mutate method in order to create the
    # entities in a single request. Since the entities for a Smart campaign
    # are closely tied to one-another it's considered a best practice to
    # create them in a single Mutate request so they all complete successfully
    # or fail entirely, leaving no orphaned entities. See:
    # https://developers.google.com/google-ads/api/docs/mutating/overview
    campaign_budget_operation = _create_campaign_budget_operation(
        client, customer_id, suggested_budget_amount
    )
    smart_campaign_operation = _create_smart_campaign_operation(
        client, customer_id
    )
    smart_campaign_setting_operation = _create_smart_campaign_setting_operation(
        client, customer_id, business_location_id, business_name
    )
    campaign_criterion_operations = _create_campaign_criterion_operations(
        client, customer_id, keyword_theme_infos
    )
    ad_group_operation = _create_ad_group_operation(client, customer_id)
    ad_group_ad_operation = _create_ad_group_ad_operation(client, customer_id)

    googleads_service = client.get_service("GoogleAdsService")

    # Send the operations into a single Mutate request.
    response = googleads_service.mutate(
        customer_id=customer_id,
        mutate_operations=[
            # It's important to create these entities in this order because
            # they depend on each other, for example the SmartCampaignSetting
            # and ad group depend on the campaign, and the ad group ad depends
            # on the ad group.
            campaign_budget_operation,
            smart_campaign_operation,
            smart_campaign_setting_operation,
            # Expand the list of campaign criterion operations into the list of
            # other mutate operations
            *campaign_criterion_operations,
            ad_group_operation,
            ad_group_ad_operation,
        ],
    )

    _print_response_details(response)


def _get_keyword_theme_constants(client, keyword_text):
    """Retrieves KeywordThemeConstants for the given criteria.

    Args:
        client: an initialized GoogleAdsClient instance.
        keyword_text: a keyword used for generating keyword themes.

    Returns:
        a list of KeywordThemeConstants.
    """
    keyword_theme_constant_service = client.get_service(
        "KeywordThemeConstantService"
    )
    request = client.get_type("SuggestKeywordThemeConstantsRequest")
    request.query_text = keyword_text
    request.country_code = _COUNTRY_CODE
    request.language_code = _LANGUAGE_CODE

    response = keyword_theme_constant_service.suggest_keyword_theme_constants(
        request=request
    )

    print(
        f"Retrieved {len(response.keyword_theme_constants)} keyword theme "
        f"constants using the keyword: '{keyword_text}'"
    )
    return response.keyword_theme_constants


def _get_budget_suggestion(
    client,
    customer_id,
    business_location_id,
    keyword_theme_infos,
):
    """Retrieves a suggested budget amount for a new budget.

    Using the SmartCampaignSuggestService to determine a daily budget for new
    and existing Smart campaigns is highly recommended because it helps the
    campaigns achieve optimal performance.

    Args:
        client: an initialized GoogleAdsClient instance.
        customer_id: a client customer ID.
        business_location_id: the ID of a Google My Business location.
        keyword_theme_infos: a list of KeywordThemeInfos.

    Returns:
        a daily budget amount in micros.
    """
    sc_suggest_service = client.get_service("SmartCampaignSuggestService")
    request = client.get_type("SuggestSmartCampaignBudgetOptionsRequest")
    request.customer_id = customer_id
    # You can retrieve suggestions for an existing campaign by setting the
    # "campaign" field of the request equal to the resource name of a campaign
    # and leaving the rest of the request fields below unset:
    # request.campaign = INSERT_CAMPAIGN_RESOURCE_NAME_HERE

    # Since these suggestions are for a new campaign, we're going to
    # use the suggestion_info field instead.
    suggestion_info = request.suggestion_info

    # Add the URL of the campaign's landing page.
    suggestion_info.final_url = _LANDING_PAGE_URL

    # Construct location information using the given geo target constant. It's
    # also possible to provide a geographic proximity using the "proximity"
    # field on suggestion_info, for example:
    #
    # suggestion_info.proximity.address.post_code = INSERT_POSTAL_CODE
    # suggestion_info.proximity.address.province_code = INSERT_PROVINCE_CODE
    # suggestion_info.proximity.address.country_code = INSERT_COUNTRY_CODE
    # suggestion_info.proximity.address.province_name = INSERT_PROVINCE_NAME
    # suggestion_info.proximity.address.street_address = INSERT_STREET_ADDRESS
    # suggestion_info.proximity.address.street_address2 = INSERT_STREET_ADDRESS_2
    # suggestion_info.proximity.address.city_name = INSERT_CITY_NAME
    # suggestion_info.proximity.radius = INSERT_RADIUS
    # suggestion_info.proximity.radius_units = RADIUS_UNITS
    #
    # For more information on proximities see:
    # https://developers.google.com/google-ads/api/reference/rpc/latest/ProximityInfo
    location = client.get_type("LocationInfo")
    # Set the location to the resource name of the given geo target constant.
    location.geo_target_constant = client.get_service(
        "GeoTargetConstantService"
    ).geo_target_constant_path(_GEO_TARGET_CONSTANT)
    # Add the LocationInfo object to the list of locations on the
    # suggestion_info object. You have the option of providing multiple
    # locations when using location-based suggestions.
    suggestion_info.location_list.locations.append(location)

    # Add the KeywordThemeInfo objects to the SuggestionInfo object.
    suggestion_info.keyword_themes.extend(keyword_theme_infos)

    # If provided, add the GMB location ID.
    if business_location_id:
        suggestion_info.business_location_id = business_location_id

    # Add a schedule detailing which days of the week the business is open.
    # This schedule describes a schedule in which the business is open on
    # Mondays from 9am to 5pm.
    ad_schedule_info = client.get_type("AdScheduleInfo")
    # Set the day of this schedule as Monday.
    ad_schedule_info.day_of_week = client.enums.DayOfWeekEnum.MONDAY
    # Set the start hour to 9am.
    ad_schedule_info.start_hour = 9
    # Set the end hour to 5pm.
    ad_schedule_info.end_hour = 17
    # Set the start and end minute of zero, for example: 9:00 and 5:00.
    zero_minute_of_hour = client.enums.MinuteOfHourEnum.ZERO
    ad_schedule_info.start_minute = zero_minute_of_hour
    ad_schedule_info.end_minute = zero_minute_of_hour
    suggestion_info.ad_schedules.append(ad_schedule_info)

    # Issue a request to retrieve a budget suggestion.
    response = sc_suggest_service.suggest_smart_campaign_budget_options(
        request=request
    )

    # Three tiers of options will be returned, a "low", "high" and
    # "recommended". Here we will use the "recommended" option. The amount is
    # specified in micros, where one million is equivalent to one currency unit.
    recommendation = response.recommended
    print(
        f"A daily budget amount of {recommendation.daily_amount_micros} micros "
        "was suggested, garnering an estimated minimum of "
        f"{recommendation.metrics.min_daily_clicks} clicks and an estimated "
        f"maximum of {recommendation.metrics.max_daily_clicks} per day."
    )

    return recommendation.daily_amount_micros


def _map_keyword_theme_constants_to_infos(client, keyword_theme_constants):
    """Maps a list of KeywordThemeConstants to KeywordThemeInfos.

    Args:
        client: an initialized GoogleAdsClient instance.
        keyword_theme_constants: a list of KeywordThemeConstants.

    Returns:
        a list of KeywordThemeInfos.
    """
    infos = []
    for constant in keyword_theme_constants:
        info = client.get_type("KeywordThemeInfo")
        info.keyword_theme_constant = constant.resource_name
        infos.append(info)

    return infos


def _create_campaign_budget_operation(
    client, customer_id, suggested_budget_amount
):
    """Creates a MutateOperation that creates a new CampaignBudget.

    A temporary ID will be assigned to this campaign budget so that it can be
    referenced by other objects being created in the same Mutate request.

    Args:
        client: an initialized GoogleAdsClient instance.
        customer_id: a client customer ID.
        suggested_budget_amount: a numeric daily budget amount in micros.

    Returns:
        a MutateOperation that creates a CampaignBudget.
    """
    mutate_operation = client.get_type("MutateOperation")
    campaign_budget_operation = mutate_operation.campaign_budget_operation
    campaign_budget = campaign_budget_operation.create
    campaign_budget.name = f"Smart campaign budget #{uuid4()}"
    # A budget used for Smart campaigns must have the type SMART_CAMPAIGN.
    # Note that the field name "type_" is an implementation detail in Python,
    # the field's actual name is "type".
    campaign_budget.type_ = client.enums.BudgetTypeEnum.SMART_CAMPAIGN
    # The suggested budget amount from the SmartCampaignSuggestService is
    # a daily budget. We don't need to specify that here, because the budget
    # period already defaults to DAILY.
    campaign_budget.amount_micros = suggested_budget_amount
    # Set a temporary ID in the budget's resource name so it can be referenced
    # by the campaign in later steps.
    campaign_budget.resource_name = client.get_service(
        "CampaignBudgetService"
    ).campaign_budget_path(customer_id, _BUDGET_TEMPORARY_ID)

    return mutate_operation


def _create_smart_campaign_operation(client, customer_id):
    """Creates a MutateOperation that creates a new Smart campaign.

    A temporary ID will be assigned to this campaign so that it can
    be referenced by other objects being created in the same Mutate request.

    Args:
        client: an initialized GoogleAdsClient instance.
        customer_id: a client customer ID.

    Returns:
        a MutateOperation that creates a campaign.
    """
    mutate_operation = client.get_type("MutateOperation")
    campaign = mutate_operation.campaign_operation.create
    campaign.name = f"Smart campaign #{uuid4()}"
    # Set the campaign status as PAUSED. The campaign is the only entity in
    # the mutate request that should have its' status set.
    campaign.status = client.enums.CampaignStatusEnum.PAUSED
    # Campaign.AdvertisingChannelType is required to be SMART.
    campaign.advertising_channel_type = (
        client.enums.AdvertisingChannelTypeEnum.SMART
    )
    # Campaign.AdvertisingChannelSubType is required to be SMART_CAMPAIGN.
    campaign.advertising_channel_sub_type = (
        client.enums.AdvertisingChannelSubTypeEnum.SMART_CAMPAIGN
    )
    # Assign the resource name with a temporary ID.
    campaign_service = client.get_service("CampaignService")
    campaign.resource_name = campaign_service.campaign_path(
        customer_id, _SMART_CAMPAIGN_TEMPORARY_ID
    )
    # Set the budget using the given budget resource name.
    campaign.campaign_budget = campaign_service.campaign_budget_path(
        customer_id, _BUDGET_TEMPORARY_ID
    )

    return mutate_operation


def _create_smart_campaign_setting_operation(
    client, customer_id, business_location_id, business_name
):
    """Creates a MutateOperation to create a new SmartCampaignSetting.

    SmartCampaignSettings are unique in that they only support UPDATE
    operations, which are used to update and create them. Below we will
    use a temporary ID in the resource name to associate it with the
    campaign created in the previous step.

    Args:
        client: an initialized GoogleAdsClient instance.
        customer_id: a client customer ID.
        business_location_id: the ID of a Google My Business location.
        business_name: the name of a Google My Business.

    Returns:
        a MutateOperation that creates a SmartCampaignSetting.
    """
    mutate_operation = client.get_type("MutateOperation")
    smart_campaign_setting = (
        mutate_operation.smart_campaign_setting_operation.update
    )
    # Set a temporary ID in the campaign setting's resource name to associate it
    # with the campaign created in the previous step.
    smart_campaign_setting.resource_name = client.get_service(
        "SmartCampaignSettingService"
    ).smart_campaign_setting_path(customer_id, _SMART_CAMPAIGN_TEMPORARY_ID)

    # Below we configure the SmartCampaignSetting using many of the same
    # details used to generate a budget suggestion.
    smart_campaign_setting.phone_number.country_code = _COUNTRY_CODE
    smart_campaign_setting.phone_number.phone_number = _PHONE_NUMBER
    smart_campaign_setting.final_url = _LANDING_PAGE_URL
    smart_campaign_setting.advertising_language_code = _LANGUAGE_CODE

    # It's required that either a business location ID or a business name is
    # added to the SmartCampaignSetting.
    if business_location_id:
        smart_campaign_setting.business_location_id = business_location_id
    else:
        smart_campaign_setting.business_name = business_name

    # Set the update mask on the operation. This is required since the smart
    # campaign setting is created in an UPDATE operation. Here the update
    # mask will be a list of all the fields that were set on the
    # SmartCampaignSetting.
    client.copy_from(
        mutate_operation.smart_campaign_setting_operation.update_mask,
        protobuf_helpers.field_mask(None, smart_campaign_setting._pb),
    )

    return mutate_operation


def _create_campaign_criterion_operations(
    client, customer_id, keyword_theme_infos
):
    """Creates a list of MutateOperations that create new campaign criteria.

    Args:
        client: an initialized GoogleAdsClient instance.
        customer_id: a client customer ID.
        keyword_theme_infos: a list of KeywordThemeInfos.

    Returns:
        a list of MutateOperations that create new campaign criteria.
    """
    campaign_service = client.get_service("CampaignService")

    operations = []
    for info in keyword_theme_infos:
        mutate_operation = client.get_type("MutateOperation")
        campaign_criterion = (
            mutate_operation.campaign_criterion_operation.create
        )
        # Set the campaign ID to a temporary ID.
        campaign_criterion.campaign = campaign_service.campaign_path(
            customer_id, _SMART_CAMPAIGN_TEMPORARY_ID
        )
        # Set the criterion type to KEYWORD_THEME.
        campaign_criterion.type_ = client.enums.CriterionTypeEnum.KEYWORD_THEME
        # Set the keyword theme to the given KeywordThemeInfo.
        campaign_criterion.keyword_theme = info
        # Add the mutate operation to the list of other operations.
        operations.append(mutate_operation)

    return operations


def _create_ad_group_operation(client, customer_id):
    """Creates a MutateOperation that creates a new ad group.

    A temporary ID will be used in the campaign resource name for this
    ad group to associate it with the Smart campaign created in earlier steps.
    A temporary ID will also be used for its own resource name so that we can
    associate an ad group ad with it later in the process.

    Only one ad group can be created for a given Smart campaign.

    Args:
        client: an initialized GoogleAdsClient instance.
        customer_id: a client customer ID.

    Returns:
        a MutateOperation that creates a new ad group.
    """
    mutate_operation = client.get_type("MutateOperation")
    ad_group = mutate_operation.ad_group_operation.create
    # Set the ad group ID to a temporary ID.
    ad_group.resource_name = client.get_service("AdGroupService").ad_group_path(
        customer_id, _AD_GROUP_TEMPORARY_ID
    )
    ad_group.name = f"Smart campaign ad group #{uuid4()}"
    # Set the campaign ID to a temporary ID.
    ad_group.campaign = client.get_service("CampaignService").campaign_path(
        customer_id, _SMART_CAMPAIGN_TEMPORARY_ID
    )
    # The ad group type must be set to SMART_CAMPAIGN_ADS.
    ad_group.type_ = client.enums.AdGroupTypeEnum.SMART_CAMPAIGN_ADS

    return mutate_operation


def _create_ad_group_ad_operation(client, customer_id):
    """Creates a MutateOperation that creates a new ad group ad.

    A temporary ID will be used in the ad group resource name for this
    ad group ad to associate it with the ad group created in earlier steps.

    Args:
        client: an initialized GoogleAdsClient instance.
        customer_id: a client customer ID.

    Returns:
        a MutateOperation that creates a new ad group ad.
    """
    mutate_operation = client.get_type("MutateOperation")
    ad_group_ad = mutate_operation.ad_group_ad_operation.create
    # Set the ad group ID to a temporary ID.
    ad_group_ad.ad_group = client.get_service("AdGroupService").ad_group_path(
        customer_id, _AD_GROUP_TEMPORARY_ID
    )
    # Set the type to SMART_CAMPAIGN_AD.
    ad_group_ad.ad.type_ = client.enums.AdTypeEnum.SMART_CAMPAIGN_AD
    ad = ad_group_ad.ad.smart_campaign_ad
    # At most, three headlines can be specified for a Smart campaign ad.
    headline_1 = client.get_type("AdTextAsset")
    headline_1.text = "Headline number one"
    headline_2 = client.get_type("AdTextAsset")
    headline_2.text = "Headline number two"
    headline_3 = client.get_type("AdTextAsset")
    headline_3.text = "Headline number three"
    ad.headlines.extend([headline_1, headline_2, headline_3])
    # At most, two descriptions can be specified for a Smart campaign ad.
    description_1 = client.get_type("AdTextAsset")
    description_1.text = "Description number one"
    description_2 = client.get_type("AdTextAsset")
    description_2.text = "Description number two"
    ad.descriptions.extend([description_1, description_2])

    return mutate_operation


def _print_response_details(response):
    """Prints the details of a MutateGoogleAdsResponse.

    Parses the "response" oneof field name and uses it to extract the new
    entity's name and resource name.

    Args:
        response: a MutateGoogleAdsResponse object.
    """
    # Parse the Mutate response to print details about the entities that
    # were created by the request.
    for result in response.mutate_operation_responses:
        resource_type = "unrecognized"
        resource_name = "not found"

        if result._pb.HasField("campaign_budget_result"):
            resource_type = "CampaignBudget"
            resource_name = result.campaign_budget_result.resource_name
        elif result._pb.HasField("campaign_result"):
            resource_type = "Campaign"
            resource_name = result.campaign_result.resource_name
        elif result._pb.HasField("smart_campaign_setting_result"):
            resource_type = "SmartCampaignSettingResult"
            resource_name = result.smart_campaign_setting_result.resource_name
        elif result._pb.HasField("campaign_criterion_result"):
            resource_type = "CampaignCriterion"
            resource_name = result.campaign_criterion_result.resource_name
        elif result._pb.HasField("ad_group_result"):
            resource_type = "AdGroup"
            resource_name = result.ad_group_result.resource_name
        elif result._pb.HasField("ad_group_ad_result"):
            resource_type = "AdGroupAd"
            resource_name = result.ad_group_ad_result.resource_name

        print(
            f"Created a(n) {resource_type} with "
            f"resource_name: '{resource_name}'."
        )


if __name__ == "__main__":
    # GoogleAdsClient will read the google-ads.yaml configuration file in the
    # home directory if none is specified.
    googleads_client = GoogleAdsClient.load_from_storage(version="v8")

    parser = argparse.ArgumentParser(description=("Creates a Smart campaign."))
    # The following argument(s) should be provided to run the example.
    parser.add_argument(
        "-c",
        "--customer_id",
        type=str,
        required=True,
        help="The Google Ads customer ID.",
    )
    parser.add_argument(
        "-k",
        "--keyword_text",
        type=str,
        required=False,
        default=_DEFAULT_KEYWORD,
        help=(
            "A keyword text used to generate a set of keyword themes, which "
            "are used to improve the budget suggestion and performance of "
            f"the Smart campaign. Default value is '{_DEFAULT_KEYWORD}'."
        ),
    )
    group = parser.add_mutually_exclusive_group(required=True)
    group.add_argument(
        "-b",
        "--business_location_id",
        type=str,
        help=(
            "The ID of a Google My Business (GMB) location. This is required "
            "if a business name is not provided. It can be retrieved using the "
            "GMB API, for details see: "
            "https://developers.google.com/my-business/reference/rest/v4/accounts.locations"
        ),
    )
    group.add_argument(
        "-n",
        "--business_name",
        type=str,
        help=(
            "The name of a Google My Business (GMB) business. This is required "
            "if a business location ID is not provided."
        ),
    )

    args = parser.parse_args()

    try:
        main(
            googleads_client,
            args.customer_id,
            args.keyword_text,
            args.business_location_id,
            args.business_name,
        )
    except GoogleAdsException as ex:
        print(
            f'Request with ID "{ex.request_id}" failed with status '
            f'"{ex.error.code().name}" and includes the following errors:'
        )
        for error in ex.failure.errors:
            print(f'Error with message "{error.message}".')
            if error.location:
                for field_path_element in error.location.field_path_elements:
                    print(f"\t\tOn field: {field_path_element.field_name}")
        sys.exit(1)

      
      

Ruby

#!/usr/bin/env ruby
# Encoding: utf-8
#
# Copyright 2021 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.
#
# This example shows how to create a Smart campaign.
# More details on Smart campaigns can be found here:
# https://support.google.com/google-ads/answer/7652860

require 'optparse'
require 'date'
require 'open-uri'
require 'google/ads/google_ads'

def add_smart_campaign(
  customer_id,
  keyword_text,
  business_location_id,
  business_name)
  client = Google::Ads::GoogleAds::GoogleAdsClient.new

  keyword_theme_constants = get_keyword_theme_constants(client, keyword_text)

  keyword_theme_infos = map_keyword_theme_constants_to_infos(
    client, keyword_theme_constants)

  suggested_budget_amount = get_budget_suggestion(
    client,
    customer_id,
    business_location_id,
    keyword_theme_infos,
  )

  # The below methods create and return MutateOperations that we later
  # provide to the GoogleAdsService.Mutate method in order to create the
  # entities in a single request. Since the entities for a Smart campaign
  # are closely tied to one-another it's considered a best practice to
  # create them in a single Mutate request so they all complete successfully
  # or fail entirely, leaving no orphaned entities. See:
  # https://developers.google.com/google-ads/api/docs/mutating/overview
  mutate_operations = []

  # It's important to create these operations in this order because
  # they depend on each other, for example the SmartCampaignSetting
  # and ad group depend on the campaign, and the ad group ad depends
  # on the ad group.
  mutate_operations << create_campaign_budget_operation(
    client,
    customer_id,
    suggested_budget_amount,
  )

  mutate_operations << create_smart_campaign_operation(
    client,
    customer_id,
  )

  mutate_operations << create_smart_campaign_setting_operation(
    client,
    customer_id,
    business_location_id,
    business_name,
  )

  mutate_operations += create_campaign_criterion_operations(
    client,
    customer_id,
    keyword_theme_infos,
  )

  mutate_operations << create_ad_group_operation(client, customer_id)

  mutate_operations << create_ad_group_ad_operation(client, customer_id)

  # Sends the operations into a single Mutate request.
  response = client.service.google_ads.mutate(
    customer_id: customer_id,
    mutate_operations: mutate_operations,
  )

  print_response_details(response)
end

# Retrieves keyword_theme_constants for the given criteria.
def get_keyword_theme_constants(client, keyword_text)
  response = client.service.keyword_theme_constant.suggest_keyword_theme_constants(
    query_text: keyword_text,
    country_code: COUNTRY_CODE,
    language_code: LANGUAGE_CODE,
  )

  puts "Retrieved #{response.keyword_theme_constants.size} keyword theme" \
    "constants using the keyword: '#{keyword_text}'"

  response.keyword_theme_constants
end

# Maps a list of keyword_theme_constants to keyword_theme_infos.
def map_keyword_theme_constants_to_infos(client, keyword_theme_constants)
  infos = []
  keyword_theme_constants.each do |constant|
    infos << client.resource.keyword_theme_info do |kti|
      kti.keyword_theme_constant = constant.resource_name
    end
  end

  infos
end

# Retrieves a suggested budget amount for a new budget.
# Using the smart_campaign_suggest_service to determine a daily budget for new
# and existing Smart campaigns is highly recommended because it helps the
# campaigns achieve optimal performance.
def get_budget_suggestion(
  client,
  customer_id,
  business_location_id,
  keyword_theme_infos)

  # Since these suggestions are for a new campaign, we're going to
  # use the suggestion_info field instead.
  suggestion_info = client.resource.smart_campaign_suggestion_info do |si|
    # Adds the URL of the campaign's landing page.
    si.final_url = LANDING_PAGE_URL
    # Constructs location information using the given geo target constant. It's
    # also possible to provide a geographic proximity using the "proximity"
    # field on suggestion_info, for example:
    # si.proximity = client.resource.proximity_info do |proximity|
    #   proximity.address = client.resource.address_info do |address|
    #     address.post_code = INSERT_POSTAL_CODE
    #     address.province_code = INSERT_PROVINCE_CODE
    #     address.country_code = INSERT_COUNTRY_CODE
    #     address.province_name = INSERT_PROVINCE_NAME
    #     address.street_address = INSERT_STREET_ADDRESS
    #     address.street_address2 = INSERT_STREET_ADDRESS_2
    #     address.city_name = INSERT_CITY_NAME
    #   end
    #   proximity.radius = INSERT_RADIUS
    #   proximity.radius_units = :INSERT_RADIUS_UNIT_ENUM
    # end
    #
    # For more information on proximities see:
    # https://developers.google.com/google-ads/api/reference/rpc/latest/ProximityInfo
    si.location_list = client.resource.location_list do |loc_list|
      # Adds the location_info object to the list of locations on the
      # suggestion_info object. You have the option of providing multiple
      # locations when using location-based suggestions.
      loc_list.locations << client.resource.location_info do |li|
        li.geo_target_constant = client.path.geo_target_constant(GEO_TARGET_CONSTANT)
      end
    end
    # Adds the keyword_theme_info objects to the suggestion_info object.
    si.keyword_themes += keyword_theme_infos
    # If provided, add the GMB location ID.
    if business_location_id
      si.business_location_id = business_location_id.to_i
    end
    # Adds a schedule detailing which days of the week the business is open.
    # This schedule describes a schedule in which the business is open on
    # Mondays from 9am to 5pm.
    si.ad_schedules += [
      client.resource.ad_schedule_info do |as|
        # Sets the day of this schedule as Monday.
        as.day_of_week = :MONDAY
        # Sets the start hour to 9:00am.
        as.start_hour = 9
        as.start_minute = :ZERO
        # Sets the end hour to 5:00pm.
        as.end_hour = 17
        as.end_minute = :ZERO
      end
    ]
  end

  # Issues a request to retrieve a budget suggestion.
  response = client.service.smart_campaign_suggest.suggest_smart_campaign_budget_options(
    customer_id: customer_id,
    suggestion_info: suggestion_info,
  )

  # Three tiers of options will be returned, a "low", "high" and
  # "recommended". Here we will use the "recommended" option. The amount is
  # specified in micros, where one million is equivalent to one currency unit.
  recommendation = response.recommended
  puts "A daily budget amount of #{recommendation.daily_amount_micros} micros" \
    " was suggested, garnering an estimated minimum of" \
    " #{recommendation.metrics.min_daily_clicks} clicks and an estimated" \
    " maximum of #{recommendation.metrics.max_daily_clicks} per day."

  recommendation.daily_amount_micros
end

# Creates a mutate_operation that creates a new campaign_budget.
# A temporary ID will be assigned to this campaign budget so that it can be
# referenced by other objects being created in the same mutate request.
def create_campaign_budget_operation(
  client,
  customer_id,
  suggested_budget_amount)
  mutate_operation = client.operation.mutate do |m|
    m.campaign_budget_operation = client.operation.create_resource.campaign_budget do |cb|
      cb.name = "Smart campaign budget ##{(Time.new.to_f * 1000).to_i}"
      # A budget used for Smart campaigns must have the type SMART_CAMPAIGN.
      cb.type = :SMART_CAMPAIGN
      # The suggested budget amount from the smart_campaign_suggest_service is
      # a daily budget. We don't need to specify that here, because the budget
      # period already defaults to DAILY.
      cb.amount_micros = suggested_budget_amount
      # Sets a temporary ID in the budget's resource name so it can be referenced
      # by the campaign in later steps.
      cb.resource_name = client.path.campaign_budget(customer_id, BUDGET_TEMPORARY_ID)
    end
  end

  mutate_operation
end

# Creates a mutate_operation that creates a new Smart campaign.
# A temporary ID will be assigned to this campaign so that it can
# be referenced by other objects being created in the same mutate request.
def create_smart_campaign_operation(
    client,
    customer_id)
  mutate_operation = client.operation.mutate do |m|
    m.campaign_operation = client.operation.create_resource.campaign do |c|
      c.name = "Smart campaign ##{(Time.new.to_f * 1000).to_i}"
      # Sets the campaign status as PAUSED. The campaign is the only entity in
      # the mutate request that should have its' status set.
      c.status = :PAUSED
      # campaign.advertising_channel_type is required to be SMART.
      c.advertising_channel_type = :SMART
      # campaign.advertising_channel_sub_type is required to be SMART_CAMPAIGN.
      c.advertising_channel_sub_type = :SMART_CAMPAIGN
      # Assigns the resource name with a temporary ID.
      c.resource_name = client.path.campaign(customer_id, SMART_CAMPAIGN_TEMPORARY_ID)
      c.campaign_budget = client.path.campaign_budget(customer_id, BUDGET_TEMPORARY_ID)
    end
  end

  mutate_operation
end

# Creates a mutate_operation to create a new smart_campaign_setting.
# smart_campaign_settings are unique in that they only support UPDATE
# operations, which are used to update and create them. Below we will
# use a temporary ID in the resource name to associate it with the
# campaign created in the previous step.
def create_smart_campaign_setting_operation(
  client,
  customer_id,
  business_location_id,
  business_name)
  mutate_operation = client.operation.mutate do |m|
    m.smart_campaign_setting_operation =
      client.operation.update_resource.smart_campaign_setting(
        # Sets a temporary ID in the campaign setting's resource name to
        # associate it with the campaign created in the previous step.
        client.path.smart_campaign_setting(
          customer_id, SMART_CAMPAIGN_TEMPORARY_ID)
      ) do |scs|
      # Below we configure the smart_campaign_setting using many of the same
      # details used to generate a budget suggestion.
      scs.phone_number = client.resource.phone_number do |p|
        p.country_code = COUNTRY_CODE
        p.phone_number = PHONE_NUMBER
      end
      scs.final_url = LANDING_PAGE_URL
      scs.advertising_language_code = LANGUAGE_CODE
      # It's required that either a business location ID or a business name is
      # added to the smart_campaign_setting.
      if business_location_id
        scs.business_location_id = business_location_id.to_i
      else
        scs.business_name = business_name
      end
    end
  end

  mutate_operation
end

# Creates a list of mutate_operations that create new campaign criteria.
def create_campaign_criterion_operations(
  client,
  customer_id,
  keyword_theme_infos)
  operations = []

  keyword_theme_infos.each do |info|
    operations << client.operation.mutate do |m|
      m.campaign_criterion_operation =
        client.operation.create_resource.campaign_criterion do |cc|
        # Sets the campaign ID to a temporary ID.
        cc.campaign = client.path.campaign(
          customer_id, SMART_CAMPAIGN_TEMPORARY_ID)
        # Sets the criterion type to KEYWORD_THEME.
        cc.type = :KEYWORD_THEME
        # Sets the keyword theme to the given keyword_theme_info.
        cc.keyword_theme = info
      end
    end
  end

  operations
end

# Creates a mutate_operation that creates a new ad group.
# A temporary ID will be used in the campaign resource name for this
# ad group to associate it with the Smart campaign created in earlier steps.
# A temporary ID will also be used for its own resource name so that we can
# associate an ad group ad with it later in the process.
# Only one ad group can be created for a given Smart campaign.
def create_ad_group_operation(client, customer_id)
  mutate_operation = client.operation.mutate do |m|
    m.ad_group_operation = client.operation.create_resource.ad_group do |ag|
      # Set the ad group ID to a temporary ID.
      ag.resource_name = client.path.ad_group(customer_id, AD_GROUP_TEMPORARY_ID)
      ag.name = "Smart campaign ad group ##{(Time.new.to_f * 1000).to_i}"
      # Set the campaign ID to a temporary ID.
      ag.campaign = client.path.campaign(customer_id, SMART_CAMPAIGN_TEMPORARY_ID)
      # The ad group type must be set to SMART_CAMPAIGN_ADS.
      ag.type = :SMART_CAMPAIGN_ADS
    end
  end

  mutate_operation
end

# Creates a mutate_operation that creates a new ad group ad.
# A temporary ID will be used in the ad group resource name for this
# ad group ad to associate it with the ad group created in earlier steps.
def create_ad_group_ad_operation(client, customer_id)
  mutate_operation = client.operation.mutate do |m|
    m.ad_group_ad_operation = client.operation.create_resource.ad_group_ad do |aga|
      # Set the ad group ID to a temporary ID.
      aga.ad_group = client.path.ad_group(customer_id, AD_GROUP_TEMPORARY_ID)
      aga.ad = client.resource.ad do |ad|
        # Set the type to SMART_CAMPAIGN_AD.
        ad.type = :SMART_CAMPAIGN_AD
        ad.smart_campaign_ad = client.resource.smart_campaign_ad_info do |sca|
          # At most, three headlines can be specified for a Smart campaign ad.
          sca.headlines << client.resource.ad_text_asset do |ata|
            ata.text = "Headline number one"
          end
          sca.headlines << client.resource.ad_text_asset do |ata|
            ata.text = "Headline number two"
          end
          sca.headlines << client.resource.ad_text_asset do |ata|
            ata.text = "Headline number three"
          end
          # At most, two descriptions can be specified for a Smart campaign ad.
          sca.descriptions << client.resource.ad_text_asset do |ata|
            ata.text = "Descriptions number one"
          end
          sca.descriptions << client.resource.ad_text_asset do |ata|
            ata.text = "Descriptions number two"
          end
        end
      end
    end
  end

  mutate_operation
end

# Prints the details of a mutate_google_ads_response.
# Parses the "response" oneof field name and uses it to extract the new
# entity's name and resource name.
def print_response_details(response)
  # Parse the mutate response to print details about the entities that
  # were created by the request.
  response.mutate_operation_responses.each do |result|
    resource_type = "unrecognized"
    resource_name = "not found"

    resource_type, resource_name = if result.campaign_budget_result
      ["campaign_budget", result.campaign_budget_result.resource_name]
    elsif result.campaign_result
      ["campaign", result.campaign_result.resource_name]
    elsif result.smart_campaign_setting_result
      ["smart_campaign_setting", result.smart_campaign_setting_result.resource_name]
    elsif result.campaign_criterion_result
      ["campaign_criterion", result.campaign_criterion_result.resource_name]
    elsif result.ad_group_result
      ["ad_group", result.ad_group_result.resource_name]
    elsif result.ad_group_ad_result
      ["ad_group_ad", result.ad_group_ad_result.resource_name]
    end

    puts "Created a(n) #{resource_type} with resource name: '#{resource_name}'"
  end
end

if __FILE__ == $0
  DEFAULT_KEYWORD = "travel"
  # Geo target constant for New York City.
  GEO_TARGET_CONSTANT = "1023191"
  # Country code is a two-letter ISO-3166 code, for a list of all codes see:
  # https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-16
  COUNTRY_CODE = "US"
  # For a list of all language codes, see:
  # https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-7
  LANGUAGE_CODE = "en"
  LANDING_PAGE_URL = "http://www.example.com"
  PHONE_NUMBER = "555-555-5555"
  BUDGET_TEMPORARY_ID = "-1"
  SMART_CAMPAIGN_TEMPORARY_ID = "-2"
  AD_GROUP_TEMPORARY_ID = "-3"

  options = {}

  # 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.
  options[:customer_id] = 'INSERT_CUSTOMER_ID_HERE'
  options[:keyword_text] = DEFAULT_KEYWORD


  OptionParser.new do |opts|
    opts.banner = sprintf('Usage: %s [options]', File.basename(__FILE__))

    opts.separator ''
    opts.separator 'Options:'

    opts.on('-C', '--customer-id CUSTOMER-ID', String, 'Customer ID') do |v|
      options[:customer_id] = v
    end

    opts.on('-k', '--keyword-text KEYWORD-TEXT', String,
      'A keyword text used to generate a set of keyword themes, which ' \
      ' are used to improve the budget suggestion and performance of the' \
      ' Smart campaign') do |v|
      options[:keyword_text] = v
    end

    opts.on('-b', '--business-location-id BUSINESS-LOCATION-ID', String,
      'The ID of a Google My Business (GMB) location. This is required' \
      ' if a business name is not provided. It can be retrieved using the' \
      ' GMB API, for details see:' \
      ' https://developers.google.com/my-business/reference/rest/v4/accounts.locations') do |v|
      options[:business_location_id] = v
    end

    opts.on('-n', '--business-name BUSINESS-NAME', String,
      'The name of a Google My Business (GMB) business. This is required' \
      ' if a business location ID is not provided.') do |v|
      options[:business_name] = v
    end

    opts.separator ''
    opts.separator 'Help:'

    opts.on_tail('-h', '--help', 'Show this message') do
      puts opts
      exit
    end
  end.parse!

  begin
    add_smart_campaign(
      options.fetch(:customer_id).tr("-", ""),
      options.fetch(:keyword_text),
      options[:business_location_id],
      options[:business_name],
    )
  rescue Google::Ads::GoogleAds::Errors::GoogleAdsError => e
    e.failure.errors.each do |error|
      STDERR.printf("Error with message: %s\n", error.message)
      if error.location
        error.location.field_path_elements.each do |field_path_element|
          STDERR.printf("\tOn field: %s\n", field_path_element.field_name)
        end
      end
      error.error_code.to_h.each do |k, v|
        next if v == :UNSPECIFIED
        STDERR.printf("\tType: %s\n\tCode: %s\n", k, v)
      end
    end
    raise
  end
end

      
      

Perl

#!/usr/bin/perl -w
#
# Copyright 2021, 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.
#
# This example shows how to create a Smart Campaign.
#
# More details on Smart Campaigns can be found here:
# https://support.google.com/google-ads/answer/7652860

use strict;
use warnings;
use utf8;

use FindBin qw($Bin);
use lib "$Bin/../../lib";
use Google::Ads::GoogleAds::Client;
use Google::Ads::GoogleAds::Utils::FieldMasks;
use Google::Ads::GoogleAds::Utils::GoogleAdsHelper;
use Google::Ads::GoogleAds::V8::Resources::Ad;
use Google::Ads::GoogleAds::V8::Resources::AdGroup;
use Google::Ads::GoogleAds::V8::Resources::AdGroupAd;
use Google::Ads::GoogleAds::V8::Resources::CampaignBudget;
use Google::Ads::GoogleAds::V8::Resources::Campaign;
use Google::Ads::GoogleAds::V8::Resources::CampaignCriterion;
use Google::Ads::GoogleAds::V8::Resources::PhoneNumber;
use Google::Ads::GoogleAds::V8::Resources::SmartCampaignSetting;
use Google::Ads::GoogleAds::V8::Common::AdTextAsset;
use Google::Ads::GoogleAds::V8::Common::AdScheduleInfo;
use Google::Ads::GoogleAds::V8::Common::KeywordThemeInfo;
use Google::Ads::GoogleAds::V8::Common::LocationInfo;
use Google::Ads::GoogleAds::V8::Common::SmartCampaignAdInfo;
use Google::Ads::GoogleAds::V8::Enums::AdGroupTypeEnum qw(SMART_CAMPAIGN_ADS);
use Google::Ads::GoogleAds::V8::Enums::AdTypeEnum qw(SMART_CAMPAIGN_AD);
use Google::Ads::GoogleAds::V8::Enums::AdvertisingChannelTypeEnum qw(SMART);
use Google::Ads::GoogleAds::V8::Enums::AdvertisingChannelSubTypeEnum;
use Google::Ads::GoogleAds::V8::Enums::BudgetTypeEnum;
use Google::Ads::GoogleAds::V8::Enums::CampaignStatusEnum qw(PAUSED);
use Google::Ads::GoogleAds::V8::Enums::CriterionTypeEnum qw(KEYWORD_THEME);
use Google::Ads::GoogleAds::V8::Enums::DayOfWeekEnum qw(MONDAY);
use Google::Ads::GoogleAds::V8::Enums::MinuteOfHourEnum qw(ZERO);
use Google::Ads::GoogleAds::V8::Enums::ProximityRadiusUnitsEnum qw(MILES);
use Google::Ads::GoogleAds::V8::Services::AdGroupService::AdGroupOperation;
use Google::Ads::GoogleAds::V8::Services::AdGroupAdService::AdGroupAdOperation;
use
  Google::Ads::GoogleAds::V8::Services::CampaignBudgetService::CampaignBudgetOperation;
use Google::Ads::GoogleAds::V8::Services::CampaignService::CampaignOperation;
use
  Google::Ads::GoogleAds::V8::Services::CampaignCriterionService::CampaignCriterionOperation;
use Google::Ads::GoogleAds::V8::Services::GoogleAdsService::MutateOperation;
use
  Google::Ads::GoogleAds::V8::Services::GoogleAdsService::MutateOperationResponse;
use
  Google::Ads::GoogleAds::V8::Services::KeywordThemeConstantService::SuggestKeywordThemeConstantsRequest;
use
  Google::Ads::GoogleAds::V8::Services::SmartCampaignSettingService::SmartCampaignSettingOperation;
use Google::Ads::GoogleAds::V8::Services::SmartCampaignSuggestService;
use
  Google::Ads::GoogleAds::V8::Services::SmartCampaignSuggestService::LocationList;
use
  Google::Ads::GoogleAds::V8::Services::SmartCampaignSuggestService::SmartCampaignSuggestionInfo;
use
  Google::Ads::GoogleAds::V8::Services::SmartCampaignSuggestService::SuggestSmartCampaignBudgetOptionsRequest;
use Google::Ads::GoogleAds::V8::Utils::ResourceNames;

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

use constant DEFAULT_KEYWORD_TEXT => "travel";

# Geo target constant for New York City.
use constant GEO_TARGET_CONSTANT         => 1023191;
use constant COUNTRY_CODE                => "US";
use constant LANGUAGE_CODE               => "en";
use constant LANDING_PAGE_URL            => "http://www.example.com";
use constant PHONE_NUMBER                => "555-555-5555";
use constant BUDGET_TEMPORARY_ID         => -1;
use constant SMART_CAMPAIGN_TEMPORARY_ID => -2;
use constant AD_GROUP_TEMPORARY_ID       => -3;

# 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 $keyword_text         = DEFAULT_KEYWORD_TEXT;
my $business_location_id = undef;
my $business_name        = undef;

sub add_smart_campaign {
  my ($api_client, $customer_id, $keyword_text, $business_location_id,
    $business_name)
    = @_;

  my $keyword_theme_constants =
    _get_keyword_theme_constants($api_client, $keyword_text);

  # Map the KeywordThemeConstants to KeywordThemeInfo objects.
  my $keyword_theme_infos = [];
  foreach my $keyword_theme_constant (@$keyword_theme_constants) {
    push @$keyword_theme_infos,
      Google::Ads::GoogleAds::V8::Common::KeywordThemeInfo->new({
        keywordThemeConstant => $keyword_theme_constant->{resourceName}});
  }

  my $suggested_budget_amount =
    _get_budget_suggestion($api_client, $customer_id, $business_location_id,
    $keyword_theme_infos);

  # The below methods create and return MutateOperations that we later provide to the
  # GoogleAdsService.Mutate method in order to create the entities in a single
  # request. Since the entities for a Smart campaign are closely tied to one-another
  # it's considered a best practice to create them in a single Mutate request; the
  # entities will either all complete successfully or fail entirely, leaving no
  # orphaned entities. See:
  # https://developers.google.com/google-ads/api/docs/mutating/overview
  my $campaign_budget_operation =
    _create_campaign_budget_operation($customer_id, $suggested_budget_amount);
  my $smart_campaign_operation = _create_smart_campaign_operation($customer_id);
  my $smart_campaign_setting_operation =
    _create_smart_campaign_setting_operation($customer_id,
    $business_location_id, $business_name);
  my @campaign_criterion_operations =
    _create_campaign_criterion_operations($customer_id, $keyword_theme_infos);
  my $ad_group_operation    = _create_ad_group_operation($customer_id);
  my $ad_group_ad_operation = _create_ad_group_ad_operation($customer_id);

  # It's important to create these entities in this order because they depend on
  # each other. For example, the SmartCampaignSetting and ad group depend on the
  # campaign and the ad group ad depends on the ad group.
  my $mutate_operations = [
    $campaign_budget_operation, $smart_campaign_operation,
    $smart_campaign_setting_operation
  ];
  push @$mutate_operations, @campaign_criterion_operations;
  push @$mutate_operations, $ad_group_operation;
  push @$mutate_operations, $ad_group_ad_operation;

  # Send the operations in a single mutate request.
  my $mutate_google_ads_response = $api_client->GoogleAdsService()->mutate({
    customerId       => $customer_id,
    mutateOperations => $mutate_operations
  });

  _print_response_details($mutate_google_ads_response);

  return 1;
}

# Retrieves keyword theme constants for the given criteria.
sub _get_keyword_theme_constants {
  my ($api_client, $keyword_text) = @_;

  my $response = $api_client->KeywordThemeConstantService()->suggest(
    Google::Ads::GoogleAds::V8::Services::KeywordThemeConstantService::SuggestKeywordThemeConstantsRequest
      ->new({
        queryText    => $keyword_text,
        countryCode  => COUNTRY_CODE,
        languageCode => LANGUAGE_CODE
      }));

  printf "Retrieved %d keyword theme constants using the keyword '%s'.\n",
    scalar(@{$response->{keywordThemeConstants}}), $keyword_text;

  return $response->{keywordThemeConstants};
}

sub _get_budget_suggestion {
  my ($api_client, $customer_id, $business_location_id, $keyword_theme_infos) =
    @_;

  my $request =
    Google::Ads::GoogleAds::V8::Services::SmartCampaignSuggestService::SuggestSmartCampaignBudgetOptionsRequest
    ->new({
      customerId => $customer_id,
      # You can retrieve suggestions for an existing campaign by setting the
      # "Campaign" field of the request to the resource name of a campaign and
      # leaving the rest of the request fields below unset:
      # campaign   => "INSERT_CAMPAIGN_RESOURCE_NAME_HERE",
      suggestionInfo =>
        Google::Ads::GoogleAds::V8::Services::SmartCampaignSuggestService::SmartCampaignSuggestionInfo
        ->new({
          # Add the URL of the campaign's landing page.
          finalUrl => LANDING_PAGE_URL,
          # Construct location information using the given geo target constant.
          # It's also possible to provide a geographic proximity using the
          # "proximity" field on suggestion_info, for example:
          # proximity => Google::Ads::GoogleAds::V8::Common::ProximityInfo->new({
          #     address => Google::Ads::GoogleAds::V8::Common::AddressInfo->new({
          #         postalCode     => "INSERT_POSTAL_CODE",
          #         provinceCode   => "INSERT_PROVINCE_CODE",
          #         countryCode    => "INSERT_COUNTRY_CODE",
          #         provinceName   => "INSERT_PROVINCE_NAME",
          #         streetAddress  => "INSERT_STREET_ADDRESS",
          #         streetAddress2 => "INSERT_STREET_ADDRESS_2",
          #         cityName       => "INSERT_CITY_NAME"
          #       }
          #     ),
          #     radius      => "INSERT_RADIUS",
          #     radiusUnits => MILES
          #   }
          # ),
          # For more information on proximities see:
          # https://developers.google.com/google-ads/api/reference/rpc/latest/ProximityInfo
          locationList =>
            Google::Ads::GoogleAds::V8::Services::SmartCampaignSuggestService::LocationList
            ->new(
            ),
          # keywordThemes => Keywordtheme
        })});

  # Add the LocationInfo object to the list of locations on the SuggestionInfo
  # object. You have the option of providing multiple locations when using
  # location-based suggestions.
  push
    @{$request->{suggestionInfo}{locationList}{locations}},
    Google::Ads::GoogleAds::V8::Common::LocationInfo->new({
      # Set the location to the resource name of the given geo target constant.
      geoTargetConstant =>
        Google::Ads::GoogleAds::V8::Utils::ResourceNames::geo_target_constant(
        GEO_TARGET_CONSTANT)});

  # Add the KeywordThemeInfo objects to the SuggestionInfo object.
  push @{$request->{suggestionInfo}{keywordThemes}}, $keyword_theme_infos;

  # Add the GM location ID if provided.
  $request->{suggestionInfo}{businessLocationId} = $business_location_id
    if defined $business_location_id;

  # Add a schedule detailing which days of the week the business is open. This
  # example schedule describes a business that is open on Mondays from 9:00 AM
  # to 5:00 PM.
  push(
    @{$request->{suggestionInfo}{adSchedules}},
    Google::Ads::GoogleAds::V8::Common::AdScheduleInfo->new({
        # Set the day of this schedule as Monday.
        dayOfWeek => MONDAY,
        # Set the start hour to 9 AM.
        startHour => 9,
        # Set the end hour to 5 PM.
        endHour => 17,
        # Set the start and end minutes to zero.
        startMinute => ZERO,
        endMinute   => ZERO
      }));

  # Issue a request to retrieve a budget suggestion.
  my $response = $api_client->SmartCampaignSuggestService()
    ->suggest_smart_campaign_budget_options($request);

  # Three tiers of options will be returned: "low", "high", and "recommended".
  # Here we will use the "recommended" option. The amount is specified in
  # micros, where one million is equivalent to one currency unit.
  printf "A daily budget amount of %d was suggested, garnering an estimated " .
    "minimum of %d clicks and an estimated maximum of %d clicks per day.\n",
    $response->{recommended}{dailyAmountMicros},
    $response->{recommended}{metrics}{minDailyClicks},
    $response->{recommended}{metrics}{maxDailyClicks};

  return $response->{recommended}{dailyAmountMicros};
}

# Creates a MutateOperation that creates a new CampaignBudget.
# A temporary ID will be assigned to this campaign budget so that it can be
# referenced by other objects being created in the same Mutate request.
sub _create_campaign_budget_operation {
  my ($customer_id, $suggested_budget_amount) = @_;

  return
    Google::Ads::GoogleAds::V8::Services::GoogleAdsService::MutateOperation->
    new({
      campaignBudgetOperation =>
        Google::Ads::GoogleAds::V8::Services::CampaignBudgetService::CampaignBudgetOperation
        ->new({
          create => Google::Ads::GoogleAds::V8::Resources::CampaignBudget->new({
              name => "Smart campaign budget #" . uniqid(),
              # A budget used for Smart campaigns must have the type SMART_CAMPAIGN.
              type =>
                Google::Ads::GoogleAds::V8::Enums::BudgetTypeEnum::SMART_CAMPAIGN,
              # The suggested budget amount from the SmartCampaignSuggestService is
              # a daily budget. We don't need to specify that here, because the
              # budget period already defaults to DAILY.
              amountMicros => $suggested_budget_amount,
              # Set a temporary ID in the budget's resource name so it can be
              # referenced by the campaign in later steps.
              resourceName =>
                Google::Ads::GoogleAds::V8::Utils::ResourceNames::campaign_budget(
                $customer_id, BUDGET_TEMPORARY_ID
                )})})});
}

# Creates a MutateOperation that creates a new Smart campaign. A temporary ID
# will be assigned to this campaign so that it can be referenced by other
# objects being created in the same Mutate request.
sub _create_smart_campaign_operation {
  my ($customer_id) = @_;

  return
    Google::Ads::GoogleAds::V8::Services::GoogleAdsService::MutateOperation->
    new({
      campaignOperation =>
        Google::Ads::GoogleAds::V8::Services::CampaignService::CampaignOperation
        ->new({
          create => Google::Ads::GoogleAds::V8::Resources::Campaign->new({
              name => "Smart campaign #" . uniqid(),
              # Set the campaign status as PAUSED. The campaign is the only
              # entity in the mutate request that should have its status set.
              status => PAUSED,
              # AdvertisingChannelType must be SMART.
              advertisingChannelType => SMART,
              # AdvertisingChannelSubType must be SMART_CAMPAIGN.
              advertisingChannelSubType =>
                Google::Ads::GoogleAds::V8::Enums::AdvertisingChannelSubTypeEnum::SMART_CAMPAIGN,
              # Assign the resource name with a temporary ID.
              resourceName =>
                Google::Ads::GoogleAds::V8::Utils::ResourceNames::campaign(
                $customer_id, SMART_CAMPAIGN_TEMPORARY_ID
                ),
              # Set the budget using the given budget resource name.
              campaignBudget =>
                Google::Ads::GoogleAds::V8::Utils::ResourceNames::campaign_budget(
                $customer_id, BUDGET_TEMPORARY_ID
                )})})});
}

# Creates a MutateOperation to create a new SmartCampaignSetting.
# SmartCampaignSettings are unique in that they only support UPDATE operations,
# which are used to update and create them. Below we will use a temporary ID in
# the resource name to associate it with the campaign created in the previous
# step.
sub _create_smart_campaign_setting_operation {
  my ($customer_id, $business_location_id, $business_name) = @_;

  my $smart_campaign_setting =
    Google::Ads::GoogleAds::V8::Resources::SmartCampaignSetting->new({
      # Set a temporary ID in the campaign setting's resource name to associate it
      # with the campaign created in the previous step.
      resourceName =>
        Google::Ads::GoogleAds::V8::Utils::ResourceNames::smart_campaign_setting(
        $customer_id, SMART_CAMPAIGN_TEMPORARY_ID
        ),
      # Below we configure the SmartCampaignSetting using many of the same
      # details used to generate a budget suggestion.
      phoneNumber => Google::Ads::GoogleAds::V8::Resources::PhoneNumber->new({
          countryCode => COUNTRY_CODE,
          phoneNumber => PHONE_NUMBER
        }
      ),
      finalUrl                => LANDING_PAGE_URL,
      advertisingLanguageCode => LANGUAGE_CODE
    });

  # Either a business location ID or a business name must be added to the
  # SmartCampaignSetting.
  if (defined($business_location_id)) {
    $smart_campaign_setting->{businessLocationId} = $business_location_id;
  } else {
    $smart_campaign_setting->{businessName} = $business_name;
  }

  return
    Google::Ads::GoogleAds::V8::Services::GoogleAdsService::MutateOperation->
    new({
      smartCampaignSettingOperation =>
        Google::Ads::GoogleAds::V8::Services::SmartCampaignSettingService::SmartCampaignSettingOperation
        ->new({
          update => $smart_campaign_setting,
          # Set the update mask on the operation. This is required since the
          # smart campaign setting is created in an UPDATE operation. Here the
          # update mask will be a list of all the fields that were set on the
          # SmartCampaignSetting.
          updateMask => all_set_fields_of($smart_campaign_setting)})});
}

# Creates a list of MutateOperations that create new campaign criteria.
sub _create_campaign_criterion_operations {
  my ($customer_id, $keyword_theme_infos) = @_;

  my $campaign_criterion_operations = [];

  foreach my $keyword_theme_info (@$keyword_theme_infos) {
    push @$campaign_criterion_operations,
      Google::Ads::GoogleAds::V8::Services::GoogleAdsService::MutateOperation->
      new({
        campaignCriterionOperation =>
          Google::Ads::GoogleAds::V8::Services::CampaignCriterionService::CampaignCriterionOperation
          ->new({
            create =>
              Google::Ads::GoogleAds::V8::Resources::CampaignCriterion->new({
                # Set the campaign ID to a temporary ID.
                campaign =>
                  Google::Ads::GoogleAds::V8::Utils::ResourceNames::campaign(
                  $customer_id, SMART_CAMPAIGN_TEMPORARY_ID
                  ),
                # Set the criterion type to KEYWORD_THEME.
                type => KEYWORD_THEME,
                # Set the keyword theme to each KeywordThemeInfo in turn.
                keywordTheme => $keyword_theme_info
              })})});
  }

  return @$campaign_criterion_operations;
}

# Creates a MutateOperation that creates a new ad group. A temporary ID will be
# used in the campaign resource name for this ad group to associate it with the
# Smart campaign created in earlier steps. A temporary ID will also be used for
# its own resource name so that we can associate an ad group ad with it later in
# the process. Only one ad group can be created for a given Smart campaign.
sub _create_ad_group_operation {
  my ($customer_id) = @_;

  return
    Google::Ads::GoogleAds::V8::Services::GoogleAdsService::MutateOperation->
    new({
      adGroupOperation =>
        Google::Ads::GoogleAds::V8::Services::AdGroupService::AdGroupOperation
        ->new({
          create => Google::Ads::GoogleAds::V8::Resources::AdGroup->new({
              # Set the ad group ID to a temporary ID.
              resourceName =>
                Google::Ads::GoogleAds::V8::Utils::ResourceNames::ad_group(
                $customer_id, AD_GROUP_TEMPORARY_ID
                ),
              name => "Smart campaign ad group #" . uniqid(),
              # Set the campaign ID to a temporary ID.
              campaign =>
                Google::Ads::GoogleAds::V8::Utils::ResourceNames::campaign(
                $customer_id, SMART_CAMPAIGN_TEMPORARY_ID
                ),
              # The ad group type must be SMART_CAMPAIGN_ADS.
              type => SMART_CAMPAIGN_ADS
            })})});
}

# Creates a MutateOperation that creates a new ad group ad.
# A temporary ID will be used in the ad group resource name for this ad group
# ad to associate it with the ad group created in earlier steps.
sub _create_ad_group_ad_operation {
  my ($customer_id) = @_;

  return
    Google::Ads::GoogleAds::V8::Services::GoogleAdsService::MutateOperation->
    new({
      adGroupAdOperation =>
        Google::Ads::GoogleAds::V8::Services::AdGroupAdService::AdGroupAdOperation
        ->new({
          create => Google::Ads::GoogleAds::V8::Resources::AdGroupAd->new({
              adGroup =>
                Google::Ads::GoogleAds::V8::Utils::ResourceNames::ad_group(
                $customer_id, AD_GROUP_TEMPORARY_ID
                ),
              ad => Google::Ads::GoogleAds::V8::Resources::Ad->new({
                  # Set the type to SMART_CAMPAIGN_AD.
                  type            => SMART_CAMPAIGN_AD,
                  smartCampaignAd =>
                    Google::Ads::GoogleAds::V8::Common::SmartCampaignAdInfo->
                    new({
                      # At most, three headlines can be specified for a Smart
                      # campaign ad.
                      headlines => [
                        Google::Ads::GoogleAds::V8::Common::AdTextAsset->new({
                            text => "Headline number one"
                          }
                        ),
                        Google::Ads::GoogleAds::V8::Common::AdTextAsset->new({
                            text => "Headline number two"
                          }
                        ),
                        Google::Ads::GoogleAds::V8::Common::AdTextAsset->new({
                            text => "Headline number three"
                          })
                      ],
                      # At most, two descriptions can be specified for a Smart
                      # campaign ad.
                      descriptions => [
                        Google::Ads::GoogleAds::V8::Common::AdTextAsset->new(
                          {text => "Description number one"}
                        ),
                        Google::Ads::GoogleAds::V8::Common::AdTextAsset->new(
                          {text => "Description number two"})]})})})})});
}

# Prints the details of a MutateGoogleAdsResponse.
sub _print_response_details {
  my ($response) = @_;

  my $mutate_operation_responses = $response->{mutateOperationResponses};

  foreach my $operation_response (@$mutate_operation_responses) {
    my $resource_name = "<not found>";
    my $entity_name   = "unknown";

    if ($operation_response->{adGroupResult}) {
      $entity_name   = "AdGroup";
      $resource_name = $operation_response->{adGroupResult}{resourceName};
    } elsif ($operation_response->{adGroupAdResult}) {
      $entity_name   = "AdGroupAd";
      $resource_name = $operation_response->{adGroupAdResult}{resourceName};
    } elsif ($operation_response->{campaignResult}) {
      $entity_name   = "Campaign";
      $resource_name = $operation_response->{campaignResult}{resourceName};
    } elsif ($operation_response->{campaignBudgetResult}) {
      $entity_name = "CampaignBudget";
      $resource_name =
        $operation_response->{campaignBudgetResult}{resourceName};
    } elsif ($operation_response->{campaignCriterionResult}) {
      $entity_name = "CampaignCriterion";
      $resource_name =
        $operation_response->{campaignCriterionResult}{resourceName};
    } elsif ($operation_response->{smartCampaignSettingResult}) {
      $entity_name = "SmartCampaignSetting";
      $resource_name =
        $operation_response->{smartCampaignSettingResult}{resourceName};
    }

    printf "Created a(n) $entity_name with resource name '$resource_name'.\n";
  }
}

# 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();

# 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,
  "keyword_text=s"         => \$keyword_text,
  "business_location_id=s" => \$business_location_id,
  "business_name=s"        => \$business_name
);

# 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);

# Call the example.
add_smart_campaign(
  $api_client,           $customer_id =~ s/-//gr, $keyword_text,
  $business_location_id, $business_name
);

=pod

=head1 NAME

add_smart_campaign

=head1 DESCRIPTION

This example shows how to create a Smart Campaign.

More details on Smart Campaigns can be found here:
https://support.google.com/google-ads/answer/7652860

=head1 SYNOPSIS

add_smart_campaign.pl [options]

    -help                       Show the help message.
    -customer_id                The Google Ads customer ID.
    -keyword_text               A keyword text used to generate a set of keyword themes,
                                which are used to improve the budget recommendation and
                                performance of the Smart Campaign. Default value is defined
                                in the DEFAULT_KEYWORD_TEXT constant.
    -business_location_id       The ID of a Google My Business (GMB) location.
                                This is required if a business name is not provided.
                                This ID can be retrieved using the GMB API, for details see:
                                https://developers.google.com/my-business/reference/rest/v4/accounts.locations
    -business_name              The name of a Google My Business (GMB) business.
                                This is required if a business location ID is not provided.

=cut