Agregar campaña inteligente

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.v17.common.AdScheduleInfo;
import com.google.ads.googleads.v17.common.AdTextAsset;
import com.google.ads.googleads.v17.common.KeywordThemeInfo;
import com.google.ads.googleads.v17.common.LocationInfo;
import com.google.ads.googleads.v17.common.SmartCampaignAdInfo;
import com.google.ads.googleads.v17.enums.AdGroupTypeEnum.AdGroupType;
import com.google.ads.googleads.v17.enums.AdTypeEnum.AdType;
import com.google.ads.googleads.v17.enums.AdvertisingChannelSubTypeEnum.AdvertisingChannelSubType;
import com.google.ads.googleads.v17.enums.AdvertisingChannelTypeEnum.AdvertisingChannelType;
import com.google.ads.googleads.v17.enums.BudgetDeliveryMethodEnum.BudgetDeliveryMethod;
import com.google.ads.googleads.v17.enums.BudgetTypeEnum.BudgetType;
import com.google.ads.googleads.v17.enums.CampaignStatusEnum.CampaignStatus;
import com.google.ads.googleads.v17.enums.DayOfWeekEnum.DayOfWeek;
import com.google.ads.googleads.v17.enums.MinuteOfHourEnum.MinuteOfHour;
import com.google.ads.googleads.v17.errors.GoogleAdsError;
import com.google.ads.googleads.v17.errors.GoogleAdsException;
import com.google.ads.googleads.v17.resources.Ad;
import com.google.ads.googleads.v17.resources.SmartCampaignSetting;
import com.google.ads.googleads.v17.services.GoogleAdsServiceClient;
import com.google.ads.googleads.v17.services.KeywordThemeConstantServiceClient;
import com.google.ads.googleads.v17.services.MutateGoogleAdsResponse;
import com.google.ads.googleads.v17.services.MutateOperation;
import com.google.ads.googleads.v17.services.MutateOperationResponse;
import com.google.ads.googleads.v17.services.SmartCampaignSuggestServiceClient;
import com.google.ads.googleads.v17.services.SmartCampaignSuggestionInfo;
import com.google.ads.googleads.v17.services.SmartCampaignSuggestionInfo.BusinessContext;
import com.google.ads.googleads.v17.services.SmartCampaignSuggestionInfo.LocationList;
import com.google.ads.googleads.v17.services.SuggestKeywordThemeConstantsRequest;
import com.google.ads.googleads.v17.services.SuggestKeywordThemeConstantsResponse;
import com.google.ads.googleads.v17.services.SuggestKeywordThemesRequest;
import com.google.ads.googleads.v17.services.SuggestKeywordThemesResponse;
import com.google.ads.googleads.v17.services.SuggestKeywordThemesResponse.KeywordTheme;
import com.google.ads.googleads.v17.services.SuggestSmartCampaignAdRequest;
import com.google.ads.googleads.v17.services.SuggestSmartCampaignAdResponse;
import com.google.ads.googleads.v17.services.SuggestSmartCampaignBudgetOptionsRequest;
import com.google.ads.googleads.v17.services.SuggestSmartCampaignBudgetOptionsResponse;
import com.google.ads.googleads.v17.services.SuggestSmartCampaignBudgetOptionsResponse.BudgetOption;
import com.google.ads.googleads.v17.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;
import java.util.stream.Stream;

/** Demonstrates how to create a Smart Campaign. */
public class AddSmartCampaign {
  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 = "800-555-0100";
  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 final int NUM_REQUIRED_HEADLINES = 3;
  private static final int NUM_REQUIRED_DESCRIPTIONS = 2;

  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 retrieve keyword theme constant suggestions from the"
                + " KeywordThemeConstantService. These keyword theme suggestions are generated"
                + " using auto-completion data for the given text and may help improve the"
                + " performance of the Smart campaign.")
    private String keywordText;

    @Parameter(
        names = ArgumentNames.FREE_FORM_KEYWORD_TEXT,
        description =
            "A keyword text used to create a freeForm keyword theme, which is entirely"
                + " user-specified and not derived from any suggestion service. Using free-form"
                + " keyword themes is typically not recommended because they are less effective"
                + " than suggested keyword themes, however they are useful in situations where a"
                + " very specific term needs to be targeted.")
    private String freeFormKeywordText;

    @Parameter(
        names = ArgumentNames.BUSINESS_PROFILE_LOCATION,
        description =
            "The resource name of a Business Profile location. This is required if a business name"
                + " is not provided. It can be retrieved using the Business Profile API"
                + " (https://developers.google.com/my-business/reference/businessinformation/rest/v1/accounts.locations)"
                + " or from the Business Profile UI"
                + " (https://support.google.com/business/answer/10737668")
    private String businessProfileLocation;

    @Parameter(
        names = ArgumentNames.BUSINESS_NAME,
        description =
            "The name of a Business Profile 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");

      // Optionally specifies a seed keyword.
      params.keywordText = null;
      // Optionally specifies a keyword that should be included as-is.
      params.freeFormKeywordText = null;

      // Must specify one of business profile location or business name.
      params.businessProfileLocation = "INSERT_BUSINESS_PROFILE_LOCATION_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.keywordText,
              params.freeFormKeywordText,
              params.businessProfileLocation,
              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 keywordText,
      String freeFormKeywordText,
      String businessProfileLocation,
      String businessName) {
    // Checks that exactly one of businessProfileLocation and businessName is set.
    if (businessProfileLocation != null && businessName != null) {
      throw new IllegalArgumentException(
          "Both the business location resource name and business name are provided but they are"
              + " mutually exclusive. Please only set a value for one of them.");
    }
    if (businessProfileLocation == null && businessName == null) {
      throw new IllegalArgumentException(
          "Neither the business location resource name nor the business name are provided. Please"
              + " set a value for one of them.");
    }

    // Gets the SmartCampaignSuggestionInfo object which acts as the basis for many of the
    // entities necessary to create a Smart campaign. It will be reused a number of times to
    // retrieve suggestions for keyword themes, budget amount, ad creatives, and campaign criteria.
    SmartCampaignSuggestionInfo suggestionInfo =
        getSmartCampaignSuggestionInfo(googleAdsClient, businessProfileLocation, businessName);

    // Generates a list of keyword themes using the SuggestKeywordThemes method on the
    // SmartCampaignSuggestService. It is strongly recommended that you use this strategy for
    // generating keyword themes.
    List<KeywordTheme> keywordThemes =
        getKeywordThemeSuggestions(googleAdsClient, customerId, suggestionInfo);

    // If a keyword text is given, retrieves keyword theme constant suggestions from the
    // KeywordThemeConstantService, maps them to KeywordThemes, and appends them to the existing
    // list.
    // This logic should ideally only be used if the suggestions from the
    // getKeywordThemeSuggestions function are insufficient.
    if (keywordText != null) {
      keywordThemes.addAll(getKeywordTextAutoCompletions(googleAdsClient, keywordText));
    }

    // Converts the list of KeywordThemes to a list of KeywordThemes objects.
    List<KeywordThemeInfo> keywordThemeInfos = getKeywordThemeInfos(keywordThemes);

    // Optionally includes any freeForm keywords in verbatim.
    if (freeFormKeywordText != null) {
      keywordThemeInfos.add(
          KeywordThemeInfo.newBuilder().setFreeFormKeywordTheme(freeFormKeywordText).build());
    }

    // Includes the keyword suggestions in the overall SuggestionInfo object.
    suggestionInfo = suggestionInfo.toBuilder().addAllKeywordThemes(keywordThemeInfos).build();

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

    // Creates adSuggestions.
    SmartCampaignAdInfo adSuggestions =
        getAdSuggestions(googleAdsClient, customerId, suggestionInfo);

    // 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, businessProfileLocation, businessName),
                createAdGroupOperation(customerId),
                createAdGroupAdOperation(customerId, adSuggestions)));
    operations.addAll(
        createCampaignCriterionOperations(customerId, keywordThemeInfos, suggestionInfo));

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

  /**
   * Retrieves KeywordThemes using the given suggestion info.
   *
   * <p>Here we use the SuggestKeywordThemes method, which uses all of the business details included
   * in the given SmartCampaignSuggestionInfo instance to generate keyword theme suggestions. This
   * is the recommended way to generate keyword themes because it uses detailed information about
   * your business, its location, and website content to generate keyword themes.
   */
  private List<KeywordTheme> getKeywordThemeSuggestions(
      GoogleAdsClient googleAdsClient,
      long customerId,
      SmartCampaignSuggestionInfo suggestionInfo) {
    // Creates the service client.
    try (SmartCampaignSuggestServiceClient client =
        googleAdsClient.getLatestVersion().createSmartCampaignSuggestServiceClient()) {
      // Sends the request.
      SuggestKeywordThemesResponse response =
          client.suggestKeywordThemes(
              SuggestKeywordThemesRequest.newBuilder()
                  .setSuggestionInfo(suggestionInfo)
                  .setCustomerId(String.valueOf(customerId))
                  .build());
      // Prints some information about the result.
      System.out.printf(
          "Retrieved %d keyword theme suggestions from the SuggestKeywordThemes method.%n",
          response.getKeywordThemesCount());
      return new ArrayList(response.getKeywordThemesList());
    }
  }

  /**
   * Retrieves KeywordThemeConstants that are derived from autocomplete data for the given keyword
   * text, which are converted to a list of KeywordTheme objects before being returned.
   */
  private List<KeywordTheme> getKeywordTextAutoCompletions(
      GoogleAdsClient googleAdsClient, String keywordText) {
    try (KeywordThemeConstantServiceClient client =
        googleAdsClient.getLatestVersion().createKeywordThemeConstantServiceClient()) {
      SuggestKeywordThemeConstantsRequest request =
          SuggestKeywordThemeConstantsRequest.newBuilder()
              .setQueryText(keywordText)
              .setCountryCode(COUNTRY_CODE)
              .setLanguageCode(LANGUAGE_CODE)
              .build();
      SuggestKeywordThemeConstantsResponse response = client.suggestKeywordThemeConstants(request);
      // Converts the keyword theme constants to KeywordTheme instances for consistency with the
      // response from SmartCampaignSuggestService.SuggestKeywordThemes.
      return response.getKeywordThemeConstantsList().stream()
          .map(
              keywordThemeConstant ->
                  KeywordTheme.newBuilder().setKeywordThemeConstant(keywordThemeConstant).build())
          .collect(Collectors.toList());
    }
  }

  /**
   * Builds a SmartCampaignSuggestionInfo object with business details.
   *
   * <p>The details are used by the SmartCampaignSuggestService to suggest a budget amount as well
   * as creatives for the ad.
   *
   * <p>Note that when retrieving ad creative suggestions it's required that the "final_url",
   * "language_code" and "keyword_themes" fields are set on the SmartCampaignSuggestionInfo
   * instance.
   *
   * @return SmartCampaignSuggestionInfo a SmartCampaignSuggestionInfo instance.
   */
  private SmartCampaignSuggestionInfo getSmartCampaignSuggestionInfo(
      GoogleAdsClient googleAdsClient, String businessProfileLocation, String businessName) {
    SmartCampaignSuggestionInfo.Builder suggestionInfoBuilder =
        SmartCampaignSuggestionInfo.newBuilder()
            // Adds the URL of the campaign's landing page.
            .setFinalUrl(LANDING_PAGE_URL)
            // Adds the language code for the campaign.
            .setLanguageCode(LANGUAGE_CODE)
            // Constructs location information using the given geo target constant. It's also
            // possible to provide a geographic proximity using the "proximity" field,
            // for example:
            // .setProximity(
            //     ProximityInfo.newBuilder()
            //         .setAddress(
            //             AddressInfo.newBuilder()
            //                 .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)
            //                 .build())
            //         .setRadius(INSERT_RADIUS)
            //         .setRadiusUnits(INSERT_RADIUS_UNITS)
            //         .build())
            // 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.
            .setLocationList(
                LocationList.newBuilder()
                    // Sets one location to the resource name of the given geo target constant.
                    .addLocations(
                        LocationInfo.newBuilder()
                            .setGeoTargetConstant(
                                ResourceNames.geoTargetConstant(GEO_TARGET_CONSTANT))
                            .build())
                    .build())
            // 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.
            .addAdSchedules(
                AdScheduleInfo.newBuilder()
                    // Sets the day of this schedule as Monday.
                    .setDayOfWeek(DayOfWeek.MONDAY)
                    // Sets the start hour to 9am.
                    .setStartHour(9)
                    // Sets the end hour to 5pm.
                    .setEndHour(17)
                    // Sets the start and end minute of zero, for example: 9:00 and 5:00.
                    .setStartMinute(MinuteOfHour.ZERO)
                    .setEndMinute(MinuteOfHour.ZERO)
                    .build());

    // Sets either of the business_profile_location or business_name, depending on whichever is
    // provided.
    if (businessProfileLocation != null) {
      suggestionInfoBuilder.setBusinessProfileLocation(businessProfileLocation);
    } else {
      suggestionInfoBuilder.setBusinessContext(
          BusinessContext.newBuilder().setBusinessName(businessName).build());
    }
    return suggestionInfoBuilder.build();
  }

  /**
   * 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,
      SmartCampaignSuggestionInfo suggestionInfo) {
    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.setSuggestionInfo(suggestionInfo);

    // 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();
    }
  }

  /**
   * Retrieves creative suggestions for a Smart campaign ad.
   *
   * <p>Using the SmartCampaignSuggestService to suggest creatives for new and existing Smart
   * campaigns is highly recommended because it helps the campaigns achieve optimal performance.
   *
   * @return SmartCampaignAdInfo a SmartCampaignAdInfo instance with suggested headlines and
   *     descriptions.
   */
  private SmartCampaignAdInfo getAdSuggestions(
      GoogleAdsClient googleAdsClient,
      long customerId,
      SmartCampaignSuggestionInfo suggestionInfo) {
    // Unlike the SuggestSmartCampaignBudgetOptions method, it's only possible to use
    // suggestion_info to retrieve ad creative suggestions.

    // Issues a request to retrieve ad creative suggestions.
    try (SmartCampaignSuggestServiceClient smartCampaignSuggestService =
        googleAdsClient.getLatestVersion().createSmartCampaignSuggestServiceClient()) {
      SuggestSmartCampaignAdResponse response =
          smartCampaignSuggestService.suggestSmartCampaignAd(
              SuggestSmartCampaignAdRequest.newBuilder()
                  .setCustomerId(Long.toString(customerId))
                  .setSuggestionInfo(suggestionInfo)
                  .build());

      // The SmartCampaignAdInfo object in the response contains a list of up to three headlines
      // and two descriptions. Note that some of the suggestions may have empty strings as text.
      // Before setting these on the ad you should review them and filter out any empty values.
      SmartCampaignAdInfo adSuggestions = response.getAdInfo();
      for (AdTextAsset headline : adSuggestions.getHeadlinesList()) {
        System.out.println(!headline.getText().isEmpty() ? headline.getText() : "None");
      }
      for (AdTextAsset description : adSuggestions.getDescriptionsList()) {
        System.out.println(!description.getText().isEmpty() ? description.getText() : "None");
      }
      return adSuggestions;
    }
  }

  /**
   * 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.
   */
  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, String businessProfileLocation, String businessName) {
    MutateOperation.Builder builder = MutateOperation.newBuilder();
    SmartCampaignSetting.Builder settingBuilder =
        builder
            .getSmartCampaignSettingOperationBuilder()
            .getUpdateBuilder()
            // Sets a temporary ID in the campaign setting's resource name to associate it with
            // the campaign created in the previous step.
            .setResourceName(
                ResourceNames.smartCampaignSetting(customerId, SMART_CAMPAIGN_TEMPORARY_ID));
    // Configures the SmartCampaignSetting using many of the same details used to
    // generate a budget suggestion.
    settingBuilder
        .setFinalUrl(LANDING_PAGE_URL)
        .setAdvertisingLanguageCode(LANGUAGE_CODE)
        .getPhoneNumberBuilder()
        .setCountryCode(COUNTRY_CODE)
        .setPhoneNumber(PHONE_NUMBER);

    // It's required that either a business profile location resource name or a business name is
    // added to the SmartCampaignSetting.
    if (businessProfileLocation != null) {
      settingBuilder.setBusinessProfileLocation(businessProfileLocation);
    } 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, SmartCampaignAdInfo adSuggestions) {
    MutateOperation.Builder opBuilder = MutateOperation.newBuilder();

    // Constructs an Ad instance containing a SmartCampaignAd.
    Ad.Builder adBuilder = Ad.newBuilder();
    adBuilder
        .setType(AdType.SMART_CAMPAIGN_AD)
        // The SmartCampaignAdInfo object includes headlines and descriptions retrieved
        // from the suggestSmartCampaignAd method. It's recommended that users review and approve or
        // update these creatives before they're set on the ad. It's possible that some or all of
        // these assets may contain empty texts, which should not be set on the ad and instead
        // should be replaced with meaningful texts from the user. Below we just accept the
        // creatives that were suggested while filtering out empty assets, but individual workflows
        // will vary here.
        .getSmartCampaignAdBuilder()
        .addAllHeadlines(
            adSuggestions.getHeadlinesList().stream()
                .filter(h -> h.hasText())
                .collect(Collectors.toList()))
        .addAllDescriptions(
            adSuggestions.getDescriptionsList().stream()
                .filter(d -> d.hasText())
                .collect(Collectors.toList()));

    // Adds additional headlines + descriptions if we didn't get enough back from the suggestion
    // service.
    int numHeadlines = adBuilder.getSmartCampaignAdBuilder().getHeadlinesCount();
    if (numHeadlines < NUM_REQUIRED_HEADLINES) {
      for (int i = 0; i < NUM_REQUIRED_HEADLINES - numHeadlines; ++i) {
        adBuilder
            .getSmartCampaignAdBuilder()
            .addHeadlines(AdTextAsset.newBuilder().setText("Placeholder headline " + i).build());
      }
    }
    if (adSuggestions.getDescriptionsCount() < NUM_REQUIRED_DESCRIPTIONS) {
      int numDescriptions = adBuilder.getSmartCampaignAdBuilder().getDescriptionsCount();
      for (int i = 0; i < NUM_REQUIRED_DESCRIPTIONS - numDescriptions; ++i) {
        adBuilder
            .getSmartCampaignAdBuilder()
            .addDescriptions(
                AdTextAsset.newBuilder().setText("Placeholder description " + i).build());
      }
    }

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

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

    List<MutateOperation> locationOperations =
        suggestionInfo.getLocationList().getLocationsList().stream()
            .map(
                location -> {
                  MutateOperation.Builder builder = MutateOperation.newBuilder();
                  builder
                      .getCampaignCriterionOperationBuilder()
                      .getCreateBuilder()
                      .setCampaign(ResourceNames.campaign(customerId, SMART_CAMPAIGN_TEMPORARY_ID))
                      .setLocation(location);
                  return builder.build();
                })
            .collect(Collectors.toList());

    return Stream.concat(keywordThemeOperations.stream(), locationOperations.stream())
        .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 a list of {@link KeywordTheme} objects to a list of {@link
   * KeywordThemeInfo} objects.
   */
  private List<KeywordThemeInfo> getKeywordThemeInfos(List<KeywordTheme> keywordThemes) {
    return keywordThemes.stream()
        .map(
            keywordTheme -> {
              KeywordThemeInfo.Builder keywordThemeInfoBuilder = KeywordThemeInfo.newBuilder();
              // Checks if the keyword_theme_constant field is set.
              if (keywordTheme.hasKeywordThemeConstant()) {
                return keywordThemeInfoBuilder
                    .setKeywordThemeConstant(
                        keywordTheme.getKeywordThemeConstant().getResourceName())
                    .build();
              } else if (keywordTheme.hasFreeFormKeywordTheme()) {
                return keywordThemeInfoBuilder
                    .setFreeFormKeywordTheme(keywordTheme.getFreeFormKeywordTheme())
                    .build();
              } else {
                throw new IllegalArgumentException(
                    String.format("A malformed KeywordTheme was encountered: %s", keywordTheme));
              }
            })
        .collect(Collectors.toCollection(ArrayList::new));
  }
}

      

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.Gax.Examples;
using Google.Ads.Gax.Util;
using Google.Ads.GoogleAds.Lib;
using Google.Ads.GoogleAds.V17.Common;
using Google.Ads.GoogleAds.V17.Enums;
using Google.Ads.GoogleAds.V17.Errors;
using Google.Ads.GoogleAds.V17.Resources;
using Google.Ads.GoogleAds.V17.Services;
using static Google.Ads.GoogleAds.V17.Enums.AdGroupTypeEnum.Types;
using static Google.Ads.GoogleAds.V17.Enums.AdvertisingChannelTypeEnum.Types;
using static Google.Ads.GoogleAds.V17.Enums.AdvertisingChannelSubTypeEnum.Types;
using static Google.Ads.GoogleAds.V17.Enums.BudgetTypeEnum.Types;
using static Google.Ads.GoogleAds.V17.Enums.CampaignStatusEnum.Types;
using static Google.Ads.GoogleAds.V17.Services.SmartCampaignSuggestionInfo.Types;

namespace Google.Ads.GoogleAds.Examples.V17
{
    /// <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 retrieve keyword theme constant suggestions from the
            /// KeywordThemeConstantService. These keyword theme suggestions are generated
            /// using auto-completion data for the given text and may help improve the
            /// performance of the Smart campaign.
            /// </summary>
            [Option("keywordText", Required = false, HelpText =
                "A keyword text used to retrieve keyword theme constant suggestions from the " +
                "KeywordThemeConstantService. These keyword theme suggestions are generated " +
                "using auto-completion data for the given text and may help improve the " +
                "performance of the Smart campaign.",
                Default = DEFAULT_KEYWORD_TEXT)]
            public string Keyword { get; set; }

            /// <summary>
            /// A keyword text used to create a freeform keyword theme, which is entirely
            /// user-specified and not derived from any suggestion service. Using free-form
            /// keyword themes is typically not recommended because they are less effective
            /// than suggested keyword themes, however they are useful in situations where a
            /// very specific term needs to be targeted.
            /// </summary>
            [Option("freeformKeywordText", Required = false, HelpText =
                "A keyword text used to create a freeform keyword theme, which is entirely " +
                "user-specified and not derived from any suggestion service. Using free-form " +
                "keyword themes is typically not recommended because they are less effective " +
                "than suggested keyword themes, however they are useful in situations where a " +
                "very specific term needs to be targeted.",
                Default = DEFAULT_KEYWORD_TEXT)]
            public string FreeformKeywordText { get; set; }

            /// <summary>
            /// The identifier for a Business Profile location</summary>
            [Option("BusinessProfileLocation", Required = false, HelpText =
                "The identifier for a Business Profile location.")]
            public string BusinessProfileLocation { get; set; }

            /// <summary>
            /// The name of a Business Profile business. This is required if a business
            /// location ID is not provided.
            /// </summary>
            [Option("businessName", Required = false, HelpText =
                "The name of a Business Profile 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 = ExampleUtilities.ParseCommandLine<Options>(args);

            AddSmartCampaign codeExample = new AddSmartCampaign();
            Console.WriteLine(codeExample.Description);
            codeExample.Run(new GoogleAdsClient(), options.CustomerId, options.Keyword,
                options.FreeformKeywordText, options.BusinessProfileLocation,
                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 = "800-555-0100";
        private const long BUDGET_TEMPORARY_ID = -1;
        private const long SMART_CAMPAIGN_TEMPORARY_ID = -2;
        private const long AD_GROUP_TEMPORARY_ID = -3;
        private const int NUM_REQUIRED_HEADLINES = 3;
        private const int NUM_REQUIRED_DESCRIPTIONS = 2;

        /// <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="freeFormKeywordText">A keyword used to create a free-form keyword theme.
        /// </param>
        /// <param name="businessProfileLocation">The identifier of a Business Profile location.
        /// </param>
        /// <param name="businessName">The name of a Business Profile business.</param>
        public void Run(GoogleAdsClient client, long customerId, string keywordText,
            string freeFormKeywordText, string businessProfileLocation, string businessName)
        {
            GoogleAdsServiceClient googleAdsServiceClient =
                client.GetService(Services.V17.GoogleAdsService);

            try
            {
                // Gets the SmartCampaignSuggestionInfo object which acts as the basis for many
                // of the entities necessary to create a Smart campaign. It will be reused a number
                // of times to retrieve suggestions for keyword themes, budget amount, ad
                //creatives, and campaign criteria.
                SmartCampaignSuggestionInfo suggestionInfo =
                    GetSmartCampaignSuggestionInfo(client, businessProfileLocation, businessName);

                // Generates a list of keyword themes using the SuggestKeywordThemes method on the
                // SmartCampaignSuggestService. It is strongly recommended that you use this
                // strategy for generating keyword themes.
                List<KeywordThemeConstant> keywordThemeConstants =
                    GetKeywordThemeSuggestions(client, customerId, suggestionInfo);

                // Optionally retrieves auto-complete suggestions for the given keyword text and
                // adds them to the list of keyWordThemeConstants.
                if (keywordText != null)
                {
                    keywordThemeConstants.AddRange(GetKeywordTextAutoCompletions(
                        client, keywordText));
                }

                // Converts the KeywordThemeConstants to KeywordThemeInfos.
                List<KeywordThemeInfo> keywordThemeInfos = keywordThemeConstants.Select(
                    constant =>
                        new KeywordThemeInfo { KeywordThemeConstant = constant.ResourceName })
                    .ToList();

                // Optionally includes any freeform keywords verbatim.
                if (freeFormKeywordText != null)
                {
                    keywordThemeInfos.Add(new KeywordThemeInfo()
                    {
                        FreeFormKeywordTheme = freeFormKeywordText
                    });
                }

                // Includes the keyword suggestions in the overall SuggestionInfo object.
                suggestionInfo.KeywordThemes.Add(keywordThemeInfos);

                SmartCampaignAdInfo adSuggestions = GetAdSuggestions(client, customerId,
                    suggestionInfo);

                long suggestedBudgetAmount = GetBudgetSuggestion(client, customerId,
                    suggestionInfo);

                // 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, businessProfileLocation,
                        businessName);
                IEnumerable<MutateOperation> campaignCriterionOperations =
                    CreateCampaignCriterionOperations(customerId, keywordThemeInfos,
                        suggestionInfo);
                MutateOperation adGroupOperation = CreateAdGroupOperation(customerId);
                MutateOperation adGroupAdOperation = CreateAdGroupAdOperation(customerId,
                    adSuggestions);

                // 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 KeywordThemeConstants suggestions with the SmartCampaignSuggestService.
        /// </summary>
        /// <param name="client">The Google Ads client.</param>
        /// <param name="customerId">The Google Ads customer ID.</param>
        /// <param name="suggestionInfo">The suggestion information.</param>
        /// <returns>The suggestions.</returns>
        private List<KeywordThemeConstant> GetKeywordThemeSuggestions(
            GoogleAdsClient client, long customerId, SmartCampaignSuggestionInfo suggestionInfo)
        {
            SmartCampaignSuggestServiceClient smartCampaignSuggestService =
                client.GetService(Services.V17.SmartCampaignSuggestService);

            SuggestKeywordThemesRequest request = new SuggestKeywordThemesRequest()
            {
                SuggestionInfo = suggestionInfo,
                CustomerId = customerId.ToString()
            };

            SuggestKeywordThemesResponse response =
                smartCampaignSuggestService.SuggestKeywordThemes(request);

            // Prints some information about the result.
            Console.WriteLine($"Retrieved {response.KeywordThemes.Count} keyword theme " +
                $"constant suggestions from the SuggestKeywordThemes method.");
            return response.KeywordThemes.ToList().ConvertAll(x => x.KeywordThemeConstant);
        }


        /// <summary>
        /// Retrieves KeywordThemeConstants that are derived from autocomplete data for the
        /// given keyword text.
        /// </summary>
        /// <param name="client">The Google Ads client.</param>
        /// <param name="keywordText">A keyword used for generating keyword auto completions.
        /// </param>
        /// <returns>A list of KeywordThemeConstants.</returns>
        private IEnumerable<KeywordThemeConstant> GetKeywordTextAutoCompletions(
            GoogleAdsClient client, string keywordText)
        {
            KeywordThemeConstantServiceClient keywordThemeConstantServiceClient =
                client.GetService(Services.V17.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>
        /// Builds a SmartCampaignSuggestionInfo object with business details.
        ///
        /// The details are used by the SmartCampaignSuggestService to suggest a
        /// budget amount as well as creatives for the ad.
        ///
        /// Note that when retrieving ad creative suggestions it's required that the
        /// "final_url", "language_code" and "keyword_themes" fields are set on the
        /// SmartCampaignSuggestionInfo instance.
        /// </summary>
        /// <param name="client">The Google Ads client.</param>
        /// <param name="businessProfileLocation">The identifier of a Business Profile location.
        /// </param>
        /// <param name="businessName">The name of a Business Profile.</param>
        /// <returns>A SmartCampaignSuggestionInfo instance .</returns>
        private SmartCampaignSuggestionInfo GetSmartCampaignSuggestionInfo(GoogleAdsClient client,
            string businessProfileLocation, string businessName)
        {
            // Note: This is broken since businessLocationId is not yet renamed in
            // SmartCampaignSuggestionInfo. The use of dynamic temporarily fixes the broken build.
            // TODO(Anash): Revert the type change once this field is fixed.
            dynamic suggestionInfo = new SmartCampaignSuggestionInfo
            {
                // Add the URL of the campaign's landing page.
                FinalUrl = LANDING_PAGE_URL,
                LanguageCode = LANGUAGE_CODE,
                // 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 LocationList()
                {
                    Locations =
                    {
                        new LocationInfo
                        {
                            // Set the location to the resource name of the given geo target
                            // constant.
                            GeoTargetConstant =
                                ResourceNames.GeoTargetConstant(GEO_TARGET_CONSTANT)
                        }
                    }
                }
            };

            // Add the Business Profile location if provided.
            if (!string.IsNullOrEmpty(businessProfileLocation))
            {
                suggestionInfo.BusinessProfileLocation = businessProfileLocation;
            }
            else
            {
                suggestionInfo.BusinessContext = new BusinessContext
                {
                    BusinessName = businessName,
                };
            }

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

            suggestionInfo.AdSchedules.Add(adScheduleInfo);

            return suggestionInfo;
        }

        /// <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="suggestionInfo"></param>
        /// <returns>A daily budget amount in micros.</returns>
        private long GetBudgetSuggestion(GoogleAdsClient client, long customerId,
            SmartCampaignSuggestionInfo suggestionInfo)
        {
            SmartCampaignSuggestServiceClient smartCampaignSuggestServiceClient = client.GetService
                (Services.V17.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 = suggestionInfo,
                };

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

            // 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>
        /// Retrieves creative suggestions for a Smart campaign ad.
        ///
        /// Using the SmartCampaignSuggestService to suggest creatives for new
        /// and existing Smart campaigns is highly recommended because it helps
        /// the campaigns achieve optimal performance.
        /// </summary>
        /// <param name="client"></param>
        /// <param name="customerId">The Google Ads customer ID.</param>
        /// <param name="suggestionInfo">a SmartCampaignSuggestionInfo instance
        /// with details about the business being advertised.</param>
        /// <returns>A SmartCampaignAdInfo instance with suggested headlines and
        /// descriptions.</returns>
        private SmartCampaignAdInfo GetAdSuggestions(GoogleAdsClient client,
            long customerId, SmartCampaignSuggestionInfo suggestionInfo)
        {
            SmartCampaignSuggestServiceClient smartCampaignSuggestService =
              client.GetService(Services.V17.SmartCampaignSuggestService);

            SuggestSmartCampaignAdRequest request = new SuggestSmartCampaignAdRequest
            {
                CustomerId = customerId.ToString(),
                // Unlike the SuggestSmartCampaignBudgetOptions method, it's only possible to
                // use suggestion_info to retrieve ad creative suggestions.
                SuggestionInfo = suggestionInfo
            };

            // Issue a request to retrieve ad creative suggestions.
            SuggestSmartCampaignAdResponse response =
              smartCampaignSuggestService.SuggestSmartCampaignAd(request);

            // The SmartCampaignAdInfo object in the response contains a list of up to
            // three headlines and two descriptions. Note that some of the suggestions
            // may have empty strings as text. Before setting these on the ad you should
            // review them and filter out any empty values.
            SmartCampaignAdInfo adSuggestions = response.AdInfo;

            if (adSuggestions != null)
            {
                Console.WriteLine($"The following headlines were suggested:");
                foreach (AdTextAsset headline in adSuggestions.Headlines)
                {
                    Console.WriteLine($"\t{headline.Text}");
                }

                Console.WriteLine($"And the following descriptions were suggested:");
                foreach (AdTextAsset description in adSuggestions.Descriptions)
                {
                    Console.WriteLine($"\t{description.Text}");
                }
            }
            else
            {
                Console.WriteLine("No ad suggestions were found.");
                adSuggestions = new SmartCampaignAdInfo();
            }

            return adSuggestions;
        }

        /// <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="businessProfileLocation">The identifier of a Business Profile location.</param>
        /// <param name="businessName">The name of a Business Profile business.</param>
        /// <returns>A MutateOperation that creates a SmartCampaignSetting.</returns>
        private MutateOperation CreateSmartCampaignSettingOperation(long customerId,
            string businessProfileLocation, 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 profile location or a business name must be added to the
            // SmartCampaignSetting.
            if (!string.IsNullOrEmpty(businessProfileLocation))
            {
                // Transform Google Business Location ID to a compatible format before
                // passing it onto the API.
                smartCampaignSetting.BusinessProfileLocation = businessProfileLocation;
            }
            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>
        /// <param name="suggestionInfo">A SmartCampaignSuggestionInfo instance.</param>
        /// <returns>A list of MutateOperations that create new campaign criteria.</returns>
        private IEnumerable<MutateOperation> CreateCampaignCriterionOperations(long customerId,
            IEnumerable<KeywordThemeInfo> keywordThemeInfos, SmartCampaignSuggestionInfo
            suggestionInfo)
        {
            List<MutateOperation> mutateOperations = 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 keyword theme to each KeywordThemeInfo in turn.
                            KeywordTheme = keywordThemeInfo,
                        }
                    }
                }).ToList();

            // Create a location criterion for each location in the suggestion info.
            mutateOperations.AddRange(
                suggestionInfo.LocationList.Locations.Select(
                    locationInfo => 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 location to the given location.
                                Location = locationInfo
                            }
                        }
                    }).ToList()
            );
            return mutateOperations;
        }

        /// <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>
        /// <param name="adSuggestions">SmartCampaignAdInfo with ad creative
        /// suggestions.</param>
        /// <returns>A MutateOperation that creates a new ad group ad.</returns>
        private MutateOperation CreateAdGroupAdOperation(long customerId, SmartCampaignAdInfo
            adSuggestions)
        {
            AdGroupAd adGroupAd = new AdGroupAd
            {
                AdGroup = ResourceNames.AdGroup(customerId, AD_GROUP_TEMPORARY_ID),
                Ad = new Ad
                {
                    SmartCampaignAd = new SmartCampaignAdInfo(),
                },
            };

            SmartCampaignAdInfo ad = adGroupAd.Ad.SmartCampaignAd;

            // The SmartCampaignAdInfo object includes headlines and descriptions
            // retrieved from the SmartCampaignSuggestService.SuggestSmartCampaignAd
            // method. It's recommended that users review and approve or update these
            // creatives before they're set on the ad. It's possible that some or all of
            // these assets may contain empty texts, which should not be set on the ad
            // and instead should be replaced with meaninful texts from the user. Below
            // we just accept the creatives that were suggested while filtering out empty
            // assets. If no headlines or descriptions were suggested, then we manually
            // add some, otherwise this operation will generate an INVALID_ARGUMENT
            // error. Individual workflows will likely vary here.
            ad.Headlines.Add(adSuggestions.Headlines);
            ad.Descriptions.Add(adSuggestions.Descriptions);

            // If there are fewer headlines than are required, we manually add additional
            // headlines to make up for the difference.
            if (adSuggestions.Headlines.Count() < NUM_REQUIRED_HEADLINES)
            {
                for (int i = 0; i < NUM_REQUIRED_HEADLINES - adSuggestions.Headlines.Count(); i++)
                {
                    ad.Headlines.Add(new AdTextAsset()
                    {
                        Text = $"Placeholder headline {i + 1}"
                    });
                }
            }

            // If there are fewer descriptions than are required, we manually add
            // additional descriptions to make up for the difference.
            if (adSuggestions.Descriptions.Count() < NUM_REQUIRED_DESCRIPTIONS)
            {
                for (int i = 0; i < NUM_REQUIRED_DESCRIPTIONS -
                    adSuggestions.Descriptions.Count(); i++)
                {
                    ad.Descriptions.Add(new AdTextAsset()
                    {
                        Text = $"Placeholder description {i + 1}"
                    });
                }
            }

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

        /// <summary>
        /// Converts the business location ID from the format returned by Business Profile
        /// to the format expected by the API.
        /// </summary>
        /// <param name="businessLocationId">The business location identifier.</param>
        /// <returns>The transformed ID.</returns>
        private long ConvertBusinessLocationId(ulong businessLocationId)
        {
            // The business location ID is an unsigned 64-bit integer. However, the Google Ads API
            // expects a signed 64-bit integer. So we convert the unsigned ID into a signed ID,
            // while allowing an overflow, so that the ID wraps around if it is too large.
            return unchecked((long) businessLocationId);
        }

        /// <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\V17\GoogleAdsClient;
use Google\Ads\GoogleAds\Lib\V17\GoogleAdsClientBuilder;
use Google\Ads\GoogleAds\Lib\V17\GoogleAdsException;
use Google\Ads\GoogleAds\Util\FieldMasks;
use Google\Ads\GoogleAds\Util\V17\ResourceNames;
use Google\Ads\GoogleAds\V17\Common\AdScheduleInfo;
use Google\Ads\GoogleAds\V17\Common\AdTextAsset;
use Google\Ads\GoogleAds\V17\Common\KeywordThemeInfo;
use Google\Ads\GoogleAds\V17\Common\LocationInfo;
use Google\Ads\GoogleAds\V17\Common\SmartCampaignAdInfo;
use Google\Ads\GoogleAds\V17\Enums\AdGroupTypeEnum\AdGroupType;
use Google\Ads\GoogleAds\V17\Enums\AdTypeEnum\AdType;
use Google\Ads\GoogleAds\V17\Enums\AdvertisingChannelSubTypeEnum\AdvertisingChannelSubType;
use Google\Ads\GoogleAds\V17\Enums\AdvertisingChannelTypeEnum\AdvertisingChannelType;
use Google\Ads\GoogleAds\V17\Enums\BudgetTypeEnum\BudgetType;
use Google\Ads\GoogleAds\V17\Enums\CampaignStatusEnum\CampaignStatus;
use Google\Ads\GoogleAds\V17\Enums\DayOfWeekEnum\DayOfWeek;
use Google\Ads\GoogleAds\V17\Enums\MinuteOfHourEnum\MinuteOfHour;
use Google\Ads\GoogleAds\V17\Errors\GoogleAdsError;
use Google\Ads\GoogleAds\V17\Resources\Ad;
use Google\Ads\GoogleAds\V17\Resources\AdGroup;
use Google\Ads\GoogleAds\V17\Resources\AdGroupAd;
use Google\Ads\GoogleAds\V17\Resources\Campaign;
use Google\Ads\GoogleAds\V17\Resources\CampaignBudget;
use Google\Ads\GoogleAds\V17\Resources\CampaignCriterion;
use Google\Ads\GoogleAds\V17\Resources\KeywordThemeConstant;
use Google\Ads\GoogleAds\V17\Resources\SmartCampaignSetting;
use Google\Ads\GoogleAds\V17\Resources\SmartCampaignSetting\PhoneNumber;
use Google\Ads\GoogleAds\V17\Services\AdGroupAdOperation;
use Google\Ads\GoogleAds\V17\Services\AdGroupOperation;
use Google\Ads\GoogleAds\V17\Services\CampaignBudgetOperation;
use Google\Ads\GoogleAds\V17\Services\CampaignCriterionOperation;
use Google\Ads\GoogleAds\V17\Services\CampaignOperation;
use Google\Ads\GoogleAds\V17\Services\MutateGoogleAdsRequest;
use Google\Ads\GoogleAds\V17\Services\MutateGoogleAdsResponse;
use Google\Ads\GoogleAds\V17\Services\MutateOperation;
use Google\Ads\GoogleAds\V17\Services\MutateOperationResponse;
use Google\Ads\GoogleAds\V17\Services\SmartCampaignSettingOperation;
use Google\Ads\GoogleAds\V17\Services\SmartCampaignSuggestionInfo;
use Google\Ads\GoogleAds\V17\Services\SmartCampaignSuggestionInfo\BusinessContext;
use Google\Ads\GoogleAds\V17\Services\SmartCampaignSuggestionInfo\LocationList;
use Google\Ads\GoogleAds\V17\Services\SuggestKeywordThemeConstantsRequest;
use Google\Ads\GoogleAds\V17\Services\SuggestKeywordThemesRequest;
use Google\Ads\GoogleAds\V17\Services\SuggestKeywordThemesResponse\KeywordTheme;
use Google\Ads\GoogleAds\V17\Services\SuggestSmartCampaignAdRequest;
use Google\Ads\GoogleAds\V17\Services\SuggestSmartCampaignBudgetOptionsRequest;
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 a keyword text used to retrieve keyword theme constant suggestions from the
    // KeywordThemeConstantService. These keyword theme suggestions are generated using
    // auto-completion data for the given text and may help improve the performance of the Smart
    // campaign.
    private const KEYWORD_TEXT = null;
    // Optional: A keyword text used to create a free-form keyword theme, which is entirely
    // user-specified and not derived from any suggestion service. Using free-form keyword themes is
    // typically not recommended because they are less effective than suggested keyword themes,
    // however they are useful in situations where a very specific term needs to be targeted.
    private const FREE_FORM_KEYWORD_TEXT = null;
    // Optional: Specify the resource name of a Business Profile location. This is required if a
    // business name is not provided. It can be retrieved using the Business Profile API, see:
    // https://developers.google.com/my-business/reference/businessinformation/rest/v1/accounts.locations
    // or from the Business Profile UI (https://support.google.com/business/answer/10737668).
    private const BUSINESS_PROFILE_LOCATION = null;
    // Optional: Specify the name of a Business Profile business. This is required if a
    // business profile location 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 = '800-555-0100';
    private const BUDGET_TEMPORARY_ID = '-1';
    private const SMART_CAMPAIGN_TEMPORARY_ID = '-2';
    private const AD_GROUP_TEMPORARY_ID = '-3';
    // These define the minimum number of headlines and descriptions that are
    // required to create an ad group ad in a Smart campaign.
    private const NUM_REQUIRED_HEADLINES = 3;
    private const NUM_REQUIRED_DESCRIPTIONS = 2;

    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::FREE_FORM_KEYWORD_TEXT => GetOpt::OPTIONAL_ARGUMENT,
            ArgumentNames::BUSINESS_PROFILE_LOCATION => GetOpt::OPTIONAL_ARGUMENT,
            ArgumentNames::BUSINESS_NAME => GetOpt::OPTIONAL_ARGUMENT
        ]);
        $businessProfileLocation =
            $options[ArgumentNames::BUSINESS_PROFILE_LOCATION] ?: self::BUSINESS_PROFILE_LOCATION;
        $businessName = $options[ArgumentNames::BUSINESS_NAME] ?: self::BUSINESS_NAME;
        if ($businessProfileLocation && $businessName) {
            throw new InvalidArgumentException(
                'Both the business location resource name and business name are provided but they '
                . 'are mutually exclusive. Please only set a value for one of them.'
            );
        }
        if (!$businessProfileLocation && !$businessName) {
            throw new InvalidArgumentException(
                'Neither the business location resource name 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)
            // We set this value to true to show how to use GAPIC v2 source code. You can remove the
            // below line if you wish to use the old-style source code. Note that in that case, you
            // probably need to modify some parts of the code below to make it work.
            // For more information, see
            // https://developers.devsite.corp.google.com/google-ads/api/docs/client-libs/php/gapic.
            ->usingGapicV2Source(true)
            ->build();

        try {
            self::runExample(
                $googleAdsClient,
                $options[ArgumentNames::CUSTOMER_ID] ?: self::CUSTOMER_ID,
                $options[ArgumentNames::KEYWORD_TEXT] ?: self::KEYWORD_TEXT,
                $options[ArgumentNames::FREE_FORM_KEYWORD_TEXT] ?: self::FREE_FORM_KEYWORD_TEXT,
                $businessProfileLocation,
                $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|null $keywordText a keyword text used for generating keyword themes
     * @param string|null $freeFormKeywordText a keyword used to create a free-form keyword theme
     * @param string|null $businessProfileLocationResourceName the resource name of a Business
     *     Profile location
     * @param string|null $businessName the name of a Business Profile
     */
    public static function runExample(
        GoogleAdsClient $googleAdsClient,
        int $customerId,
        ?string $keywordText,
        ?string $freeFormKeywordText,
        ?string $businessProfileLocationResourceName,
        ?string $businessName
    ) {
        // Gets the SmartCampaignSuggestionInfo object which acts as the basis for many of the
        // entities necessary to create a Smart campaign. It will be reused a number of times to
        // retrieve suggestions for keyword themes, budget amount, ads, and campaign criteria.
        $suggestionInfo = self::getSmartCampaignSuggestionInfo(
            $businessProfileLocationResourceName,
            $businessName
        );

        // Generates a list of keyword themes using the SuggestKeywordThemes method on the
        // SmartCampaignSuggestService. It is strongly recommended that you use this strategy for
        // generating keyword themes.
        $keywordThemes =
            self::getKeywordThemeSuggestions($googleAdsClient, $customerId, $suggestionInfo);

        // Optionally retrieves auto-complete suggestions for the given keyword text and adds them
        // to the list of keyword themes.
        if (!empty($keywordText)) {
            $keywordThemes = array_merge(
                $keywordThemes,
                self::getKeywordTextAutoCompletions($googleAdsClient, $keywordText)
            );
        }

        // Maps the list of KeywordThemes to KeywordThemeInfos.
        $keywordThemeInfos = array_map(function (KeywordTheme $keywordTheme) {
            if ($keywordTheme->getKeywordThemeConstant()) {
                return new KeywordThemeInfo([
                    'keyword_theme_constant' => $keywordTheme->getKeywordThemeConstant()
                        ->getResourceName()
                ]);
            } elseif ($keywordTheme->getFreeFormKeywordTheme()) {
                return new KeywordThemeInfo([
                    'free_form_keyword_theme' => $keywordTheme->getFreeFormKeywordTheme()
                ]);
            } else {
                throw new \UnexpectedValueException(
                    'A malformed KeywordTheme was encountered: ' . $keywordTheme->getKeywordTheme()
                );
            }
        }, $keywordThemes);

        // Optionally includes any free-form keywords in verbatim.
        if (!empty($freeFormKeywordText)) {
            $keywordThemeInfos[] =
                new KeywordThemeInfo(['free_form_keyword_theme' => $freeFormKeywordText]);
        }
        // Includes the keyword suggestions in the overall SuggestionInfo object.
        $suggestionInfo = $suggestionInfo->setKeywordThemes($keywordThemeInfos);

        $suggestedBudgetAmount = self::getBudgetSuggestion(
            $googleAdsClient,
            $customerId,
            $suggestionInfo
        );
        $adSuggestions = self::getAdSuggestions($googleAdsClient, $customerId, $suggestionInfo);

        // 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
        );
        $smartCampaignOperation = self::createSmartCampaignOperation($customerId);
        $smartCampaignSettingOperation = self::createSmartCampaignSettingOperation(
            $customerId,
            $businessProfileLocationResourceName,
            $businessName
        );
        $campaignCriterionOperations = self::createCampaignCriterionOperations(
            $customerId,
            $keywordThemeInfos,
            $suggestionInfo
        );
        $adGroupOperation = self::createAdGroupOperation($customerId);
        $adGroupAdOperation = self::createAdGroupAdOperation($customerId, $adSuggestions);

        // Issues a single mutate request to add the entities.
        $googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient();
        $response = $googleAdsServiceClient->mutate(MutateGoogleAdsRequest::build(
            $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,
                    $smartCampaignOperation,
                    $smartCampaignSettingOperation,
                ],
                $campaignCriterionOperations,
                [
                    $adGroupOperation,
                    $adGroupAdOperation
                ]
            )
        ));

        self::printResponseDetails($response);
    }

    /**
     * Retrieves KeywordThemes using the given suggestion info.
     *
     * Here we use the SuggestKeywordThemes method, which uses all of the business details included
     * in the given SmartCampaignSuggestionInfo instance to generate keyword theme suggestions. This
     * is the recommended way to generate keyword themes because it uses detailed information about
     * your business, its location, and website content to generate keyword themes.
     *
     * @param GoogleAdsClient $googleAdsClient the Google Ads API client
     * @param int $customerId the customer ID
     * @param SmartCampaignSuggestionInfo $suggestionInfo instance with details
     *     about the business being advertised
     * @return KeywordTheme[] a list of KeywordThemes
     */
    private static function getKeywordThemeSuggestions(
        GoogleAdsClient $googleAdsClient,
        int $customerId,
        SmartCampaignSuggestionInfo $suggestionInfo
    ): array {
        $smartCampaignSuggestServiceClient =
            $googleAdsClient->getSmartCampaignSuggestServiceClient();

        // Issues a request to retrieve the keyword themes.
        $response = $smartCampaignSuggestServiceClient->suggestKeywordThemes(
            (new SuggestKeywordThemesRequest())
                ->setCustomerId($customerId)
                ->setSuggestionInfo($suggestionInfo)
        );

        printf(
            "Retrieved %d keyword theme suggestions from the SuggestKeywordThemes "
            . "method.%s",
            $response->getKeywordThemes()->count(),
            PHP_EOL
        );
        return iterator_to_array($response->getKeywordThemes()->getIterator());
    }

    /**
     * Retrieves KeywordThemeConstants that are derived from autocomplete data for the given keyword
     * text.
     *
     * These KeywordThemeConstants are derived from autocomplete data for the
     * given keyword text. They are mapped to KeywordThemes before being returned.
     *
     * @param GoogleAdsClient $googleAdsClient the Google Ads API client
     * @param string $keywordText a keyword text used for generating keyword themes
     * @return KeywordTheme[] a list of KeywordThemes
     */
    private static function getKeywordTextAutoCompletions(
        GoogleAdsClient $googleAdsClient,
        string $keywordText
    ): array {
        $keywordThemeConstantService = $googleAdsClient->getKeywordThemeConstantServiceClient();

        // Issues a request to retrieve the keyword theme constants.
        $response = $keywordThemeConstantService->suggestKeywordThemeConstants(
            (new SuggestKeywordThemeConstantsRequest())
                ->setQueryText($keywordText)
                ->setCountryCode(self::COUNTRY_CODE)
                ->setLanguageCode(self::LANGUAGE_CODE)
        );

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

        // Maps the keyword theme constants to KeywordTheme instances for consistency with the
        // response from SmartCampaignSuggestService.SuggestKeywordThemes.
        return array_map(function (KeywordThemeConstant $keywordThemeConstant) {
            return new KeywordTheme([
                'keyword_theme_constant' => $keywordThemeConstant
            ]);
        }, iterator_to_array($response->getKeywordThemeConstants()->getIterator()));
    }

    /**
     * Builds a SmartCampaignSuggestionInfo object with business details.
     *
     * The details are used by the SmartCampaignSuggestService to suggest a budget amount as well
     * as creatives for the ad.
     *
     * Note that when retrieving ad creative suggestions it's required that the "final_url",
     * "language_code" and "keyword_themes" fields are set on the SmartCampaignSuggestionInfo
     * instance.
     *
     * @param string|null $businessProfileLocationResourceName the resource name of a Business
     *     Profile location
     * @param string|null $businessName the name of a Business Profile
     * @return SmartCampaignSuggestionInfo a SmartCampaignSuggestionInfo instance
     */
    private static function getSmartCampaignSuggestionInfo(
        ?string $businessProfileLocationResourceName,
        ?string $businessName
    ): SmartCampaignSuggestionInfo {
        $suggestionInfo = new SmartCampaignSuggestionInfo([
            // Adds the URL of the campaign's landing page.
            'final_url' => self::LANDING_PAGE_URL,

            // Adds the language code for the campaign.
            'language_code' => self::LANGUAGE_CODE,

            // 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 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
            ])]
        ]);

        // Sets either of the business_profile_location or business_name, depending on whichever is
        // provided.
        if ($businessProfileLocationResourceName) {
            $suggestionInfo->setBusinessProfileLocation($businessProfileLocationResourceName);
        } else {
            $suggestionInfo->setBusinessContext(new BusinessContext([
                'business_name' => $businessName
            ]));
        }
        return $suggestionInfo;
    }

    /**
     * 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 SmartCampaignSuggestionInfo $suggestionInfo a SmartCampaignSuggestionInfo instance
     *      with details about the business being advertised
     * @return int a daily budget amount in micros
     */
    private static function getBudgetSuggestion(
        GoogleAdsClient $googleAdsClient,
        int $customerId,
        SmartCampaignSuggestionInfo $suggestionInfo
    ): int {



        // Issues a request to retrieve a budget suggestion.
        $smartCampaignSuggestService = $googleAdsClient->getSmartCampaignSuggestServiceClient();
        $response = $smartCampaignSuggestService->suggestSmartCampaignBudgetOptions(
            (new SuggestSmartCampaignBudgetOptionsRequest())
                ->setCustomerId($customerId)
                // You can retrieve suggestions for an existing campaign by setting the "campaign"
                // field equal to the resource name of a campaign:
                // ->setCampaign('INSERT_CAMPAIGN_RESOURCE_NAME_HERE');
                // Since these suggestions are for a new campaign, we're going to use the
                // suggestion_info field instead.
                ->setSuggestionInfo($suggestionInfo)
        );

        // 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();
    }

    /**
     * Retrieves creative suggestions for a Smart campaign ad.
     *
     * Using the SmartCampaignSuggestService to suggest creatives 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 SmartCampaignSuggestionInfo $suggestionInfo a SmartCampaignSuggestionInfo instance
     *      with details about the business being advertised
     * @return SmartCampaignAdInfo|null a SmartCampaignAdInfo instance with suggested headlines and
     *      descriptions
     */
    private static function getAdSuggestions(
        GoogleAdsClient $googleAdsClient,
        int $customerId,
        SmartCampaignSuggestionInfo $suggestionInfo
    ) {
        // Unlike the SuggestSmartCampaignBudgetOptions method, it's only possible to use
        // suggestion_info to retrieve ad creative suggestions.

        // Issues a request to retrieve ad creative suggestions.
        $smartCampaignSuggestService = $googleAdsClient->getSmartCampaignSuggestServiceClient();
        $response = $smartCampaignSuggestService->suggestSmartCampaignAd(
            (new SuggestSmartCampaignAdRequest())
                ->setCustomerId($customerId)
                ->setSuggestionInfo($suggestionInfo)
        );

        // The SmartCampaignAdInfo object in the response contains a list of up to three headlines
        // and two descriptions. Note that some of the suggestions may have empty strings as text.
        // Before setting these on the ad you should review them and filter out any empty values.
        $adSuggestions = $response->getAdInfo();
        if (is_null($adSuggestions)) {
            return null;
        }
        print 'The following headlines were suggested:' . PHP_EOL;
        foreach ($adSuggestions->getHeadlines() as $headline) {
            print "\t" . ($headline->getText() ?: 'None') . PHP_EOL;
        }
        print 'And the following descriptions were suggested:' . PHP_EOL;
        foreach ($adSuggestions->getDescriptions() as $description) {
            print "\t" . ($description->getText() ?: 'None') . PHP_EOL;
        }
        return $adSuggestions;
    }

    /**
     * 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 $businessProfileLocationResourceName the resource name of a Business
     *     Profile location
     * @param string|null $businessName the name of a Business Profile
     * @return MutateOperation a MutateOperation that creates a SmartCampaignSetting
     */
    private static function createSmartCampaignSettingOperation(
        int $customerId,
        ?string $businessProfileLocationResourceName,
        ?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 profile location resource name or a business name is
        // added to the SmartCampaignSetting.
        if ($businessProfileLocationResourceName) {
            $smartCampaignSetting->setBusinessProfileLocation($businessProfileLocationResourceName);
        } 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
     * @param SmartCampaignSuggestionInfo $smartCampaignSuggestionInfo a SmartCampaignSuggestionInfo
     *     instance
     * @return MutateOperation[] a list of MutateOperations that create new campaign criteria
     */
    private static function createCampaignCriterionOperations(
        int $customerId,
        array $keywordThemeInfos,
        SmartCampaignSuggestionInfo $smartCampaignSuggestionInfo
    ): 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 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
                ])
            ]);
        }

        // Create a location criterion for each location in the suggestion info object to add
        // corresponding location targeting to the Smart campaign.
        foreach ($smartCampaignSuggestionInfo->getLocationList()->getLocations() as $location) {
            // 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),
                // Set the location to the given location.
                'location' => $location
            ]);

            // 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
     * @param SmartCampaignAdInfo|null $adSuggestions a SmartCampaignAdInfo object with ad creative
     *      suggestions
     * @return MutateOperation a MutateOperation that creates a new ad group ad
     */
    private static function createAdGroupAdOperation(
        int $customerId,
        ?SmartCampaignAdInfo $adSuggestions
    ): MutateOperation {
        if (is_null($adSuggestions)) {
            $smartCampaignAdInfo = new SmartCampaignAdInfo();
        } else {
            // The SmartCampaignAdInfo object includes headlines and descriptions retrieved
            // from the SmartCampaignSuggestService::SuggestSmartCampaignAd method. It's
            // recommended that users review and approve or update these creatives before
            // they're set on the ad. It's possible that some or all of these assets may
            // contain empty texts, which should not be set on the ad and instead should be
            // replaced with meaningful texts from the user. Below we just accept the creatives
            // that were suggested while filtering out empty assets, but individual workflows
            // will vary here.
            $smartCampaignAdInfo = new SmartCampaignAdInfo([
                'headlines' => array_filter(
                    iterator_to_array($adSuggestions->getHeadlines()->getIterator()),
                    function ($value) {
                        return $value->getText();
                    }
                ),
                'descriptions' => array_filter(
                    iterator_to_array($adSuggestions->getDescriptions()->getIterator()),
                    function ($value) {
                        return $value->getText();
                    }
                )
            ]);
        }
        // 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' => $smartCampaignAdInfo
            ])
        ]);

        // The SmartCampaignAdInfo object includes headlines and descriptions retrieved from the
        // SmartCampaignSuggestService.SuggestSmartCampaignAd method. It's recommended that users
        // review and approve or update these ads before they're set on the ad. It's possible that
        // some or all of these assets may contain empty texts, which should not be set on the ad
        // and instead should be replaced with meaningful texts from the user.
        // Below we just accept the ads that were suggested while filtering out empty assets.
        // If no headlines or descriptions were suggested, then we manually add some, otherwise
        // this operation will generate an INVALID_ARGUMENT error. Individual workflows will likely
        // vary here.
        $currentHeadlinesCount = $smartCampaignAdInfo->getHeadlines()->count();
        for ($i = 0; $i < self::NUM_REQUIRED_HEADLINES - $currentHeadlinesCount; $i++) {
            $smartCampaignAdInfo->setHeadlines(
                array_merge(
                    iterator_to_array($smartCampaignAdInfo->getHeadlines()),
                    [new AdTextAsset(['text' => 'Placeholder headline ' . $i])]
                )
            );
        }
        $currentDescriptionsCount = $smartCampaignAdInfo->getDescriptions()->count();
        for ($i = 0; $i < self::NUM_REQUIRED_DESCRIPTIONS - $currentDescriptionsCount; $i++) {
            $smartCampaignAdInfo->setDescriptions(
                array_merge(
                    iterator_to_array($smartCampaignAdInfo->getDescriptions()),
                    [new AdTextAsset(['text' => 'Placeholder description ' . $i])]
                )
            );
        }

        // 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();
            } elseif ($result->hasCampaignResult()) {
                $resourceType = "Campaign";
                $resourceName = $result->getCampaignResult()->getResourceName();
            } elseif ($result->hasSmartCampaignSettingResult()) {
                $resourceType = "SmartCampaignSetting";
                $resourceName = $result->getSmartCampaignSettingResult()->getResourceName();
            } elseif ($result->getAdGroupResult()) {
                $resourceType = "AdGroup";
                $resourceName = $result->getAdGroupResult()->getResourceName();
            } elseif ($result->getAdGroupAdResult()) {
                $resourceType = "AdGroupAd";
                $resourceName = $result->getAdGroupAdResult()->getResourceName();
            } elseif ($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

# 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 = "800-555-0100"
_BUDGET_TEMPORARY_ID = "-1"
_SMART_CAMPAIGN_TEMPORARY_ID = "-2"
_AD_GROUP_TEMPORARY_ID = "-3"
# These define the minimum number of headlines and descriptions that are
# required to create an AdGroupAd in a Smart campaign.
_REQUIRED_NUM_HEADLINES = 3
_REQUIRED_NUM_DESCRIPTIONS = 2


def main(
    client,
    customer_id,
    keyword_text,
    free_form_keyword_text,
    business_profile_location,
    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.
        free_form_keyword_text: a keyword used to create a free-form keyword
          theme.
        business_profile_location: the ID of a Business Profile location.
        business_name: the name of a Business Profile.
    """
    # The SmartCampaignSuggestionInfo object acts as the basis for many of the
    # entities necessary to create a Smart campaign. It will be reused a number
    # of times to retrieve suggestions for keyword themes, budget amount,
    # ad creatives, and campaign criteria.
    suggestion_info = get_smart_campaign_suggestion_info(
        client, business_profile_location, business_name
    )

    # After creating a SmartCampaignSuggestionInfo object we first use it to
    # generate a list of keyword themes using the SuggestKeywordThemes method
    # on the SmartCampaignSuggestService. It is strongly recommended that you
    # use this strategy for generating keyword themes.
    keyword_themes = get_keyword_theme_suggestions(
        client, customer_id, suggestion_info
    )

    # If a keyword text is given, retrieve keyword theme constant suggestions
    # from the KeywordThemeConstantService, map them to KeywordThemes, and
    # append them to the existing list. This logic should ideally only be used
    # if the suggestions from the get_keyword_theme_suggestions function are
    # insufficient.
    if keyword_text:
        keyword_themes.extend(
            get_keyword_text_auto_completions(client, keyword_text)
        )

    # Map the KeywordThemes retrieved by the previous two steps to
    # KeywordThemeInfo instances.
    keyword_theme_infos = map_keyword_themes_to_keyword_infos(
        client, keyword_themes
    )

    # If a free-form keyword text is given we create a KeywordThemeInfo instance
    # from it and add it to the existing list.
    if free_form_keyword_text:
        keyword_theme_infos.append(
            get_free_form_keyword_theme_info(client, free_form_keyword_text)
        )

    # Now add the generated keyword themes to the suggestion info instance.
    suggestion_info.keyword_themes = keyword_theme_infos

    # Retrieve a budget amount suggestion.
    suggested_budget_amount = get_budget_suggestion(
        client, customer_id, suggestion_info
    )

    # Retrieve Smart campaign ad creative suggestions.
    ad_suggestions = get_ad_suggestions(client, customer_id, suggestion_info)

    # 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_profile_location, business_name
    )
    campaign_criterion_operations = create_campaign_criterion_operations(
        client, customer_id, keyword_theme_infos, suggestion_info
    )
    ad_group_operation = create_ad_group_operation(client, customer_id)
    ad_group_ad_operation = create_ad_group_ad_operation(
        client, customer_id, ad_suggestions
    )

    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_suggestions(client, customer_id, suggestion_info):
    """Retrieves KeywordThemes using the given suggestion info.

    Here we use the SuggestKeywordThemes method, which uses all of the business
    details included in the given SmartCampaignSuggestionInfo instance to
    generate keyword theme suggestions. This is the recommended way to
    generate keyword themes because it uses detailed information about your
    business, its location, and website content to generate keyword themes.

    Args:
        client: an initialized GoogleAdsClient instance.
        customer_id: a client customer ID.
        suggestion_info: a SmartCampaignSuggestionInfo instance with details
          about the business being advertised.

    Returns:
        a list of KeywordThemes.
    """
    smart_campaign_suggest_service = client.get_service(
        "SmartCampaignSuggestService"
    )
    request = client.get_type("SuggestKeywordThemesRequest")
    request.customer_id = customer_id
    request.suggestion_info = suggestion_info

    response = smart_campaign_suggest_service.suggest_keyword_themes(
        request=request
    )

    print(
        f"Retrieved {len(response.keyword_themes)} keyword theme suggestions "
        "from the SuggestKeywordThemes method."
    )
    return response.keyword_themes


def get_keyword_text_auto_completions(client, keyword_text):
    """Retrieves KeywordThemeConstants for the given keyword text.

    These KeywordThemeConstants are derived from autocomplete data for the
    given keyword text. They are mapped to KeywordThemes before being returned.

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

    Returns:
        a list of KeywordThemes.
    """
    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}'"
    )

    # Map the keyword theme constants to KeywordTheme instances for consistency
    # with the response from SmartCampaignSuggestService.SuggestKeywordThemes.
    keyword_themes = []
    KeywordTheme = client.get_type("SuggestKeywordThemesResponse").KeywordTheme
    for keyword_theme_constant in response.keyword_theme_constants:
        keyword_theme = KeywordTheme()
        keyword_theme.keyword_theme_constant = keyword_theme_constant
        keyword_themes.append(keyword_theme)

    return keyword_themes


def get_free_form_keyword_theme_info(client, free_form_keyword_text):
    """Creates a KeywordThemeInfo using the given free-form keyword text.

    Args:
        client: an initialized GoogleAdsClient instance.
        free_form_keyword_text: a keyword used to create a free-form keyword
          theme.

    Returns:
        a KeywordThemeInfo instance.
    """
    info = client.get_type("KeywordThemeInfo")
    info.free_form_keyword_theme = free_form_keyword_text
    return info


def map_keyword_themes_to_keyword_infos(client, keyword_themes):
    """Maps a list of KeywordThemes to KeywordThemeInfos.

    Args:
        client: an initialized GoogleAdsClient instance.
        keyword_themes: a list of KeywordThemes.

    Returns:
        a list of KeywordThemeInfos.
    """
    infos = []
    for keyword_theme in keyword_themes:
        info = client.get_type("KeywordThemeInfo")
        # Check if the keyword_theme_constant field is set.
        if "keyword_theme_constant" in keyword_theme:
            info.keyword_theme_constant = (
                keyword_theme.keyword_theme_constant.resource_name
            )
            infos.append(info)
        # Check if the free_form_keyword_theme field is set.
        elif "free_form_keyword_theme" in keyword_theme:
            info.free_form_keyword_theme = keyword_theme.free_form_keyword_theme
            infos.append(info)
        else:
            raise ValueError(
                f"A malformed KeywordTheme was encountered: {keyword_theme}"
            )

    return infos


def get_smart_campaign_suggestion_info(
    client, business_profile_location, business_name
):
    """Builds a SmartCampaignSuggestionInfo object with business details.

    The details are used by the SmartCampaignSuggestService to suggest a
    budget amount as well as creatives for the ad.

    Note that when retrieving ad creative suggestions it's required that the
    "final_url", "language_code" and "keyword_themes" fields are set on the
    SmartCampaignSuggestionInfo instance.

    Args:
        client: an initialized GoogleAdsClient instance.
        business_profile_location: the resource name of a Business Profile
          location.
        business_name: the name of a Business Profile.

    Returns:
        A SmartCampaignSuggestionInfo instance.
    """
    suggestion_info = client.get_type("SmartCampaignSuggestionInfo")

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

    # Add the language code for the campaign.
    suggestion_info.language_code = _LANGUAGE_CODE

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

    # Set either of the business_profile_location or business_name, depending on
    # whichever is provided.
    if business_profile_location:
        suggestion_info.business_profile_location = business_profile_location
    else:
        suggestion_info.business_context.business_name = business_name

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

    return suggestion_info


def get_budget_suggestion(client, customer_id, suggestion_info):
    """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.
        suggestion_info: a SmartCampaignSuggestionInfo instance with details
          about the business being advertised.

    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.
    request.suggestion_info = suggestion_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 get_ad_suggestions(client, customer_id, suggestion_info):
    """Retrieves creative suggestions for a Smart campaign ad.

    Using the SmartCampaignSuggestService to suggest creatives 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.
        suggestion_info: a SmartCampaignSuggestionInfo instance with details
          about the business being advertised.

    Returns:
        a SmartCampaignAdInfo instance with suggested headlines and
            descriptions.
    """
    sc_suggest_service = client.get_service("SmartCampaignSuggestService")
    request = client.get_type("SuggestSmartCampaignAdRequest")
    request.customer_id = customer_id

    # Unlike the SuggestSmartCampaignBudgetOptions method, it's only possible
    # to use suggestion_info to retrieve ad creative suggestions.
    request.suggestion_info = suggestion_info

    # Issue a request to retrieve ad creative suggestions.
    response = sc_suggest_service.suggest_smart_campaign_ad(request=request)

    # The SmartCampaignAdInfo object in the response contains a list of up to
    # three headlines and two descriptions. Note that some of the suggestions
    # may have empty strings as text. Before setting these on the ad you should
    # review them and filter out any empty values.
    ad_suggestions = response.ad_info

    print("The following headlines were suggested:")
    for headline in ad_suggestions.headlines:
        print(f"\t{headline.text or '<None>'}")

    print("And the following descriptions were suggested:")
    for description in ad_suggestions.descriptions:
        print(f"\t{description.text or '<None>'}")

    return ad_suggestions


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_profile_location, 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_profile_location: the resource name of a Business Profile
          location.
        business_name: the name of a Business Profile.

    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

    # Set either of the business_profile_location or business_name, depending on
    # whichever is provided.
    if business_profile_location:
        smart_campaign_setting.business_profile_location = (
            business_profile_location
        )
    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, suggestion_info
):
    """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.
        suggestion_info: A SmartCampaignSuggestionInfo instance.

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

    # Create a location criterion for each location in the suggestion info
    # object to add corresponding location targeting to the Smart campaign
    for location_info in suggestion_info.location_list.locations:
        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 location to the given location.
        campaign_criterion.location = location_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, ad_suggestions):
    """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.
        ad_suggestions: a SmartCampaignAdInfo object with ad creative
          suggestions.

    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

    # The SmartCampaignAdInfo object includes headlines and descriptions
    # retrieved from the SmartCampaignSuggestService.SuggestSmartCampaignAd
    # method. It's recommended that users review and approve or update these
    # creatives before they're set on the ad. It's possible that some or all of
    # these assets may contain empty texts, which should not be set on the ad
    # and instead should be replaced with meaningful texts from the user. Below
    # we just accept the creatives that were suggested while filtering out empty
    # assets. If no headlines or descriptions were suggested, then we manually
    # add some, otherwise this operation will generate an INVALID_ARGUMENT
    # error. Individual workflows will likely vary here.
    ad.headlines = [asset for asset in ad_suggestions.headlines if asset.text]
    num_missing_headlines = _REQUIRED_NUM_HEADLINES - len(ad.headlines)

    # If there are fewer headlines than are required, we manually add additional
    # headlines to make up for the difference.
    for i in range(num_missing_headlines):
        headline = client.get_type("AdTextAsset")
        headline.text = f"placeholder headline {i}"
        ad.headlines.append(headline)

    ad.descriptions = [
        asset for asset in ad_suggestions.descriptions if asset.text
    ]
    num_missing_descriptions = _REQUIRED_NUM_DESCRIPTIONS - len(ad.descriptions)

    # If there are fewer descriptions than are required, we manually add
    # additional descriptions to make up for the difference.
    for i in range(num_missing_descriptions):
        description = client.get_type("AdTextAsset")
        description.text = f"placeholder description {i}"
        ad.descriptions.append(description)

    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__":
    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,
        help=(
            "A keyword text used to retrieve keyword theme constant "
            "suggestions from the KeywordThemeConstantService. These keyword "
            "theme suggestions are generated using auto-completion data for "
            "the given text and may help improve the performance of "
            "the Smart campaign."
        ),
    )
    parser.add_argument(
        "-f",
        "--free_form_keyword_text",
        type=str,
        required=False,
        help=(
            "A keyword text used to create a freeform keyword theme, which is "
            "entirely user-specified and not derived from any suggestion "
            "service. Using free-form keyword themes is typically not "
            "recommended because they are less effective than suggested "
            "keyword themes, however they are useful in situations where a "
            "very specific term needs to be targeted."
        ),
    )
    group = parser.add_mutually_exclusive_group(required=True)
    group.add_argument(
        "-b",
        "--business_profile_location",
        type=str,
        help=(
            "Optional: Specify the resource name of a Business Profile location. This"
            "is required if a business name is not provided. It can be retrieved"
            "using the Business Profile API, see:"
            "https://developers.google.com/my-business/reference/businessinformation/rest/v1/accounts.locations"
            "or from the Business Profile UI (https://support.google.com/business/answer/10737668)."
        ),
    )
    group.add_argument(
        "-n",
        "--business_name",
        type=str,
        help=(
            "The name of a Business Profile business. This is required "
            "if a business profile location is not provided."
        ),
    )

    args = parser.parse_args()

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

    try:
        main(
            googleads_client,
            args.customer_id,
            args.keyword_text,
            args.free_form_keyword_text,
            args.business_profile_location,
            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)

      

Rita

#!/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,
  free_form_keyword_text,
  business_profile_location,
  business_name)
  # GoogleAdsClient will read a config file from
  # ENV['HOME']/google_ads_config.rb when called without parameters
  client = Google::Ads::GoogleAds::GoogleAdsClient.new

  # The SmartCampaignSuggestionInfo object acts as the basis for many of the
  # entities necessary to create a Smart campaign. It will be reused a number
  # of times to retrieve suggestions for keyword themes, budget amount,
  # ad creatives, and campaign criteria.
  suggestion_info = get_smart_campaign_suggestion_info(
    client,
    business_profile_location,
    business_name,
  )

  # After creating a SmartCampaignSuggestionInfo object we first use it to
  # generate a list of keyword themes using the SuggestKeywordThemes method
  # on the SmartCampaignSuggestService. It is strongly recommended that you
  # use this strategy for generating keyword themes.
  keyword_themes = get_keyword_theme_suggestions(
    client,
    customer_id,
    suggestion_info,
  )

  # If a keyword text is given, retrieve keyword theme constant suggestions
  # from the KeywordThemeConstantService, map them to KeywordThemes, and append
  # them to the existing list. This logic should ideally only be used if the
  # suggestions from the get_keyword_theme_suggestions function are
  # insufficient.
  if keyword_text
    keyword_themes += get_keyword_text_auto_completions(
      client,
      keyword_text,
    )
  end

  # Map the KeywordThemeConstants retrieved by the previous two steps to
  # KeywordThemeInfo instances.
  keyword_theme_infos = map_keyword_themes_to_keyword_infos(
    client,
    keyword_themes,
  )

  # If a free-form keyword text is given we create a KeywordThemeInfo instance
  # from it and add it to the existing list.
  if free_form_keyword_text
    keyword_theme_infos << get_freeform_keyword_theme_info(
      client,
      free_form_keyword_text,
    )
  end

  # Now add the generated keyword themes to the suggestion info instance.
  suggestion_info.keyword_themes += keyword_theme_infos

  suggested_budget_amount = get_budget_suggestion(
    client,
    customer_id,
    suggestion_info,
  )
  ad_suggestions = get_ad_suggestions(
    client,
    customer_id,
    suggestion_info,
  )

  # 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_profile_location,
    business_name,
  )

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

  mutate_operations << create_ad_group_operation(client, customer_id)

  mutate_operations << create_ad_group_ad_operation(
    client,
    customer_id,
    ad_suggestions
  )

  # 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

def get_keyword_theme_suggestions(client, customer_id, suggestion_info)
  response = client.service.smart_campaign_suggest.suggest_keyword_themes(
    customer_id: customer_id,
    suggestion_info: suggestion_info,
  )

  puts "Retrieved #{response.keyword_themes.size} keyword theme" \
    " suggestions from SuggestKeywordThemes service."
  return response.keyword_themes
end

# Retrieves keyword_theme_constants for the given criteria.
# These KeywordThemeConstants are derived from autocomplete data for the given
# keyword text. They are mapped to KeywordThemes before being returned.
def get_keyword_text_auto_completions(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.map do |ktc|
    client.resource.keyword_theme do |kt|
      kt.keyword_theme_constant = ktc
    end
  end
end

def get_freeform_keyword_theme_info(client, free_form_keyword_text)
  client.resource.keyword_theme_info do |kti|
    kti.free_form_keyword_theme = free_form_keyword_text
  end
end

# Maps a list of keyword_theme_constants to keyword_theme_infos.
def map_keyword_themes_to_keyword_infos(client, keyword_themes)
  keyword_themes.map do |kt|
    client.resource.keyword_theme_info do |kti|
      if kt.keyword_theme_constant
        kti.keyword_theme_constant = kt.keyword_theme_constant.resource_name
      elsif kt.free_form_keyword_theme
        kti.free_form_keyword_theme = kt.free_form_keyword_theme
      else
        raise "Malformed keyword_theme #{kt}"
      end
    end
  end
end

# Builds a SmartCampaignSuggestionInfo object with business details.
#
# The details are used by the SmartCampaignSuggestService to suggest a
# budget amount as well as creatives for the ad.
#
# Note that when retrieving ad creative suggestions it's required that the
# "final_url", "language_code" and "keyword_themes" fields are set on the
# SmartCampaignSuggestionInfo instance.
def get_smart_campaign_suggestion_info(
  client,
  business_profile_location,
  business_name)

  # 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
    # Add the language code for the campaign.
    si.language_code = LANGUAGE_CODE
    # 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
    # Set either of the business_profile_location or business_name, depending on
    # whichever is provided.
    if business_profile_location
      si.business_profile_location = business_profile_location
    else
      si.business_context = client.resource.business_context do |bc|
        bc.business_name = business_name
      end
    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

  suggestion_info
end

# 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.
def get_budget_suggestion(client, customer_id, suggestion_info)
  # Issues a request to retrieve a budget suggestion.
  response = client.service.smart_campaign_suggest.suggest_smart_campaign_budget_options(
    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:
    # 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: 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

# Retrieves creative suggestions for a Smart campaign ad.
#
# Using the SmartCampaignSuggestService to suggest creatives for new and
# existing Smart campaigns is highly recommended because it helps the
# campaigns achieve optimal performance.
def get_ad_suggestions(client, customer_id, suggestion_info)
  # Issue a request to retrieve ad creative suggestions.
  response = client.service.smart_campaign_suggest.suggest_smart_campaign_ad(
    customer_id: customer_id,
    # Unlike the SuggestSmartCampaignBudgetOptions method, it's only possible
    # to use suggestion_info to retrieve ad creative suggestions.
    suggestion_info: suggestion_info,
  )

  # The SmartCampaignAdInfo object in the response contains a list of up to
  # three headlines and two descriptions. Note that some of the suggestions
  # may have empty strings as text. Before setting these on the ad you should
  # review them and filter out any empty values.
  ad_suggestions = response.ad_info

  # If there are no suggestions, the response will be blank.
  return nil if ad_suggestions.nil?

  puts 'The following headlines were suggested:'
  ad_suggestions.headlines.each do  |headline|
    puts "\t#{headline.text || '<None>'}"
  end

  puts 'And the following descriptions were suggested:'
  ad_suggestions.descriptions.each do |description|
    puts "\t#{description.text || '<None>'}"
  end

  ad_suggestions
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_profile_location,
  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_profile_location
        scs.business_profile_location = business_profile_location
      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,
  suggestion_info)
  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 keyword theme to the given keyword_theme_info.
        cc.keyword_theme = info
      end
    end
  end

  # Create a location criterion for each location in the suggestion info object
  # to add corresponding location targeting to the Smart campaign
  suggestion_info.location_list.locations.each do |location|
    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 location to the given location.
        cc.location = location
      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, ad_suggestions)
  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|
          # The SmartCampaignAdInfo object includes headlines and descriptions
          # retrieved from the SmartCampaignSuggestService.SuggestSmartCampaignAd
          # method. It's recommended that users review and approve or update these
          # creatives before they're set on the ad. It's possible that some or all of
          # these assets may contain empty texts, which should not be set on the ad
          # and instead should be replaced with meaningful texts from the user. Below
          # we just accept the creatives that were suggested while filtering out empty
          # assets. If no headlines or descriptions were suggested, then we manually
          # add some, otherwise this operation will generate an INVALID_ARGUMENT
          # error. Individual workflows will likely vary here.
          sca.headlines += ad_suggestions.headlines.filter(&:text) if ad_suggestions
          if sca.headlines.size < REQUIRED_NUM_HEADLINES
            (REQUIRED_NUM_HEADLINES - sca.headlines.size).times do |i|
              sca.headlines << client.resource.ad_text_asset do |asset|
                asset.text = "placeholder headline #{i}"
              end
            end
          end

          sca.descriptions += ad_suggestions.descriptions.filter(&:text) if ad_suggestions
          if sca.descriptions.size < REQUIRED_NUM_DESCRIPTIONS
            (REQUIRED_NUM_DESCRIPTIONS - sca.descriptions.size).times do |i|
              sca.descriptions << client.resource.ad_text_asset do |asset|
                asset.text = "placeholder description #{i}"
              end
            end
          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
  # 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 = "800-555-0100"
  BUDGET_TEMPORARY_ID = "-1"
  SMART_CAMPAIGN_TEMPORARY_ID = "-2"
  AD_GROUP_TEMPORARY_ID = "-3"
  # These define the minimum number of headlines and descriptions that are
  # required to create an AdGroupAd in a Smart campaign.
  REQUIRED_NUM_HEADLINES = 3
  REQUIRED_NUM_DESCRIPTIONS = 2

  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'

  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 retrieve keyword theme constant ' \
      'suggestions from the KeywordThemeConstantService. These keyword ' \
      'theme suggestions are generated using auto-completion data for ' \
      'the given text and may help improve the performance of ' \
      'the Smart campaign.') do |v|
      options[:keyword_text] = v
    end

    opts.on('-f', '--freeform-keyword-text FREEFORM-KEYWORD-TEXT', String,
      'A keyword text used to create a freeform keyword theme, which is ' \
      'entirely user-specified and not derived from any suggestion ' \
      'service. Using free-form keyword themes is typically not ' \
      'recommended because they are less effective than suggested ' \
      'keyword themes, however they are useful in situations where a ' \
      'very specific term needs to be targeted.') do |v|
      options[:free_form_keyword_text] = v
    end

    opts.on('-b', '--business-profile-location BUSINESS-PROFILE-LOCATION', String,
      'The resource name of a Business Profile 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/businessinformation/rest/v1/accounts.locations' \
      ' or from the Business Profile UI (https://support.google.com/business/answer/10737668).') do |v|
      options[:business_profile_location] = v
    end

    opts.on('-n', '--business-name BUSINESS-NAME', String,
      'The name of a Business Profile 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[:keyword_text],
      options[:free_form_keyword_text],
      options[:business_profile_location],
      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::GoogleAdsHelper;
use Google::Ads::GoogleAds::Utils::FieldMasks;
use Google::Ads::GoogleAds::V17::Resources::Ad;
use Google::Ads::GoogleAds::V17::Resources::AdGroup;
use Google::Ads::GoogleAds::V17::Resources::AdGroupAd;
use Google::Ads::GoogleAds::V17::Resources::CampaignBudget;
use Google::Ads::GoogleAds::V17::Resources::Campaign;
use Google::Ads::GoogleAds::V17::Resources::CampaignCriterion;
use Google::Ads::GoogleAds::V17::Resources::PhoneNumber;
use Google::Ads::GoogleAds::V17::Resources::SmartCampaignSetting;
use Google::Ads::GoogleAds::V17::Common::AdScheduleInfo;
use Google::Ads::GoogleAds::V17::Common::KeywordThemeInfo;
use Google::Ads::GoogleAds::V17::Common::LocationInfo;
use Google::Ads::GoogleAds::V17::Common::SmartCampaignAdInfo;
use Google::Ads::GoogleAds::V17::Common::AdTextAsset;
use Google::Ads::GoogleAds::V17::Enums::AdGroupTypeEnum qw(SMART_CAMPAIGN_ADS);
use Google::Ads::GoogleAds::V17::Enums::AdTypeEnum      qw(SMART_CAMPAIGN_AD);
use Google::Ads::GoogleAds::V17::Enums::AdvertisingChannelTypeEnum qw(SMART);
use Google::Ads::GoogleAds::V17::Enums::AdvertisingChannelSubTypeEnum;
use Google::Ads::GoogleAds::V17::Enums::BudgetTypeEnum;
use Google::Ads::GoogleAds::V17::Enums::CampaignStatusEnum       qw(PAUSED);
use Google::Ads::GoogleAds::V17::Enums::DayOfWeekEnum            qw(MONDAY);
use Google::Ads::GoogleAds::V17::Enums::MinuteOfHourEnum         qw(ZERO);
use Google::Ads::GoogleAds::V17::Enums::ProximityRadiusUnitsEnum qw(MILES);
use Google::Ads::GoogleAds::V17::Services::AdGroupService::AdGroupOperation;
use Google::Ads::GoogleAds::V17::Services::AdGroupAdService::AdGroupAdOperation;
use
  Google::Ads::GoogleAds::V17::Services::CampaignBudgetService::CampaignBudgetOperation;
use Google::Ads::GoogleAds::V17::Services::CampaignService::CampaignOperation;
use
  Google::Ads::GoogleAds::V17::Services::CampaignCriterionService::CampaignCriterionOperation;
use Google::Ads::GoogleAds::V17::Services::GoogleAdsService::MutateOperation;
use
  Google::Ads::GoogleAds::V17::Services::KeywordThemeConstantService::SuggestKeywordThemeConstantsRequest;
use
  Google::Ads::GoogleAds::V17::Services::SmartCampaignSettingService::SmartCampaignSettingOperation;
use
  Google::Ads::GoogleAds::V17::Services::SmartCampaignSuggestService::BusinessContext;
use
  Google::Ads::GoogleAds::V17::Services::SmartCampaignSuggestService::KeywordTheme;
use
  Google::Ads::GoogleAds::V17::Services::SmartCampaignSuggestService::LocationList;
use
  Google::Ads::GoogleAds::V17::Services::SmartCampaignSuggestService::SmartCampaignSuggestionInfo;
use
  Google::Ads::GoogleAds::V17::Services::SmartCampaignSuggestService::SuggestKeywordThemesRequest;
use
  Google::Ads::GoogleAds::V17::Services::SmartCampaignSuggestService::SuggestSmartCampaignAdRequest;
use
  Google::Ads::GoogleAds::V17::Services::SmartCampaignSuggestService::SuggestSmartCampaignBudgetOptionsRequest;
use Google::Ads::GoogleAds::V17::Utils::ResourceNames;

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

# Geo target constant for New York City.
use constant 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
use constant COUNTRY_CODE => "US";
# For a list of all language codes, see:
# https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-7
use constant LANGUAGE_CODE               => "en";
use constant LANDING_PAGE_URL            => "http://www.example.com";
use constant PHONE_NUMBER                => "800-555-0100";
use constant BUDGET_TEMPORARY_ID         => -1;
use constant SMART_CAMPAIGN_TEMPORARY_ID => -2;
use constant AD_GROUP_TEMPORARY_ID       => -3;
# These define the minimum number of headlines and descriptions that are
# required to create an AdGroupAd in a Smart campaign.
use constant REQUIRED_NUM_HEADLINES    => 3;
use constant REQUIRED_NUM_DESCRIPTIONS => 2;

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

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

  # The SmartCampaignSuggestionInfo object acts as the basis for many of the
  # entities necessary to create a Smart campaign. It will be reused a number
  # of times to retrieve suggestions for keyword themes, budget amount,
  # ad creatives, and campaign criteria.
  my $suggestion_info =
    _get_smart_campaign_suggestion_info($business_profile_location,
    $business_name);

  # After creating a SmartCampaignSuggestionInfo object we first use it to
  # generate a list of keyword themes using the SuggestKeywordThemes method
  # on the SmartCampaignSuggestService. It is strongly recommended that you
  # use this strategy for generating keyword themes.
  my $keyword_themes =
    _get_keyword_theme_suggestions($api_client, $customer_id, $suggestion_info);

  # If a keyword text is given, retrieve keyword theme constant suggestions
  # from the KeywordThemeConstantService, map them to KeywordThemes, and
  # append them to the existing list. This logic should ideally only be used
  # if the suggestions from the get_keyword_theme_suggestions funtion are
  # insufficient.
  if (defined $keyword_text) {
    push @$keyword_themes,
      @{_get_keyword_text_auto_completions($api_client, $keyword_text)};
  }

  # Map the KeywordThemeConstants retrieved by the previous two steps to
  # KeywordThemeInfo instances.
  my $keyword_theme_infos = _map_keyword_themes_to_infos($keyword_themes);

  # If a free-form keyword text is given we create a KeywordThemeInfo instance
  # from it and add it to the existing list.
  if (defined $free_form_keyword_text) {
    push @$keyword_theme_infos,
      _get_free_form_keyword_theme_info($free_form_keyword_text);
  }

  # Now add the generated keyword themes to the suggestion info instance.
  $suggestion_info->{keywordThemes} = $keyword_theme_infos;

  # Retrieve a budget amount suggestion.
  my $suggested_budget_amount =
    _get_budget_suggestion($api_client, $customer_id, $suggestion_info);

  # Retrieve Smart campaign ad creative suggestions.
  my $ad_suggestions =
    _get_ad_suggestions($api_client, $customer_id, $suggestion_info);

  # 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_profile_location, $business_name);
  my $campaign_criterion_operations =
    _create_campaign_criterion_operations($customer_id, $keyword_theme_infos,
    $suggestion_info);
  my $ad_group_operation = _create_ad_group_operation($customer_id);
  my $ad_group_ad_operation =
    _create_ad_group_ad_operation($customer_id, $ad_suggestions);

  # 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,
    # Expand the list of campaign criterion operations into the list of
    # other mutate operations.
    @$campaign_criterion_operations,
    $ad_group_operation, $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 KeywordThemes using the given suggestion info.
# Here we use the SuggestKeywordThemes method, which uses all of the business
# details included in the given SmartCampaignSuggestionInfo instance to generate
# keyword theme suggestions. This is the recommended way to generate keyword themes
# because it uses detailed information about your business, its location, and
# website content to generate keyword themes.
sub _get_keyword_theme_suggestions {
  my ($api_client, $customer_id, $suggestion_info) = @_;

  my $response =
    $api_client->SmartCampaignSuggestService()->suggest_keyword_themes(
    Google::Ads::GoogleAds::V17::Services::SmartCampaignSuggestService::SuggestKeywordThemesRequest
      ->new({
        customerId     => $customer_id,
        suggestionInfo => $suggestion_info
      }));

  printf "Retrieved %d keyword theme suggestions from the SuggestKeywordThemes"
    . "method.\n",
    scalar @{$response->{keywordThemes}};

  return $response->{keywordThemes};
}

# Retrieves KeywordThemeConstants for the given keyword text.
# These KeywordThemeConstants are derived from autocomplete data for the given
# keyword text. They are mapped to KeywordThemes before being returned.
sub _get_keyword_text_auto_completions {
  my ($api_client, $keyword_text) = @_;

  my $response = $api_client->KeywordThemeConstantService()->suggest(
    Google::Ads::GoogleAds::V17::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;

  # Map the keyword theme constants to KeywordTheme instances for consistency
  # with the response from SmartCampaignSuggestService.SuggestKeywordThemes.
  my $keyword_themes = [];
  foreach my $keyword_theme_constant (@{$response->{keywordThemeConstants}}) {
    push @$keyword_themes,
      Google::Ads::GoogleAds::V17::Services::SmartCampaignSuggestService::KeywordTheme
      ->new({
        keywordThemeConstant => $keyword_theme_constant
      });
  }

  return $keyword_themes;
}

# Creates a KeywordInfo instance using the given free-form keyword text.
sub _get_free_form_keyword_theme_info {
  my ($free_form_keyword_text) = @_;

  return Google::Ads::GoogleAds::V17::Common::KeywordThemeInfo->new({
    freeFormKeywordTheme => $free_form_keyword_text
  });
}

# Maps a list of KeywordThemes to KeywordThemeInfos.
sub _map_keyword_themes_to_keyword_infos {
  my ($keyword_themes) = @_;

  my $keyword_theme_infos = [];
  foreach my $keyword_theme (@$keyword_themes) {
    if (defined $keyword_theme->{keywordThemeConstant}) {
      push @$keyword_theme_infos,
        Google::Ads::GoogleAds::V17::Common::KeywordThemeInfo->new({
          keywordThemeConstant =>
            $keyword_theme->{keywordThemeConstant}{resourceName}});
    } elsif (defined $keyword_theme->{freeFormKeywordTheme}) {
      push @$keyword_theme_infos,
        Google::Ads::GoogleAds::V17::Common::KeywordThemeInfo->new({
          freeFormKeywordTheme => $keyword_theme->{freeFormKeywordTheme}});
    } else {
      die "A malformed KeywordTheme was encountered: $keyword_theme";
    }
  }

  return $keyword_theme_infos;
}

# Builds a SmartCampaignSuggestionInfo object with business details.
# The details are used by the SmartCampaignSuggestService to suggest a budget
# amount as well as creatives for the ad.
# Note that when retrieving ad creative suggestions you must set the
# "final_url", "language_code" and "keyword_themes" fields on the
# SmartCampaignSuggestionInfo instance.
sub _get_smart_campaign_suggestion_info {
  my ($business_profile_location, $business_name) = @_;

  my $suggestion_info =
    Google::Ads::GoogleAds::V17::Services::SmartCampaignSuggestService::SmartCampaignSuggestionInfo
    ->new({
      # Add the URL of the campaign's landing page.
      finalUrl => LANDING_PAGE_URL,
      # Add the language code for the campaign.
      languageCode => LANGUAGE_CODE,
      # 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::V17::Common::ProximityInfo->new({
      #     address => Google::Ads::GoogleAds::V17::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::V17::Services::SmartCampaignSuggestService::LocationList
        ->new(
        )});

  # 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 @{$suggestion_info->{locationList}{locations}},
    Google::Ads::GoogleAds::V17::Common::LocationInfo->new({
      # Set the location to the resource name of the given geo target constant.
      geoTargetConstant =>
        Google::Ads::GoogleAds::V17::Utils::ResourceNames::geo_target_constant(
        GEO_TARGET_CONSTANT)});

  # Set one of the business_profile_location or business_name, whichever is provided.
  if (defined $business_profile_location) {
    $suggestion_info->{businessProfileLocation} =
      _convert_business_profile_location($business_profile_location);
  } else {
    $suggestion_info->{businessContext} =
      Google::Ads::GoogleAds::V17::Services::SmartCampaignSuggestService::BusinessContext
      ->new({
        businessName => $business_name
      });
  }

  # 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 @{$suggestion_info->{adSchedules}},
    Google::Ads::GoogleAds::V17::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
    });

  return $suggestion_info;
}

# 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.
sub _get_budget_suggestion {
  my ($api_client, $customer_id, $suggestion_info) = @_;

  my $request =
    Google::Ads::GoogleAds::V17::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",
      #
      # Since these suggestions are for a new campaign, we're going to use the
      # "suggestion_info" field instead.
      suggestionInfo => $suggestion_info
    });

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

# Retrieves creative suggestions for a Smart campaign ad.
# Using the SmartCampaignSuggestService to suggest creatives for new and
# existing Smart campaigns is highly recommended because it helps the campaigns
# achieve optimal performance.
sub _get_ad_suggestions {
  my ($api_client, $customer_id, $suggestion_info) = @_;

  # Issue a request to retrieve ad creative suggestions.
  my $response =
    $api_client->SmartCampaignSuggestService()->suggest_smart_campaign_ad(
    Google::Ads::GoogleAds::V17::Services::SmartCampaignSuggestService::SuggestSmartCampaignAdRequest
      ->new({
        customerId => $customer_id,
        # Unlike the SuggestSmartCampaignBudgetOptions method, it's only
        # possible to use suggestion_info to retrieve ad creative suggestions.
        suggestionInfo => $suggestion_info
      }));

  # The SmartCampaignAdInfo object in the response contains a list of up to
  # three headlines and two descriptions. Note that some of the suggestions
  # may have empty strings as text. Before setting these on the ad you should
  # review them and filter out any empty values.
  my $ad_suggestions = $response->{adInfo};
  printf "The following headlines were suggested:\n";
  foreach my $headline (@{$ad_suggestions->{headlines}}) {
    printf "\t%s\n",