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 // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package com.google.ads.googleads.examples.advancedoperations; import static com.google.ads.googleads.examples.utils.CodeSampleHelper.getPrintableDateTime; import com.beust.jcommander.Parameter; import com.google.ads.googleads.examples.utils.ArgumentNames; import com.google.ads.googleads.examples.utils.CodeSampleParams; import com.google.ads.googleads.lib.GoogleAdsClient; import com.google.ads.googleads.v18.common.AudienceInfo; import com.google.ads.googleads.v18.common.ImageAsset; import com.google.ads.googleads.v18.common.LanguageInfo; import com.google.ads.googleads.v18.common.LocationInfo; import com.google.ads.googleads.v18.common.MaximizeConversionValue; import com.google.ads.googleads.v18.common.SearchThemeInfo; import com.google.ads.googleads.v18.common.TextAsset; import com.google.ads.googleads.v18.enums.AdvertisingChannelTypeEnum.AdvertisingChannelType; import com.google.ads.googleads.v18.enums.AssetFieldTypeEnum.AssetFieldType; import com.google.ads.googleads.v18.enums.AssetGroupStatusEnum.AssetGroupStatus; import com.google.ads.googleads.v18.enums.BudgetDeliveryMethodEnum.BudgetDeliveryMethod; import com.google.ads.googleads.v18.enums.CampaignStatusEnum.CampaignStatus; import com.google.ads.googleads.v18.errors.GoogleAdsError; import com.google.ads.googleads.v18.errors.GoogleAdsException; import com.google.ads.googleads.v18.resources.Asset; import com.google.ads.googleads.v18.resources.AssetGroup; import com.google.ads.googleads.v18.resources.AssetGroupAsset; import com.google.ads.googleads.v18.resources.AssetGroupSignal; import com.google.ads.googleads.v18.resources.Campaign; import com.google.ads.googleads.v18.resources.CampaignBudget; import com.google.ads.googleads.v18.resources.CampaignCriterion; import com.google.ads.googleads.v18.services.AssetGroupAssetOperation; import com.google.ads.googleads.v18.services.AssetGroupOperation; import com.google.ads.googleads.v18.services.AssetGroupSignalOperation; import com.google.ads.googleads.v18.services.AssetOperation; import com.google.ads.googleads.v18.services.CampaignBudgetOperation; import com.google.ads.googleads.v18.services.CampaignCriterionOperation; import com.google.ads.googleads.v18.services.CampaignOperation; import com.google.ads.googleads.v18.services.GoogleAdsServiceClient; import com.google.ads.googleads.v18.services.MutateGoogleAdsResponse; import com.google.ads.googleads.v18.services.MutateOperation; import com.google.ads.googleads.v18.services.MutateOperationResponse; import com.google.ads.googleads.v18.utils.ResourceNames; import com.google.common.collect.ImmutableList; import com.google.common.io.ByteStreams; import com.google.protobuf.ByteString; import com.google.protobuf.Descriptors.FieldDescriptor; import java.io.FileNotFoundException; import java.io.IOException; import java.net.URL; import java.util.ArrayList; import java.util.List; import java.util.Map.Entry; import java.util.stream.Collectors; import org.joda.time.DateTime; /** * This example shows how to create a Performance Max campaign. * * <p>For more information about Performance Max campaigns, see * https://developers.google.com/google-ads/api/docs/performance-max/overview * * <p>Prerequisites: - You must have at least one conversion action in the account. For more about * conversion actions, see * https://developers.google.com/google-ads/api/docs/conversions/overview#conversion_actions * * <p>This example uses the default customer conversion goals. For an example of setting * campaign-specific conversion goals, see {@link * com.google.ads.googleads.examples.shoppingads.AddPerformanceMaxRetailCampaign}. */ public class AddPerformanceMaxCampaign { // We specify temporary IDs that are specific to a single mutate request. Temporary IDs are always // negative and unique within one mutate request. // // <p>See https://developers.google.com/google-ads/api/docs/mutating/best-practices for further // details. // // <p>These temporary IDs are fixed because they are used in multiple places. private static final int BUDGET_TEMPORARY_ID = -1; private static final int PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID = -2; private static final int ASSET_GROUP_TEMPORARY_ID = -3; // There are also entities that will be created in the same request but do not // need to be fixed temporary IDs because they are referenced only once. private static long temporaryId = ASSET_GROUP_TEMPORARY_ID - 1; private static class AddPerformanceMaxCampaignParams extends CodeSampleParams { @Parameter(names = ArgumentNames.CUSTOMER_ID, required = true) private Long customerId; @Parameter( names = ArgumentNames.AUDIENCE_ID, description = "An audience ID to use to improve the targeting of the Performance Max campaign") private Long audienceId; } public static void main(String[] args) throws IOException { AddPerformanceMaxCampaignParams params = new AddPerformanceMaxCampaignParams(); 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"); // Optional: Specify an audience ID. // params.audienceId = Long.parseLong("INSERT_AUDIENCE_ID_HERE"); } 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 AddPerformanceMaxCampaign() .runExample(googleAdsClient, params.customerId, params.audienceId); } 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); } } /** * Runs the example. * * @param googleAdsClient the Google Ads API client. * @param customerId the client customer ID. * @param audienceId the optional audience ID. */ private void runExample(GoogleAdsClient googleAdsClient, long customerId, Long audienceId) throws IOException { // Performance Max campaigns require that repeated assets such as headlines // and descriptions be created before the campaign. // For the list of required assets for a Performance Max campaign, see // https://developers.google.com/google-ads/api/docs/performance-max/assets // // Creates the headlines. List<String> headlines = ImmutableList.of("Travel", "Travel Reviews", "Book travel"); List<String> headlineAssetResourceNames = createMultipleTextAssets(googleAdsClient, customerId, headlines); // Creates the descriptions. List<String> descriptions = ImmutableList.of("Take to the air!", "Fly to the sky!"); List<String> descriptionAssetResourceNames = createMultipleTextAssets(googleAdsClient, customerId, descriptions); // 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 Performance Max // 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 List<MutateOperation> mutateOperations = new ArrayList<>(); mutateOperations.add(createCampaignBudgetOperation(customerId)); mutateOperations.add(createPerformanceMaxCampaignOperation(customerId)); mutateOperations.addAll(createCampaignCriterionOperations(customerId)); String assetGroupResourceName = ResourceNames.assetGroup(customerId, ASSET_GROUP_TEMPORARY_ID); mutateOperations.addAll( createAssetGroupOperations( customerId, assetGroupResourceName, headlineAssetResourceNames, descriptionAssetResourceNames)); mutateOperations.addAll( createAssetGroupSignalOperations(customerId, assetGroupResourceName, audienceId)); try (GoogleAdsServiceClient googleAdsServiceClient = googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) { MutateGoogleAdsResponse response = googleAdsServiceClient.mutate(Long.toString(customerId), mutateOperations); printResponseDetails(response); } } /** Creates a MutateOperation that creates a new CampaignBudget. */ private MutateOperation createCampaignBudgetOperation(long customerId) { CampaignBudget campaignBudget = CampaignBudget.newBuilder() .setName("Performance Max campaign budget #" + getPrintableDateTime()) // The budget period already defaults to DAILY. .setAmountMicros(50_000_000) .setDeliveryMethod(BudgetDeliveryMethod.STANDARD) // A Performance Max campaign cannot use a shared campaign budget. .setExplicitlyShared(false) // Set 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)) .build(); return MutateOperation.newBuilder() .setCampaignBudgetOperation( CampaignBudgetOperation.newBuilder().setCreate(campaignBudget).build()) .build(); } /** Creates a MutateOperation that creates a new Performance Max campaign. */ private MutateOperation createPerformanceMaxCampaignOperation(long customerId) { Campaign performanceMaxCampaign = Campaign.newBuilder() .setName("Performance Max campaign #" + getPrintableDateTime()) // Sets the campaign status as PAUSED. The campaign is the only entity in // the mutate request that should have its status set. .setStatus(CampaignStatus.PAUSED) // All Performance Max campaigns have an advertising_channel_type of // PERFORMANCE_MAX. The advertising_channel_sub_type should not be set. .setAdvertisingChannelType(AdvertisingChannelType.PERFORMANCE_MAX) // Bidding strategy must be set directly on the campaign. // Setting a portfolio bidding strategy by resource name is not supported. // Max Conversion and Maximize Conversion Value are the only strategies // supported for Performance Max campaigns. // An optional ROAS (Return on Advertising Spend) can be set for // maximize_conversion_value. The ROAS value must be specified as a ratio in // the API. It is calculated by dividing "total value" by "total spend". // For more information on Maximize Conversion Value, see the support // article: http://support.google.com/google-ads/answer/7684216. // A targetRoas of 3.5 corresponds to a 350% return on ad spend. .setMaximizeConversionValue( MaximizeConversionValue.newBuilder().setTargetRoas(3.5).build()) // Sets the Final URL expansion opt out. This flag is specific to // Performance Max campaigns. If opted out (True), only the final URLs in // the asset group or URLs specified in the advertiser's Google Merchant // Center or business data feeds are targeted. // If opted in (False), the entire domain will be targeted. For best // results, set this value to false to opt in and allow URL expansions. You // can optionally add exclusions to limit traffic to parts of your website. .setUrlExpansionOptOut(false) // Assigns the resource name with a temporary ID. .setResourceName( ResourceNames.campaign(customerId, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID)) // Sets the budget using the given budget resource name. .setCampaignBudget(ResourceNames.campaignBudget(customerId, BUDGET_TEMPORARY_ID)) // Optional fields. .setStartDate(new DateTime().plusDays(1).toString("yyyyMMdd")) .setEndDate(new DateTime().plusDays(365).toString("yyyyMMdd")) .build(); return MutateOperation.newBuilder() .setCampaignOperation( CampaignOperation.newBuilder().setCreate(performanceMaxCampaign).build()) .build(); } /** Creates a list of MutateOperations that create new campaign criteria. */ private List<MutateOperation> createCampaignCriterionOperations(long customerId) { String campaignResourceName = ResourceNames.campaign(customerId, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID); List<CampaignCriterion> campaignCriteria = new ArrayList<>(); // Sets the LOCATION campaign criteria. // Targets all of New York City except Brooklyn. // Location IDs are listed here: // https://developers.google.com/google-ads/api/reference/data/geotargets // and they can also be retrieved using the GeoTargetConstantService as shown // here: https://developers.google.com/google-ads/api/docs/targeting/location-targeting // // We will add one positive location target for New York City (ID=1023191) // and one negative location target for Brooklyn (ID=1022762). // First, adds the positive (negative = False) for New York City. campaignCriteria.add( CampaignCriterion.newBuilder() .setCampaign(campaignResourceName) .setLocation( LocationInfo.newBuilder() .setGeoTargetConstant(ResourceNames.geoTargetConstant(1023191)) .build()) .setNegative(false) .build()); // Next adds the negative target for Brooklyn. campaignCriteria.add( CampaignCriterion.newBuilder() .setCampaign(campaignResourceName) .setLocation( LocationInfo.newBuilder() .setGeoTargetConstant(ResourceNames.geoTargetConstant(1022762)) .build()) .setNegative(true) .build()); // Sets the LANGUAGE campaign criterion. campaignCriteria.add( CampaignCriterion.newBuilder() .setCampaign(campaignResourceName) // Sets the language. // For a list of all language codes, see: // https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-7 .setLanguage( LanguageInfo.newBuilder() .setLanguageConstant(ResourceNames.languageConstant(1000)) // English .build()) .build()); // Returns a list of mutate operations with one operation per criterion. return campaignCriteria.stream() .map( criterion -> MutateOperation.newBuilder() .setCampaignCriterionOperation( CampaignCriterionOperation.newBuilder().setCreate(criterion).build()) .build()) .collect(Collectors.toList()); } /** Creates multiple text assets and returns the list of resource names. */ private List<String> createMultipleTextAssets( GoogleAdsClient googleAdsClient, long customerId, List<String> texts) { List<MutateOperation> mutateOperations = new ArrayList<>(); for (String text : texts) { Asset asset = Asset.newBuilder().setTextAsset(TextAsset.newBuilder().setText(text)).build(); AssetOperation assetOperation = AssetOperation.newBuilder().setCreate(asset).build(); mutateOperations.add(MutateOperation.newBuilder().setAssetOperation(assetOperation).build()); } List<String> assetResourceNames = new ArrayList<>(); // Creates the service client. try (GoogleAdsServiceClient googleAdsServiceClient = googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) { // Sends the operations in a single Mutate request. MutateGoogleAdsResponse response = googleAdsServiceClient.mutate(Long.toString(customerId), mutateOperations); for (MutateOperationResponse result : response.getMutateOperationResponsesList()) { if (result.hasAssetResult()) { assetResourceNames.add(result.getAssetResult().getResourceName()); } } printResponseDetails(response); } return assetResourceNames; } /** Creates a list of MutateOperations that create a new AssetGroup. */ private List<MutateOperation> createAssetGroupOperations( long customerId, String assetGroupResourceName, List<String> headlineAssetResourceNames, List<String> descriptionAssetResourceNames) throws IOException { List<MutateOperation> mutateOperations = new ArrayList<>(); String campaignResourceName = ResourceNames.campaign(customerId, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID); // Creates the AssetGroup. AssetGroup assetGroup = AssetGroup.newBuilder() .setName("Performance Max asset group #" + getPrintableDateTime()) .setCampaign(campaignResourceName) .addFinalUrls("http://www.example.com") .addFinalMobileUrls("http://www.example.com") .setStatus(AssetGroupStatus.PAUSED) .setResourceName(assetGroupResourceName) .build(); AssetGroupOperation assetGroupOperation = AssetGroupOperation.newBuilder().setCreate(assetGroup).build(); mutateOperations.add( MutateOperation.newBuilder().setAssetGroupOperation(assetGroupOperation).build()); // For the list of required assets for a Performance Max campaign, see // https://developers.google.com/google-ads/api/docs/performance-max/assets // An AssetGroup is linked to an Asset by creating a new AssetGroupAsset // and providing: // the resource name of the AssetGroup // the resource name of the Asset // the field_type of the Asset in this AssetGroup. // To learn more about AssetGroups, see // https://developers.google.com/google-ads/api/docs/performance-max/asset-groups // Links the previously created multiple text assets. // Links the headline assets. for (String resourceName : headlineAssetResourceNames) { AssetGroupAsset assetGroupAsset = AssetGroupAsset.newBuilder() .setFieldType(AssetFieldType.HEADLINE) .setAssetGroup(assetGroupResourceName) .setAsset(resourceName) .build(); AssetGroupAssetOperation assetGroupAssetOperation = AssetGroupAssetOperation.newBuilder().setCreate(assetGroupAsset).build(); mutateOperations.add( MutateOperation.newBuilder() .setAssetGroupAssetOperation(assetGroupAssetOperation) .build()); } // Links the description assets. for (String resourceName : descriptionAssetResourceNames) { AssetGroupAsset assetGroupAsset = AssetGroupAsset.newBuilder() .setFieldType(AssetFieldType.DESCRIPTION) .setAssetGroup(assetGroupResourceName) .setAsset(resourceName) .build(); AssetGroupAssetOperation assetGroupAssetOperation = AssetGroupAssetOperation.newBuilder().setCreate(assetGroupAsset).build(); mutateOperations.add( MutateOperation.newBuilder() .setAssetGroupAssetOperation(assetGroupAssetOperation) .build()); } // Creates and links the long headline text asset. List<MutateOperation> createAndLinkTextAssetOperations = createAndLinkTextAsset(customerId, "Travel the World", AssetFieldType.LONG_HEADLINE); mutateOperations.addAll(createAndLinkTextAssetOperations); // Creates and links the business name text asset. createAndLinkTextAssetOperations = createAndLinkTextAsset(customerId, "Interplanetary Cruises", AssetFieldType.BUSINESS_NAME); mutateOperations.addAll(createAndLinkTextAssetOperations); // Creates and links the image assets. // Creates and links the Logo Asset. createAndLinkTextAssetOperations = createAndLinkImageAsset( customerId, "https://gaagl.page.link/bjYi", AssetFieldType.LOGO, "Marketing Logo"); mutateOperations.addAll(createAndLinkTextAssetOperations); // Creates and links the Marketing Image Asset. createAndLinkTextAssetOperations = createAndLinkImageAsset( customerId, "https://gaagl.page.link/Eit5", AssetFieldType.MARKETING_IMAGE, "Marketing Image"); mutateOperations.addAll(createAndLinkTextAssetOperations); // Creates and links the Square Marketing Image Asset. createAndLinkTextAssetOperations = createAndLinkImageAsset( customerId, "https://gaagl.page.link/bjYi", AssetFieldType.SQUARE_MARKETING_IMAGE, "Square Marketing Image"); mutateOperations.addAll(createAndLinkTextAssetOperations); return mutateOperations; } /** Creates a list of MutateOperations that create a new linked text asset. */ List<MutateOperation> createAndLinkTextAsset( long customerId, String text, AssetFieldType assetFieldType) { List<MutateOperation> mutateOperations = new ArrayList<>(); String assetResourceName = ResourceNames.asset(customerId, getNextTemporaryId()); // Creates the Text Asset. Asset asset = Asset.newBuilder() .setResourceName(assetResourceName) .setTextAsset(TextAsset.newBuilder().setText(text).build()) .build(); AssetOperation assetOperation = AssetOperation.newBuilder().setCreate(asset).build(); mutateOperations.add(MutateOperation.newBuilder().setAssetOperation(assetOperation).build()); // Creates an AssetGroupAsset to link the Asset to the AssetGroup. AssetGroupAsset assetGroupAsset = AssetGroupAsset.newBuilder() .setFieldType(assetFieldType) .setAssetGroup(ResourceNames.assetGroup(customerId, ASSET_GROUP_TEMPORARY_ID)) .setAsset(assetResourceName) .build(); AssetGroupAssetOperation assetGroupAssetOperation = AssetGroupAssetOperation.newBuilder().setCreate(assetGroupAsset).build(); mutateOperations.add( MutateOperation.newBuilder().setAssetGroupAssetOperation(assetGroupAssetOperation).build()); return mutateOperations; } /** Creates a list of MutateOperations that create a new linked image asset. */ List<MutateOperation> createAndLinkImageAsset( long customerId, String url, AssetFieldType assetFieldType, String assetName) throws IOException { List<MutateOperation> mutateOperations = new ArrayList<>(); String assetResourceName = ResourceNames.asset(customerId, getNextTemporaryId()); // Creates a media file. byte[] assetBytes = ByteStreams.toByteArray(new URL(url).openStream()); // Creates the Image Asset. Asset asset = Asset.newBuilder() .setResourceName(assetResourceName) .setImageAsset(ImageAsset.newBuilder().setData(ByteString.copyFrom(assetBytes)).build()) // Provides a unique friendly name to identify your asset. When there is an existing // image asset with the same content but a different name, the new name will be dropped // silently. .setName(assetName) .build(); AssetOperation assetOperation = AssetOperation.newBuilder().setCreate(asset).build(); mutateOperations.add(MutateOperation.newBuilder().setAssetOperation(assetOperation).build()); // Creates an AssetGroupAsset to link the Asset to the AssetGroup. AssetGroupAsset assetGroupAsset = AssetGroupAsset.newBuilder() .setFieldType(assetFieldType) .setAssetGroup(ResourceNames.assetGroup(customerId, ASSET_GROUP_TEMPORARY_ID)) .setAsset(assetResourceName) .build(); AssetGroupAssetOperation assetGroupAssetOperation = AssetGroupAssetOperation.newBuilder().setCreate(assetGroupAsset).build(); mutateOperations.add( MutateOperation.newBuilder().setAssetGroupAssetOperation(assetGroupAssetOperation).build()); return mutateOperations; } /** * Creates a list of MutateOperations that create {@link * com.google.ads.googleads.v18.resources.AssetGroupSignal} objects. */ private List<MutateOperation> createAssetGroupSignalOperations( long customerId, String assetGroupResourceName, Long audienceId) { List<MutateOperation> mutateOperations = new ArrayList<>(); if (audienceId != null) { // Creates an audience asset group signal. // To learn more about Audience Signals, see: // https://developers.google.com/google-ads/api/performance-max/asset-group-signals#audiences AssetGroupSignal audienceSignal = AssetGroupSignal.newBuilder() .setAssetGroup(assetGroupResourceName) .setAudience( AudienceInfo.newBuilder() .setAudience(ResourceNames.audience(customerId, audienceId))) .build(); mutateOperations.add( MutateOperation.newBuilder() .setAssetGroupSignalOperation( AssetGroupSignalOperation.newBuilder().setCreate(audienceSignal)) .build()); } // Creates a search theme asset group signal. // To learn more about Search Themes Signals, see: // https://developers.google.com/google-ads/api/performance-max/asset-group-signals#search_themes AssetGroupSignal searchThemeSignal = AssetGroupSignal.newBuilder() .setAssetGroup(assetGroupResourceName) .setSearchTheme(SearchThemeInfo.newBuilder().setText("travel").build()) .build(); mutateOperations.add( MutateOperation.newBuilder() .setAssetGroupSignalOperation( AssetGroupSignalOperation.newBuilder().setCreate(searchThemeSignal)) .build()); return mutateOperations; } /** * Prints the details of a MutateGoogleAdsResponse. * * <p>Parses the "response" oneof field name and uses it to extract the new entity's name and * resource name. */ private void printResponseDetails(MutateGoogleAdsResponse response) { // Parses the Mutate response to print details about the entities that were created by the // request. String suffix = "_result"; for (MutateOperationResponse result : response.getMutateOperationResponsesList()) { for (Entry<FieldDescriptor, Object> responseFields : result.getAllFields().entrySet()) { String fieldName = responseFields.getKey().getName(); String value = responseFields.getValue().toString().trim(); if (fieldName.endsWith(suffix)) { fieldName = fieldName.substring(0, fieldName.length() - suffix.length()); } System.out.printf("Created a(n) %s with %s.%n", fieldName, value); } } } /** Returns the next temporary ID and decreases it by one. */ private long getNextTemporaryId() { return temporaryId--; } }
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 CommandLine; using Google.Ads.Gax.Examples; using Google.Ads.Gax.Util; using Google.Ads.GoogleAds.Config; using Google.Ads.GoogleAds.Lib; using Google.Ads.GoogleAds.V18.Common; using Google.Ads.GoogleAds.V18.Errors; using Google.Ads.GoogleAds.V18.Resources; using Google.Ads.GoogleAds.V18.Services; using Google.Protobuf; using System; using System.Collections.Generic; using System.Threading; using static Google.Ads.GoogleAds.V18.Enums.AdvertisingChannelTypeEnum.Types; using static Google.Ads.GoogleAds.V18.Enums.AssetFieldTypeEnum.Types; using static Google.Ads.GoogleAds.V18.Enums.AssetGroupStatusEnum.Types; using static Google.Ads.GoogleAds.V18.Enums.BudgetDeliveryMethodEnum.Types; using static Google.Ads.GoogleAds.V18.Enums.CampaignStatusEnum.Types; namespace Google.Ads.GoogleAds.Examples.V18 { /// <summary> /// This example shows how to create a Performance Max campaign. /// /// For more information about Performance Max campaigns, see /// https://developers.google.com/google-ads/api/docs/performance-max/overview /// /// Prerequisites: /// - You must have at least one conversion action in the account. For /// more about conversion actions, see /// https://developers.google.com/google-ads/api/docs/conversions/overview#conversion_actions /// /// This example uses the default customer conversion goals. For an example /// of setting campaign-specific conversion goals, see /// ShoppingAds/AddPerformanceMaxRetailCampaign.cs /// </summary> public class AddPerformanceMaxCampaign : ExampleBase { /// <summary> /// Command line options for running the <see cref="AddPerformanceMaxCampaign"/> 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> /// Optional: An audience ID to use to improve the targeting of the Performance Max /// campaign. /// </summary> [Option("audienceId", Required = false, HelpText = "The ID of an audience.")] public long? AudienceId { 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); AddPerformanceMaxCampaign codeExample = new AddPerformanceMaxCampaign(); Console.WriteLine(codeExample.Description); codeExample.Run( new GoogleAdsClient(), options.CustomerId, options.AudienceId ); } // We specify temporary IDs that are specific to a single mutate request. Temporary IDs are // always negative and unique within one mutate request. // // See https://developers.google.com/google-ads/api/docs/mutating/best-practices for further // details. // // These temporary IDs are fixed because they are used in multiple places. private const int TEMPORARY_ID_BUDGET = -1; private const int TEMPORARY_ID_CAMPAIGN = -2; private const int TEMPORARY_ID_ASSET_GROUP = -3; // There are also entities that will be created in the same request but do not need to be // fixed temporary IDs because they are referenced only once. private class AssetGroupAssetTemporaryResourceNameGenerator { private long customerId; private long assetGroupId; private long next; public AssetGroupAssetTemporaryResourceNameGenerator(long customerId, long assetGroupId) { this.customerId = customerId; this.assetGroupId = assetGroupId; this.next = assetGroupId - 1; } public string Next() { long i = next; Interlocked.Decrement(ref next); return ResourceNames.Asset(customerId, i); } } /// <summary> /// Returns a description about the code example. /// </summary> public override string Description => "This example shows how to create a Performance Max campaign."; /// <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="audienceId">The optional audience ID.</param> public void Run(GoogleAdsClient client, long customerId, long? audienceId) { try { GoogleAdsServiceClient googleAdsServiceClient = client.GetService(Services.V18.GoogleAdsService); // Performance Max campaigns require that repeated assets such as headlines and // descriptions be created before the campaign. // // For the list of required assets for a Performance Max campaign, see // https://developers.google.com/google-ads/api/docs/performance-max/assets // // Create the headlines. List<string> headlineAssetResourceNames = CreateMultipleTextAssets( client, customerId, new[] { "Travel", "Travel Reviews", "Book travel" } ); // Create the descriptions. List<string> descriptionAssetResourceNames = CreateMultipleTextAssets( client, customerId, new[] { "Take to the air!", "Fly to the sky!" } ); string tempResourceNameCampaignBudget = ResourceNames.CampaignBudget( customerId, TEMPORARY_ID_BUDGET ); // 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 Performance Max campaign are closely tied to // one-another, it is 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 MutateOperation campaignBudgetOperation = CreateCampaignBudgetOperation( tempResourceNameCampaignBudget ); string tempResourceNameCampaign = ResourceNames.Campaign( customerId, TEMPORARY_ID_CAMPAIGN ); MutateOperation performanceMaxCampaignOperation = CreatePerformanceMaxCampaignOperation( tempResourceNameCampaign, tempResourceNameCampaignBudget ); List<MutateOperation> campaignCriterionOperations = CreateCampaignCriterionOperations(tempResourceNameCampaign); List<MutateOperation> assetGroupOperations = CreateAssetGroupOperations( tempResourceNameCampaign, ResourceNames.AssetGroup(customerId, TEMPORARY_ID_ASSET_GROUP), headlineAssetResourceNames, descriptionAssetResourceNames, new AssetGroupAssetTemporaryResourceNameGenerator( customerId, TEMPORARY_ID_ASSET_GROUP ), client.Config ); List<MutateOperation> assetGroupSignalOperations = CreateAssetGroupSignalOperations( customerId, ResourceNames.AssetGroup(customerId, TEMPORARY_ID_ASSET_GROUP), audienceId ); MutateGoogleAdsRequest request = new MutateGoogleAdsRequest { CustomerId = customerId.ToString() }; // It's important to create these entities in this order because they depend on // each other. // // Additionally, we take several lists of operations and flatten them into one // large list. request.MutateOperations.Add(campaignBudgetOperation); request.MutateOperations.Add(performanceMaxCampaignOperation); request.MutateOperations.AddRange(campaignCriterionOperations); request.MutateOperations.AddRange(assetGroupOperations); request.MutateOperations.AddRange(assetGroupSignalOperations); MutateGoogleAdsResponse response = googleAdsServiceClient.Mutate(request); 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> /// 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="budgetResourceName">The temporary resource name of the budget to /// create.</param> /// <returns>A MutateOperation that creates a CampaignBudget.</returns> private MutateOperation CreateCampaignBudgetOperation(string budgetResourceName) { MutateOperation operation = new MutateOperation { CampaignBudgetOperation = new CampaignBudgetOperation { Create = new CampaignBudget { Name = "Performance Max campaign budget #" + ExampleUtilities.GetRandomString(), // The budget period already defaults to DAILY. AmountMicros = 50000000, DeliveryMethod = BudgetDeliveryMethod.Standard, // A Performance Max campaign cannot use a shared campaign budget. ExplicitlyShared = false, // Set a temporary ID in the budget's resource name so it can be referenced // by the campaign in later steps. ResourceName = budgetResourceName } } }; return operation; } /// Creates a MutateOperation that creates a new Performance Max campaign. /// <param name="campaignResourceName">The campaign resource name.</param> /// <param name="campaignBudgetResourceName">The campaign budget resource name.</param> /// <returns>A MutateOperations that will create this new campaign.</returns> private MutateOperation CreatePerformanceMaxCampaignOperation( string campaignResourceName, string campaignBudgetResourceName) { MutateOperation operation = new MutateOperation() { CampaignOperation = new CampaignOperation() { Create = new Campaign() { Name = "Performance Max 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, // All Performance Max campaigns have an AdvertisingChannelType of // PerformanceMax. The AdvertisingChannelSubType should not be set. AdvertisingChannelType = AdvertisingChannelType.PerformanceMax, // Bidding strategy must be set directly on the campaign. Setting a // portfolio bidding strategy by resource name is not supported. Max // Conversion and Maximize Conversion Value are the only strategies // supported for Performance Max campaigns. BiddingStrategyType is // read-only and cannot be set by the API. An optional ROAS (Return on // Advertising Spend) can be set to enable the MaximizeConversionValue // bidding strategy. The ROAS value must be specified as a ratio in the API. // It is calculated by dividing "total value" by "total spend". // // For more information on Maximize Conversion Value, see the support // article: // http://support.google.com/google-ads/answer/7684216. // // A target_roas of 3.5 corresponds to a 350% return on ad spend. MaximizeConversionValue = new MaximizeConversionValue() { TargetRoas = 3.5 }, // Set the Final URL expansion opt out. This flag is specific to // Performance Max campaigns. If opted out (True), only the final URLs in // the asset group or URLs specified in the advertiser's Google Merchant // Center or business data feeds are targeted. // If opted in (False), the entire domain will be targeted. For best // results, set this value to false to opt in and allow URL expansions. You // can optionally add exclusions to limit traffic to parts of your website. UrlExpansionOptOut = false, // Use the temporary resource name created earlier ResourceName = campaignResourceName, // Set the budget using the given budget resource name. CampaignBudget = campaignBudgetResourceName, // Optional fields StartDate = DateTime.Now.AddDays(1).ToString("yyyyMMdd"), EndDate = DateTime.Now.AddDays(365).ToString("yyyyMMdd") } } }; return operation; } /// <summary> /// Creates a list of MutateOperations that create new campaign criteria. /// </summary> /// <param name="campaignResourceName">The campaign resource name.</param> /// <returns>A list of MutateOperations that create new campaign criteria.</returns> private List<MutateOperation> CreateCampaignCriterionOperations( string campaignResourceName) { List<MutateOperation> operations = new List<MutateOperation>(); // Set the LOCATION campaign criteria. // Target all of New York City except Brooklyn. // Location IDs are listed here: // https://developers.google.com/google-ads/api/reference/data/geotargets // and they can also be retrieved using the GeoTargetConstantService as shown // here: https://developers.google.com/google-ads/api/docs/targeting/location-targeting // // We will add one positive location target for New York City (ID=1023191) // and one negative location target for Brooklyn (ID=1022762). // First, add the positive (negative = False) for New York City. MutateOperation operation1 = new MutateOperation() { CampaignCriterionOperation = new CampaignCriterionOperation() { Create = new CampaignCriterion() { Campaign = campaignResourceName, Location = new LocationInfo() { GeoTargetConstant = ResourceNames.GeoTargetConstant(1023191) }, Negative = false } } }; operations.Add(operation1); // Next add the negative target for Brooklyn. MutateOperation operation2 = new MutateOperation() { CampaignCriterionOperation = new CampaignCriterionOperation() { Create = new CampaignCriterion() { Campaign = campaignResourceName, Location = new LocationInfo() { GeoTargetConstant = ResourceNames.GeoTargetConstant(1022762) }, Negative = true } } }; operations.Add(operation2); // Set the LANGUAGE campaign criterion. MutateOperation operation3 = new MutateOperation() { CampaignCriterionOperation = new CampaignCriterionOperation() { Create = new CampaignCriterion() { Campaign = campaignResourceName, // Set the language. // For a list of all language codes, see: // https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-7 Language = new LanguageInfo() { LanguageConstant = ResourceNames.LanguageConstant(1000) // English }, } } }; operations.Add(operation3); return operations; } /// <summary> /// Creates multiple text assets and returns the list of resource names. /// </summary> /// <param name="client">The Google Ads Client.</param> /// <param name="customerId">The customer's ID.</param> /// <param name="texts">The texts to add.</param> /// <returns>A list of asset resource names.</returns> private List<string> CreateMultipleTextAssets( GoogleAdsClient client, long customerId, string[] texts) { // Get the GoogleAdsService. GoogleAdsServiceClient googleAdsServiceClient = client.GetService(Services.V18.GoogleAdsService); MutateGoogleAdsRequest request = new MutateGoogleAdsRequest() { CustomerId = customerId.ToString() }; foreach (string text in texts) { request.MutateOperations.Add( new MutateOperation() { AssetOperation = new AssetOperation() { Create = new Asset() { TextAsset = new TextAsset() { Text = text } } } } ); } // Send the operations in a single Mutate request. MutateGoogleAdsResponse response = googleAdsServiceClient.Mutate(request); List<string> assetResourceNames = new List<string>(); foreach (MutateOperationResponse operationResponse in response.MutateOperationResponses) { MutateAssetResult assetResult = operationResponse.AssetResult; assetResourceNames.Add(assetResult.ResourceName); } PrintResponseDetails(response); return assetResourceNames; } /// <summary> /// Creates a list of MutateOperations that create a new asset_group. /// </summary> /// <param name="campaignResourceName">The campaign resource name.</param> /// <param name="assetGroupResourceName">The asset group resource name.</param> /// <param name="headlineAssetResourceNames">The headline asset resource names.</param> /// <param name="descriptionAssetResourceNames">The description asset resource /// names.</param> /// <param name="resourceNameGenerator">A generator for unique temporary ID's.</param> /// <param name="config">The Google Ads config.</param> /// <returns>A list of MutateOperations that create the new asset group.</returns> private List<MutateOperation> CreateAssetGroupOperations( string campaignResourceName, string assetGroupResourceName, List<string> headlineAssetResourceNames, List<string> descriptionAssetResourceNames, AssetGroupAssetTemporaryResourceNameGenerator resourceNameGenerator, GoogleAdsConfig config) { List<MutateOperation> operations = new List<MutateOperation>(); // Create the AssetGroup operations.Add( new MutateOperation() { AssetGroupOperation = new AssetGroupOperation() { Create = new AssetGroup() { Name = "Performance Max asset group #" + ExampleUtilities.GetRandomString(), Campaign = campaignResourceName, FinalUrls = { "http://www.example.com" }, FinalMobileUrls = { "http://www.example.com" }, Status = AssetGroupStatus.Paused, ResourceName = assetGroupResourceName } } } ); // For the list of required assets for a Performance Max campaign, see // https://developers.google.com/google-ads/api/docs/performance-max/assets // An AssetGroup is linked to an Asset by creating a new AssetGroupAsset // and providing: // the resource name of the AssetGroup // the resource name of the Asset // the field_type of the Asset in this AssetGroup. // // To learn more about AssetGroups, see // https://developers.google.com/google-ads/api/docs/performance-max/asset-groups // Link the previously created multiple text assets. // Link the headline assets. foreach (string resourceName in headlineAssetResourceNames) { operations.Add( new MutateOperation() { AssetGroupAssetOperation = new AssetGroupAssetOperation() { Create = new AssetGroupAsset() { FieldType = AssetFieldType.Headline, AssetGroup = assetGroupResourceName, Asset = resourceName } } } ); } // Link the description assets. foreach (string resourceName in descriptionAssetResourceNames) { operations.Add( new MutateOperation() { AssetGroupAssetOperation = new AssetGroupAssetOperation() { Create = new AssetGroupAsset() { FieldType = AssetFieldType.Description, AssetGroup = assetGroupResourceName, Asset = resourceName } } } ); } // Create and link the long headline text asset. operations.AddRange( CreateAndLinkTextAsset( assetGroupResourceName, resourceNameGenerator.Next(), "Travel the World", AssetFieldType.LongHeadline ) ); // Create and link the business name text asset. operations.AddRange( CreateAndLinkTextAsset( assetGroupResourceName, resourceNameGenerator.Next(), "Interplanetary Cruises", AssetFieldType.BusinessName ) ); // Create and link the image assets. // Create and link the Logo Asset. operations.AddRange( CreateAndLinkImageAsset( assetGroupResourceName, resourceNameGenerator.Next(), "https://gaagl.page.link/bjYi", AssetFieldType.Logo, "Marketing Logo", config ) ); // Create and link the Marketing Image Asset. operations.AddRange( CreateAndLinkImageAsset( assetGroupResourceName, resourceNameGenerator.Next(), "https://gaagl.page.link/Eit5", AssetFieldType.MarketingImage, "Marketing Image", config ) ); // Create and link the Square Marketing Image Asset. operations.AddRange( CreateAndLinkImageAsset( assetGroupResourceName, resourceNameGenerator.Next(), "https://gaagl.page.link/bjYi", AssetFieldType.SquareMarketingImage, "Square Marketing Image", config ) ); return operations; } /// <summary> /// Creates a list of MutateOperations that create a new linked text asset. /// </summary> /// <param name="assetGroupResourceName">The resource name of the asset group to be /// created.</param> /// <param name="assetResourceName">The resource name of the text asset to be /// created.</param> /// <param name="text">The text of the asset to be created.</param> /// <param name="fieldType">The field type of the asset to be created.</param> /// <returns>A list of MutateOperations that create the new linked text asset.</returns> private List<MutateOperation> CreateAndLinkTextAsset( string assetGroupResourceName, string assetResourceName, string text, AssetFieldType fieldType) { List<MutateOperation> operations = new List<MutateOperation>(); // Create the Text Asset. operations.Add( new MutateOperation() { AssetOperation = new AssetOperation() { Create = new Asset() { ResourceName = assetResourceName, TextAsset = new TextAsset() { Text = text } } } } ); // Create an AssetGroupAsset to link the Asset to the AssetGroup. operations.Add( new MutateOperation() { AssetGroupAssetOperation = new AssetGroupAssetOperation() { Create = new AssetGroupAsset() { FieldType = fieldType, AssetGroup = assetGroupResourceName, Asset = assetResourceName } } } ); return operations; } /// <summary> /// Creates a list of MutateOperations that create a new linked image asset. /// </summary> /// <param name="assetGroupResourceName">The resource name of the asset group to be /// created.</param> /// <param name="assetResourceName">The resource name of the text asset to be /// created.</param> /// <param name="url">The url of the image to be retrieved and put into an asset.</param> /// <param name="fieldType">The field type of the asset to be created.</param> /// <param name="assetName">The asset name.</param> /// <param name="config">The Google Ads Config.</param> /// <returns>A list of MutateOperations that create a new linked image asset.</returns> private List<MutateOperation> CreateAndLinkImageAsset( string assetGroupResourceName, string assetResourceName, string url, AssetFieldType fieldType, string assetName, GoogleAdsConfig config) { List<MutateOperation> operations = new List<MutateOperation>(); // Create the Image Asset. operations.Add( new MutateOperation() { AssetOperation = new AssetOperation() { Create = new Asset() { ResourceName = assetResourceName, ImageAsset = new ImageAsset() { Data = ByteString.CopyFrom( MediaUtilities.GetAssetDataFromUrl(url, config) ) }, // Provide a unique friendly name to identify your asset. // When there is an existing image asset with the same content but a // different name, the new name will be dropped silently. Name = assetName } } } ); // Create an AssetGroupAsset to link the Asset to the AssetGroup. operations.Add( new MutateOperation() { AssetGroupAssetOperation = new AssetGroupAssetOperation() { Create = new AssetGroupAsset() { FieldType = fieldType, AssetGroup = assetGroupResourceName, Asset = assetResourceName } } } ); return operations; } /// <summary> /// Creates a list of MutateOperations that may create AssetGroupSignals /// </summary> /// <param name="customerId">The customer ID.</param> /// <param name="assetGroupResourceName">The resource name of the asset group to be /// created.</param> /// <param name="audienceId">The optional audience ID.</param> /// <returns>A list of MutateOperations that create may create AssetGroupSignals.</returns> private List<MutateOperation> CreateAssetGroupSignalOperations( long customerId, string assetGroupResourceName, long? audienceId) { List<MutateOperation> operations = new List<MutateOperation>(); if (audienceId.HasValue) { // Create an audience asset group signal. // To learn more about Audience Signals, see // https://developers.google.com/google-ads/api/docs/performance-max/asset-groups#audience_signals operations.Add( new MutateOperation() { AssetGroupSignalOperation = new AssetGroupSignalOperation() { Create = new AssetGroupSignal() { AssetGroup = assetGroupResourceName, Audience = new AudienceInfo() { Audience = ResourceNames.Audience(customerId, audienceId.Value) } } } } ); } // Create a search theme asset group signal. // To learn more about Search Themes Signals, see: // https://developers.google.com/google-ads/api/performance-max/asset-group-signals#search_themes operations.Add( new MutateOperation() { AssetGroupSignalOperation = new AssetGroupSignalOperation() { Create = new AssetGroupSignal() { AssetGroup = assetGroupResourceName, SearchTheme = new SearchThemeInfo() { Text = "travel" } } } } ); return operations; } /// <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 entityName = operationResponse.ResponseCase.ToString(); // Trim the substring "Result" from the end of the entity name. entityName = entityName.Remove(entityName.Length - 6); string resourceName; 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; case MutateOperationResponse.ResponseOneofCase.AssetResult: resourceName = operationResponse.AssetResult.ResourceName; break; case MutateOperationResponse.ResponseOneofCase.AssetGroupResult: resourceName = operationResponse.AssetGroupResult.ResourceName; break; case MutateOperationResponse.ResponseOneofCase.AssetGroupAssetResult: resourceName = operationResponse.AssetGroupAssetResult.ResourceName; break; default: resourceName = "<not found>"; 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\V18\GoogleAdsClient; use Google\Ads\GoogleAds\Lib\V18\GoogleAdsClientBuilder; use Google\Ads\GoogleAds\Lib\V18\GoogleAdsException; use Google\Ads\GoogleAds\Util\V18\ResourceNames; use Google\Ads\GoogleAds\V18\Common\AudienceInfo; use Google\Ads\GoogleAds\V18\Common\ImageAsset; use Google\Ads\GoogleAds\V18\Common\LanguageInfo; use Google\Ads\GoogleAds\V18\Common\LocationInfo; use Google\Ads\GoogleAds\V18\Common\MaximizeConversionValue; use Google\Ads\GoogleAds\V18\Common\TextAsset; use Google\Ads\GoogleAds\V18\Enums\AdvertisingChannelTypeEnum\AdvertisingChannelType; use Google\Ads\GoogleAds\V18\Enums\AssetFieldTypeEnum\AssetFieldType; use Google\Ads\GoogleAds\V18\Enums\AssetGroupStatusEnum\AssetGroupStatus; use Google\Ads\GoogleAds\V18\Enums\BudgetDeliveryMethodEnum\BudgetDeliveryMethod; use Google\Ads\GoogleAds\V18\Enums\CampaignStatusEnum\CampaignStatus; use Google\Ads\GoogleAds\V18\Errors\GoogleAdsError; use Google\Ads\GoogleAds\V18\Resources\Asset; use Google\Ads\GoogleAds\V18\Resources\AssetGroup; use Google\Ads\GoogleAds\V18\Resources\AssetGroupAsset; use Google\Ads\GoogleAds\V18\Resources\AssetGroupSignal; use Google\Ads\GoogleAds\V18\Resources\Campaign; use Google\Ads\GoogleAds\V18\Resources\CampaignBudget; use Google\Ads\GoogleAds\V18\Resources\CampaignCriterion; use Google\Ads\GoogleAds\V18\Services\AssetGroupAssetOperation; use Google\Ads\GoogleAds\V18\Services\AssetGroupOperation; use Google\Ads\GoogleAds\V18\Services\AssetGroupSignalOperation; use Google\Ads\GoogleAds\V18\Services\AssetOperation; use Google\Ads\GoogleAds\V18\Services\CampaignBudgetOperation; use Google\Ads\GoogleAds\V18\Services\CampaignCriterionOperation; use Google\Ads\GoogleAds\V18\Services\CampaignOperation; use Google\Ads\GoogleAds\V18\Services\MutateGoogleAdsRequest; use Google\Ads\GoogleAds\V18\Services\MutateGoogleAdsResponse; use Google\Ads\GoogleAds\V18\Services\MutateOperation; use Google\Ads\GoogleAds\V18\Services\MutateOperationResponse; use Google\ApiCore\ApiException; use Google\ApiCore\Serializer; /** * This example shows how to create a Performance Max campaign. * * For more information about Performance Max campaigns, see * https://developers.google.com/google-ads/api/docs/performance-max/overview. * * Prerequisites: * - You must have at least one conversion action in the account. For more about conversion actions, * see * https://developers.google.com/google-ads/api/docs/conversions/overview#conversion_actions. * * This example uses the default customer conversion goals. For an example of setting * campaign-specific conversion goals, see ShoppingAds/AddPerformanceMaxRetailCampaign.php. */ class AddPerformanceMaxCampaign { private const CUSTOMER_ID = 'INSERT_CUSTOMER_ID_HERE'; // Optional: An audience ID to use to improve the targeting of the Performance Max campaign. private const AUDIENCE_ID = null; // We specify temporary IDs that are specific to a single mutate request. // Temporary IDs are always negative and unique within one mutate request. // // See https://developers.google.com/google-ads/api/docs/mutating/best-practices // for further details. // // These temporary IDs are fixed because they are used in multiple places. private const BUDGET_TEMPORARY_ID = -1; private const PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID = -2; private const ASSET_GROUP_TEMPORARY_ID = -3; // There are also entities that will be created in the same request but do not need to be fixed // temporary IDs because they are referenced only once. /** @var int the negative temporary ID used in bulk mutates. */ private static $nextTempId = self::ASSET_GROUP_TEMPORARY_ID - 1; public static function main() { // Either pass the required parameters for this example on the command line, or insert them // into the constants above. $options = (new ArgumentParser())->parseCommandArguments([ ArgumentNames::CUSTOMER_ID => GetOpt::REQUIRED_ARGUMENT, ArgumentNames::AUDIENCE_ID => GetOpt::OPTIONAL_ARGUMENT ]); // Generate a refreshable OAuth2 credential for authentication. $oAuth2Credential = (new OAuth2TokenBuilder())->fromFile()->build(); // Construct a Google Ads client configured from a properties file and the // OAuth2 credentials above. $googleAdsClient = (new GoogleAdsClientBuilder()) ->fromFile() ->withOAuth2Credential($oAuth2Credential) ->build(); try { self::runExample( $googleAdsClient, $options[ArgumentNames::CUSTOMER_ID] ?: self::CUSTOMER_ID, $options[ArgumentNames::AUDIENCE_ID] ?: self::AUDIENCE_ID ); } catch (GoogleAdsException $googleAdsException) { printf( "Request with ID '%s' has failed.%sGoogle Ads failure details:%s", $googleAdsException->getRequestId(), PHP_EOL, PHP_EOL ); foreach ($googleAdsException->getGoogleAdsFailure()->getErrors() as $error) { /** @var GoogleAdsError $error */ printf( "\t%s: %s%s", $error->getErrorCode()->getErrorCode(), $error->getMessage(), PHP_EOL ); } exit(1); } catch (ApiException $apiException) { printf( "ApiException was thrown with message '%s'.%s", $apiException->getMessage(), PHP_EOL ); exit(1); } } /** * Runs the example. * * @param GoogleAdsClient $googleAdsClient the Google Ads API client * @param int $customerId the customer ID * @param int|null $audienceId the audience ID */ public static function runExample( GoogleAdsClient $googleAdsClient, int $customerId, ?int $audienceId ) { // Performance Max campaigns require that repeated assets such as headlines // and descriptions be created before the campaign. // For the list of required assets for a Performance Max campaign, see // https://developers.google.com/google-ads/api/docs/performance-max/assets. // // Creates the headlines. $headlineAssetResourceNames = self::createMultipleTextAssets( $googleAdsClient, $customerId, ["Travel", "Travel Reviews", "Book travel"] ); // Creates the descriptions. $descriptionAssetResourceNames = self::createMultipleTextAssets( $googleAdsClient, $customerId, ["Take to the air!", "Fly to the sky!"] ); // It's important to create the below entities in this order because they depend on // each other. $operations = []; // 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 Performance Max // 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. $operations[] = self::createCampaignBudgetOperation($customerId); $operations[] = self::createPerformanceMaxCampaignOperation($customerId); $operations = array_merge($operations, self::createCampaignCriterionOperations($customerId)); $operations = array_merge($operations, self::createAssetGroupOperations( $customerId, $headlineAssetResourceNames, $descriptionAssetResourceNames )); $operations = array_merge($operations, self::createAssetGroupSignalOperations( $customerId, ResourceNames::forAssetGroup($customerId, self::ASSET_GROUP_TEMPORARY_ID), $audienceId )); // Issues a mutate request to create everything and prints its information. $googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient(); $response = $googleAdsServiceClient->mutate(MutateGoogleAdsRequest::build( $customerId, $operations )); self::printResponseDetails($response); } /** * 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 * @return MutateOperation the mutate operation that creates a campaign budget */ private static function createCampaignBudgetOperation(int $customerId): MutateOperation { // Creates a mutate operation that creates a campaign budget operation. return new MutateOperation([ 'campaign_budget_operation' => new CampaignBudgetOperation([ 'create' => new CampaignBudget([ // 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 ), 'name' => 'Performance Max campaign budget #' . Helper::getPrintableDatetime(), // The budget period already defaults to DAILY. 'amount_micros' => 50000000, 'delivery_method' => BudgetDeliveryMethod::STANDARD, // A Performance Max campaign cannot use a shared campaign budget. 'explicitly_shared' => false ]) ]) ]); } /** * Creates a MutateOperation that creates a new Performance Max 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 the mutate operation that creates the campaign */ private static function createPerformanceMaxCampaignOperation(int $customerId): MutateOperation { // Creates a mutate operation that creates a campaign operation. return new MutateOperation([ 'campaign_operation' => new CampaignOperation([ 'create' => new Campaign([ 'name' => 'Performance Max campaign #' . Helper::getPrintableDatetime(), // Assigns the resource name with a temporary ID. 'resource_name' => ResourceNames::forCampaign( $customerId, self::PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID ), // Sets the budget using the given budget resource name. 'campaign_budget' => ResourceNames::forCampaignBudget( $customerId, self::BUDGET_TEMPORARY_ID ), // The campaign is the only entity in the mutate request that should have its // status set. // Recommendation: Set the campaign to PAUSED when creating it to prevent // the ads from immediately serving. 'status' => CampaignStatus::PAUSED, // All Performance Max campaigns have an advertising_channel_type of // PERFORMANCE_MAX. The advertising_channel_sub_type should not be set. 'advertising_channel_type' => AdvertisingChannelType::PERFORMANCE_MAX, // Bidding strategy must be set directly on the campaign. // Setting a portfolio bidding strategy by resource name is not supported. // Max Conversion and Maximize Conversion Value are the only strategies // supported for Performance Max campaigns. // An optional ROAS (Return on Advertising Spend) can be set for // maximize_conversion_value. The ROAS value must be specified as a ratio in // the API. It is calculated by dividing "total value" by "total spend". // For more information on Maximize Conversion Value, see the support // article: http://support.google.com/google-ads/answer/7684216. // A target_roas of 3.5 corresponds to a 350% return on ad spend. 'maximize_conversion_value' => new MaximizeConversionValue([ 'target_roas' => 3.5 ]), // Sets the Final URL expansion opt out. This flag is specific to // Performance Max campaigns. If opted out (true), only the final URLs in // the asset group or URLs specified in the advertiser's Google Merchant // Center or business data feeds are targeted. // If opted in (false), the entire domain will be targeted. For best // results, set this value to false to opt in and allow URL expansions. You // can optionally add exclusions to limit traffic to parts of your website. 'url_expansion_opt_out' => false, // Optional fields. 'start_date' => date('Ymd', strtotime('+1 day')), 'end_date' => date('Ymd', strtotime('+365 days')) ]) ]) ]); } /** * Creates a list of MutateOperations that create new campaign criteria. * * @param int $customerId the customer ID * @return MutateOperation[] a list of MutateOperations that create the new campaign criteria */ private static function createCampaignCriterionOperations(int $customerId): array { $operations = []; // Set the LOCATION campaign criteria. // Target all of New York City except Brooklyn. // Location IDs are listed here: // https://developers.google.com/google-ads/api/reference/data/geotargets // and they can also be retrieved using the GeoTargetConstantService as shown // here: https://developers.google.com/google-ads/api/docs/targeting/location-targeting // // We will add one positive location target for New York City (ID=1023191) // and one negative location target for Brooklyn (ID=1022762). // First, adds the positive (negative = false) for New York City. $operations[] = new MutateOperation([ 'campaign_criterion_operation' => new CampaignCriterionOperation([ 'create' => new CampaignCriterion([ 'campaign' => ResourceNames::forCampaign( $customerId, self::PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID ), 'location' => new LocationInfo([ 'geo_target_constant' => ResourceNames::forGeoTargetConstant(1023191) ]), 'negative' => false ]) ]) ]); // Next adds the negative target for Brooklyn. $operations[] = new MutateOperation([ 'campaign_criterion_operation' => new CampaignCriterionOperation([ 'create' => new CampaignCriterion([ 'campaign' => ResourceNames::forCampaign( $customerId, self::PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID ), 'location' => new LocationInfo([ 'geo_target_constant' => ResourceNames::forGeoTargetConstant(1022762) ]), 'negative' => true ]) ]) ]); // Sets the LANGUAGE campaign criterion. $operations[] = new MutateOperation([ 'campaign_criterion_operation' => new CampaignCriterionOperation([ 'create' => new CampaignCriterion([ 'campaign' => ResourceNames::forCampaign( $customerId, self::PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID ), // Set the language. // For a list of all language codes, see: // https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-7 'language' => new LanguageInfo([ 'language_constant' => ResourceNames::forLanguageConstant(1000) // English ]) ]) ]) ]); return $operations; } /** * Creates multiple text assets and returns the list of resource names. * * @param GoogleAdsClient $googleAdsClient the Google Ads API client * @param int $customerId the customer ID * @param string[] $texts a list of strings, each of which will be used to create a text asset * @return string[] a list of asset resource names */ private static function createMultipleTextAssets( GoogleAdsClient $googleAdsClient, int $customerId, array $texts ): array { // Here again, we use the GoogleAdService to create multiple text assets in a single // request. $operations = []; foreach ($texts as $text) { // Creates a mutate operation for a text asset. $operations[] = new MutateOperation([ 'asset_operation' => new AssetOperation([ 'create' => new Asset(['text_asset' => new TextAsset(['text' => $text])]) ]) ]); } // Issues a mutate request to add all assets. $googleAdsService = $googleAdsClient->getGoogleAdsServiceClient(); /** @var MutateGoogleAdsResponse $mutateGoogleAdsResponse */ $mutateGoogleAdsResponse = $googleAdsService->mutate(MutateGoogleAdsRequest::build($customerId, $operations)); $assetResourceNames = []; foreach ($mutateGoogleAdsResponse->getMutateOperationResponses() as $response) { /** @var MutateOperationResponse $response */ $assetResourceNames[] = $response->getAssetResult()->getResourceName(); } self::printResponseDetails($mutateGoogleAdsResponse); return $assetResourceNames; } /** * Creates a list of MutateOperations that create a new asset group. * * A temporary ID will be assigned to this asset group so that it can * be referenced by other objects being created in the same Mutate request. * * @param GoogleAdsClient $googleAdsClient the Google Ads API client * @param int $customerId the customer ID * @param string[] $headlineAssetResourceNames a list of headline resource names * @param string[] $descriptionAssetResourceNames a list of description resource names * @return MutateOperation[] a list of MutateOperations that create new asset group */ private static function createAssetGroupOperations( int $customerId, array $headlineAssetResourceNames, array $descriptionAssetResourceNames ): array { $operations = []; // Creates a new mutate operation that creates an asset group operation. $operations[] = new MutateOperation([ 'asset_group_operation' => new AssetGroupOperation([ 'create' => new AssetGroup([ 'resource_name' => ResourceNames::forAssetGroup( $customerId, self::ASSET_GROUP_TEMPORARY_ID ), 'name' => 'Performance Max asset group #' . Helper::getPrintableDatetime(), 'campaign' => ResourceNames::forCampaign( $customerId, self::PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID ), 'final_urls' => ['http://www.example.com'], 'final_mobile_urls' => ['http://www.example.com'], 'status' => AssetGroupStatus::PAUSED ]) ]) ]); // For the list of required assets for a Performance Max campaign, see // https://developers.google.com/google-ads/api/docs/performance-max/assets // An AssetGroup is linked to an Asset by creating a new AssetGroupAsset // and providing: // - the resource name of the AssetGroup // - the resource name of the Asset // - the field_type of the Asset in this AssetGroup // // To learn more about AssetGroups, see // https://developers.google.com/google-ads/api/docs/performance-max/asset-groups. // Links the previously created multiple text assets. // Links the headline assets. foreach ($headlineAssetResourceNames as $resourceName) { $operations[] = new MutateOperation([ 'asset_group_asset_operation' => new AssetGroupAssetOperation([ 'create' => new AssetGroupAsset([ 'asset' => $resourceName, 'asset_group' => ResourceNames::forAssetGroup( $customerId, self::ASSET_GROUP_TEMPORARY_ID ), 'field_type' => AssetFieldType::HEADLINE ]) ]) ]); } // Links the description assets. foreach ($descriptionAssetResourceNames as $resourceName) { $operations[] = new MutateOperation([ 'asset_group_asset_operation' => new AssetGroupAssetOperation([ 'create' => new AssetGroupAsset([ 'asset' => $resourceName, 'asset_group' => ResourceNames::forAssetGroup( $customerId, self::ASSET_GROUP_TEMPORARY_ID ), 'field_type' => AssetFieldType::DESCRIPTION ]) ]) ]); } // Creates and links the long headline text asset. $operations = array_merge($operations, self::createAndLinkTextAsset( $customerId, 'Travel the World', AssetFieldType::LONG_HEADLINE )); // Creates and links the business name text asset. $operations = array_merge($operations, self::createAndLinkTextAsset( $customerId, 'Interplanetary Cruises', AssetFieldType::BUSINESS_NAME )); // Creates and links the image assets. // Creates and links the Logo Asset. $operations = array_merge($operations, self::createAndLinkImageAsset( $customerId, 'https://gaagl.page.link/bjYi', AssetFieldType::LOGO, 'Marketing Logo' )); // Creates and links the Marketing Image Asset. $operations = array_merge($operations, self::createAndLinkImageAsset( $customerId, 'https://gaagl.page.link/Eit5', AssetFieldType::MARKETING_IMAGE, 'Marketing Image' )); // Creates and links the Square Marketing Image Asset. $operations = array_merge($operations, self::createAndLinkImageAsset( $customerId, 'https://gaagl.page.link/bjYi', AssetFieldType::SQUARE_MARKETING_IMAGE, 'Square Marketing Image' )); return $operations; } /** * Creates a list of MutateOperations that create a new linked text asset. * * @param int $customerId the customer ID * @param string $text the text of the asset to be created * @param int $fieldType the field type of the new asset in the AssetGroupAsset * @return MutateOperation[] a list of MutateOperations that create a new linked text asset */ private static function createAndLinkTextAsset( int $customerId, string $text, int $fieldType ): array { $operations = []; // Creates a new mutate operation that creates a text asset. $operations[] = new MutateOperation([ 'asset_operation' => new AssetOperation([ 'create' => new Asset([ 'resource_name' => ResourceNames::forAsset($customerId, self::$nextTempId), 'text_asset' => new TextAsset(['text' => $text]) ]) ]) ]); // Creates an asset group asset to link the asset to the asset group. $operations[] = new MutateOperation([ 'asset_group_asset_operation' => new AssetGroupAssetOperation([ 'create' => new AssetGroupAsset([ 'asset' => ResourceNames::forAsset($customerId, self::$nextTempId), 'asset_group' => ResourceNames::forAssetGroup( $customerId, self::ASSET_GROUP_TEMPORARY_ID ), 'field_type' => $fieldType ]) ]) ]); self::$nextTempId--; return $operations; } /** * Creates a list of MutateOperations that create a new linked image asset. * * @param int $customerId the customer ID * @param string $url the URL of the image to be retrieved and put into an asset * @param int $fieldType the field type of the new asset in the AssetGroupAsset * @param string $assetName the asset name * @return MutateOperation[] a list of MutateOperations that create a new linked image asset */ private static function createAndLinkImageAsset( int $customerId, string $url, int $fieldType, string $assetName ): array { $operations = []; // Creates a new mutate operation that creates an image asset. $operations[] = new MutateOperation([ 'asset_operation' => new AssetOperation([ 'create' => new Asset([ 'resource_name' => ResourceNames::forAsset($customerId, self::$nextTempId), // Provide a unique friendly name to identify your asset. // When there is an existing image asset with the same content but a different // name, the new name will be dropped silently. 'name' => $assetName, 'image_asset' => new ImageAsset(['data' => file_get_contents($url)]) ]) ]) ]); // Creates an asset group asset to link the asset to the asset group. $operations[] = new MutateOperation([ 'asset_group_asset_operation' => new AssetGroupAssetOperation([ 'create' => new AssetGroupAsset([ 'asset' => ResourceNames::forAsset($customerId, self::$nextTempId), 'asset_group' => ResourceNames::forAssetGroup( $customerId, self::ASSET_GROUP_TEMPORARY_ID ), 'field_type' => $fieldType ]) ]) ]); self::$nextTempId--; return $operations; } /** * Creates a list of MutateOperations that may create asset group signals. * * @param int $customerId the customer ID * @param string $assetGroupResourceName the resource name of the asset group * @param int|null $audienceId the audience ID * @return MutateOperation[] a list of MutateOperations that may create asset group signals */ private static function createAssetGroupSignalOperations( int $customerId, string $assetGroupResourceName, ?int $audienceId ): array { $operations = []; if (is_null($audienceId)) { return $operations; } $operations[] = new MutateOperation([ 'asset_group_signal_operation' => new AssetGroupSignalOperation([ // To learn more about Audience Signals, see // https://developers.google.com/google-ads/api/docs/performance-max/asset-groups#audience_signals. 'create' => new AssetGroupSignal([ 'asset_group' => $assetGroupResourceName, 'audience' => new AudienceInfo([ 'audience' => ResourceNames::forAudience($customerId, $audienceId) ]) ]) ]) ]); return $operations; } /** * 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. * * @param MutateGoogleAdsResponse $mutateGoogleAdsResponse the mutate Google Ads response */ private static function printResponseDetails( MutateGoogleAdsResponse $mutateGoogleAdsResponse ): void { foreach ($mutateGoogleAdsResponse->getMutateOperationResponses() as $response) { /** @var MutateOperationResponse $response */ $getter = Serializer::getGetter($response->getResponse()); printf( "Created a(n) %s with '%s'.%s", preg_replace( '/Result$/', '', ucfirst(Serializer::toCamelCase($response->getResponse())) ), $response->$getter()->getResourceName(), PHP_EOL ); } } } AddPerformanceMaxCampaign::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 Performance Max campaign. For more information about Performance Max campaigns, see https://developers.google.com/google-ads/api/docs/performance-max/overview Prerequisites: - You must have at least one conversion action in the account. For more about conversion actions, see https://developers.google.com/google-ads/api/docs/conversions/overview#conversion_actions This example uses the default customer conversion goals. For an example of setting campaign-specific conversion goals, see shopping_ads/add_performance_max_retail_campaign.py """ import argparse from datetime import datetime, timedelta import sys from uuid import uuid4 from google.ads.googleads.client import GoogleAdsClient from google.ads.googleads.errors import GoogleAdsException from google.ads.googleads.util import convert_snake_case_to_upper_case import requests # We specify temporary IDs that are specific to a single mutate request. # Temporary IDs are always negative and unique within one mutate request. # # See https://developers.google.com/google-ads/api/docs/mutating/best-practices # for further details. # # These temporary IDs are fixed because they are used in multiple places. _BUDGET_TEMPORARY_ID = "-1" _PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID = "-2" _ASSET_GROUP_TEMPORARY_ID = "-3" # There are also entities that will be created in the same request but do not # need to be fixed temporary IDs because they are referenced only once. next_temp_id = int(_ASSET_GROUP_TEMPORARY_ID) - 1 def main(client, customer_id, audience_id): """The main method that creates all necessary entities for the example. Args: client: an initialized GoogleAdsClient instance. customer_id: a client customer ID. audience_id: an optional audience ID. """ googleads_service = client.get_service("GoogleAdsService") # Performance Max campaigns require that repeated assets such as headlines # and descriptions be created before the campaign. # For the list of required assets for a Performance Max campaign, see # https://developers.google.com/google-ads/api/docs/performance-max/assets # # Create the headlines. headline_asset_resource_names = create_multiple_text_assets( client, customer_id, [ "Travel", "Travel Reviews", "Book travel", ], ) # Create the descriptions. description_asset_resource_names = create_multiple_text_assets( client, customer_id, [ "Take to the air!", "Fly to the sky!", ], ) # 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 Performance Max # 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, ) performance_max_campaign_operation = ( create_performance_max_campaign_operation( client, customer_id, ) ) campaign_criterion_operations = create_campaign_criterion_operations( client, customer_id, ) asset_group_operations = create_asset_group_operation( client, customer_id, headline_asset_resource_names, description_asset_resource_names, ) asset_group_signal_operations = create_asset_group_signal_operations( client, customer_id, audience_id ) mutate_operations = [ # It's important to create these entities in this order because # they depend on each other. campaign_budget_operation, performance_max_campaign_operation, # Expand the list of multiple operations into the list of # other mutate operations *campaign_criterion_operations, *asset_group_operations, *asset_group_signal_operations, ] # Send the operations in a single Mutate request. response = googleads_service.mutate( customer_id=customer_id, mutate_operations=mutate_operations ) print_response_details(response) def create_campaign_budget_operation( client, customer_id, ): """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. 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"Performance Max campaign budget #{uuid4()}" # The budget period already defaults to DAILY. campaign_budget.amount_micros = 50000000 campaign_budget.delivery_method = ( client.enums.BudgetDeliveryMethodEnum.STANDARD ) # A Performance Max campaign cannot use a shared campaign budget. campaign_budget.explicitly_shared = False # 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_performance_max_campaign_operation( client, customer_id, ): """Creates a MutateOperation that creates a new Performance Max 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"Performance Max 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 # All Performance Max campaigns have an advertising_channel_type of # PERFORMANCE_MAX. The advertising_channel_sub_type should not be set. campaign.advertising_channel_type = ( client.enums.AdvertisingChannelTypeEnum.PERFORMANCE_MAX ) # Bidding strategy must be set directly on the campaign. # Setting a portfolio bidding strategy by resource name is not supported. # Max Conversion and Maximize Conversion Value are the only strategies # supported for Performance Max campaigns. # An optional ROAS (Return on Advertising Spend) can be set for # maximize_conversion_value. The ROAS value must be specified as a ratio in # the API. It is calculated by dividing "total value" by "total spend". # For more information on Maximize Conversion Value, see the support # article: http://support.google.com/google-ads/answer/7684216. # A target_roas of 3.5 corresponds to a 350% return on ad spend. campaign.bidding_strategy_type = ( client.enums.BiddingStrategyTypeEnum.MAXIMIZE_CONVERSION_VALUE ) campaign.maximize_conversion_value.target_roas = 3.5 # Set the Final URL expansion opt out. This flag is specific to # Performance Max campaigns. If opted out (True), only the final URLs in # the asset group or URLs specified in the advertiser's Google Merchant # Center or business data feeds are targeted. # If opted in (False), the entire domain will be targeted. For best # results, set this value to false to opt in and allow URL expansions. You # can optionally add exclusions to limit traffic to parts of your website. campaign.url_expansion_opt_out = False # Assign the resource name with a temporary ID. campaign_service = client.get_service("CampaignService") campaign.resource_name = campaign_service.campaign_path( customer_id, _PERFORMANCE_MAX_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 ) # Optional fields campaign.start_date = (datetime.now() + timedelta(1)).strftime("%Y%m%d") campaign.end_date = (datetime.now() + timedelta(365)).strftime("%Y%m%d") return mutate_operation def create_campaign_criterion_operations( client, customer_id, ): """Creates a list of MutateOperations that create new campaign criteria. Args: client: an initialized GoogleAdsClient instance. customer_id: a client customer ID. Returns: a list of MutateOperations that create new campaign criteria. """ campaign_service = client.get_service("CampaignService") geo_target_constant_service = client.get_service("GeoTargetConstantService") googleads_service = client.get_service("GoogleAdsService") operations = [] # Set the LOCATION campaign criteria. # Target all of New York City except Brooklyn. # Location IDs are listed here: # https://developers.google.com/google-ads/api/reference/data/geotargets # and they can also be retrieved using the GeoTargetConstantService as shown # here: https://developers.google.com/google-ads/api/docs/targeting/location-targeting # # We will add one positive location target for New York City (ID=1023191) # and one negative location target for Brooklyn (ID=1022762). # First, add the positive (negative = False) for New York City. mutate_operation = client.get_type("MutateOperation") campaign_criterion = mutate_operation.campaign_criterion_operation.create campaign_criterion.campaign = campaign_service.campaign_path( customer_id, _PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID ) campaign_criterion.location.geo_target_constant = ( geo_target_constant_service.geo_target_constant_path("1023191") ) campaign_criterion.negative = False operations.append(mutate_operation) # Next add the negative target for Brooklyn. mutate_operation = client.get_type("MutateOperation") campaign_criterion = mutate_operation.campaign_criterion_operation.create campaign_criterion.campaign = campaign_service.campaign_path( customer_id, _PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID ) campaign_criterion.location.geo_target_constant = ( geo_target_constant_service.geo_target_constant_path("1022762") ) campaign_criterion.negative = True operations.append(mutate_operation) # Set the LANGUAGE campaign criterion. mutate_operation = client.get_type("MutateOperation") campaign_criterion = mutate_operation.campaign_criterion_operation.create campaign_criterion.campaign = campaign_service.campaign_path( customer_id, _PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID ) # Set the language. # For a list of all language codes, see: # https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-7 campaign_criterion.language.language_constant = ( googleads_service.language_constant_path("1000") ) # English operations.append(mutate_operation) return operations def create_multiple_text_assets(client, customer_id, texts): """Creates multiple text assets and returns the list of resource names. Args: client: an initialized GoogleAdsClient instance. customer_id: a client customer ID. texts: a list of strings, each of which will be used to create a text asset. Returns: asset_resource_names: a list of asset resource names. """ # Here again we use the GoogleAdService to create multiple text # assets in a single request. googleads_service = client.get_service("GoogleAdsService") operations = [] for text in texts: mutate_operation = client.get_type("MutateOperation") asset = mutate_operation.asset_operation.create asset.text_asset.text = text operations.append(mutate_operation) # Send the operations in a single Mutate request. response = googleads_service.mutate( customer_id=customer_id, mutate_operations=operations, ) asset_resource_names = [] for result in response.mutate_operation_responses: if result._pb.HasField("asset_result"): asset_resource_names.append(result.asset_result.resource_name) print_response_details(response) return asset_resource_names def create_asset_group_operation( client, customer_id, headline_asset_resource_names, description_asset_resource_names, ): """Creates a list of MutateOperations that create a new asset_group. A temporary ID will be assigned to this asset group 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. headline_asset_resource_names: a list of headline resource names. description_asset_resource_names: a list of description resource names. Returns: MutateOperations that create a new asset group and related assets. """ asset_group_service = client.get_service("AssetGroupService") campaign_service = client.get_service("CampaignService") operations = [] # Create the AssetGroup mutate_operation = client.get_type("MutateOperation") asset_group = mutate_operation.asset_group_operation.create asset_group.name = f"Performance Max asset group #{uuid4()}" asset_group.campaign = campaign_service.campaign_path( customer_id, _PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID ) asset_group.final_urls.append("http://www.example.com") asset_group.final_mobile_urls.append("http://www.example.com") asset_group.status = client.enums.AssetGroupStatusEnum.PAUSED asset_group.resource_name = asset_group_service.asset_group_path( customer_id, _ASSET_GROUP_TEMPORARY_ID, ) operations.append(mutate_operation) # For the list of required assets for a Performance Max campaign, see # https://developers.google.com/google-ads/api/docs/performance-max/assets # An AssetGroup is linked to an Asset by creating a new AssetGroupAsset # and providing: # the resource name of the AssetGroup # the resource name of the Asset # the field_type of the Asset in this AssetGroup. # # To learn more about AssetGroups, see # https://developers.google.com/google-ads/api/docs/performance-max/asset-groups # Link the previously created multiple text assets. # Link the headline assets. for resource_name in headline_asset_resource_names: mutate_operation = client.get_type("MutateOperation") asset_group_asset = mutate_operation.asset_group_asset_operation.create asset_group_asset.field_type = client.enums.AssetFieldTypeEnum.HEADLINE asset_group_asset.asset_group = asset_group_service.asset_group_path( customer_id, _ASSET_GROUP_TEMPORARY_ID, ) asset_group_asset.asset = resource_name operations.append(mutate_operation) # Link the description assets. for resource_name in description_asset_resource_names: mutate_operation = client.get_type("MutateOperation") asset_group_asset = mutate_operation.asset_group_asset_operation.create asset_group_asset.field_type = ( client.enums.AssetFieldTypeEnum.DESCRIPTION ) asset_group_asset.asset_group = asset_group_service.asset_group_path( customer_id, _ASSET_GROUP_TEMPORARY_ID, ) asset_group_asset.asset = resource_name operations.append(mutate_operation) # Create and link the long headline text asset. mutate_operations = create_and_link_text_asset( client, customer_id, "Travel the World", client.enums.AssetFieldTypeEnum.LONG_HEADLINE, ) operations.extend(mutate_operations) # Create and link the business name text asset. mutate_operations = create_and_link_text_asset( client, customer_id, "Interplanetary Cruises", client.enums.AssetFieldTypeEnum.BUSINESS_NAME, ) operations.extend(mutate_operations) # Create and link the image assets. # Create and link the Logo Asset. mutate_operations = create_and_link_image_asset( client, customer_id, "https://gaagl.page.link/bjYi", client.enums.AssetFieldTypeEnum.LOGO, "Marketing Logo", ) operations.extend(mutate_operations) # Create and link the Marketing Image Asset. mutate_operations = create_and_link_image_asset( client, customer_id, "https://gaagl.page.link/Eit5", client.enums.AssetFieldTypeEnum.MARKETING_IMAGE, "Marketing Image", ) operations.extend(mutate_operations) # Create and link the Square Marketing Image Asset. mutate_operations = create_and_link_image_asset( client, customer_id, "https://gaagl.page.link/bjYi", client.enums.AssetFieldTypeEnum.SQUARE_MARKETING_IMAGE, "Square Marketing Image", ) operations.extend(mutate_operations) return operations def create_and_link_text_asset(client, customer_id, text, field_type): """Creates a list of MutateOperations that create a new linked text asset. Args: client: an initialized GoogleAdsClient instance. customer_id: a client customer ID. text: the text of the asset to be created. field_type: the field_type of the new asset in the AssetGroupAsset. Returns: MutateOperations that create a new linked text asset. """ global next_temp_id operations = [] asset_service = client.get_service("AssetService") asset_group_service = client.get_service("AssetGroupService") # Create the Text Asset. mutate_operation = client.get_type("MutateOperation") asset = mutate_operation.asset_operation.create asset.resource_name = asset_service.asset_path(customer_id, next_temp_id) asset.text_asset.text = text operations.append(mutate_operation) # Create an AssetGroupAsset to link the Asset to the AssetGroup. mutate_operation = client.get_type("MutateOperation") asset_group_asset = mutate_operation.asset_group_asset_operation.create asset_group_asset.field_type = field_type asset_group_asset.asset_group = asset_group_service.asset_group_path( customer_id, _ASSET_GROUP_TEMPORARY_ID, ) asset_group_asset.asset = asset_service.asset_path( customer_id, next_temp_id ) operations.append(mutate_operation) next_temp_id -= 1 return operations def create_and_link_image_asset( client, customer_id, url, field_type, asset_name ): """Creates a list of MutateOperations that create a new linked image asset. Args: client: an initialized GoogleAdsClient instance. customer_id: a client customer ID. url: the url of the image to be retrieved and put into an asset. field_type: the field_type of the new asset in the AssetGroupAsset. asset_name: the asset name. Returns: MutateOperations that create a new linked image asset. """ global next_temp_id operations = [] asset_service = client.get_service("AssetService") asset_group_service = client.get_service("AssetGroupService") # Create the Image Asset. mutate_operation = client.get_type("MutateOperation") asset = mutate_operation.asset_operation.create asset.resource_name = asset_service.asset_path(customer_id, next_temp_id) # Provide a unique friendly name to identify your asset. # When there is an existing image asset with the same content but a different # name, the new name will be dropped silently. asset.name = asset_name asset.type_ = client.enums.AssetTypeEnum.IMAGE asset.image_asset.data = get_image_bytes(url) operations.append(mutate_operation) # Create an AssetGroupAsset to link the Asset to the AssetGroup. mutate_operation = client.get_type("MutateOperation") asset_group_asset = mutate_operation.asset_group_asset_operation.create asset_group_asset.field_type = field_type asset_group_asset.asset_group = asset_group_service.asset_group_path( customer_id, _ASSET_GROUP_TEMPORARY_ID, ) asset_group_asset.asset = asset_service.asset_path( customer_id, next_temp_id ) operations.append(mutate_operation) next_temp_id -= 1 return operations def get_image_bytes(url): """Loads image data from a URL. Args: url: a URL str. Returns: Images bytes loaded from the given URL. """ response = requests.get(url) return response.content 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. suffix = "_result" for result in response.mutate_operation_responses: for field_descriptor, value in result._pb.ListFields(): if field_descriptor.name.endswith(suffix): name = field_descriptor.name[: -len(suffix)] else: name = field_descriptor.name print( f"Created a(n) {convert_snake_case_to_upper_case(name)} with " f"{str(value).strip()}." ) def create_asset_group_signal_operations(client, customer_id, audience_id): """Creates a list of MutateOperations that may create asset group signals. Args: client: an initialized GoogleAdsClient instance. customer_id: a client customer ID. audience_id: an optional audience ID. Returns: MutateOperations that create new asset group signals. """ googleads_service = client.get_service("GoogleAdsService") asset_group_resource_name = googleads_service.asset_group_path( customer_id, _ASSET_GROUP_TEMPORARY_ID ) operations = [] if audience_id: # Create an audience asset group signal. # To learn more about Audience Signals, see: # https://developers.google.com/google-ads/api/performance-max/asset-group-signals#audiences mutate_operation = client.get_type("MutateOperation") operation = mutate_operation.asset_group_signal_operation.create operation.asset_group = asset_group_resource_name operation.audience.audience = googleads_service.audience_path( customer_id, audience_id ) operations.append(mutate_operation) # Create a search theme asset group signal. # To learn more about Search Themes Signals, see: # https://developers.google.com/google-ads/api/performance-max/asset-group-signals#search_themes mutate_operation = client.get_type("MutateOperation") operation = mutate_operation.asset_group_signal_operation.create operation.asset_group = asset_group_resource_name operation.search_theme.text = "travel" operations.append(mutate_operation) return operations if __name__ == "__main__": parser = argparse.ArgumentParser( description=("Creates a Performance Max 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( "-a", "--audience_id", type=str, help="The ID of an audience.", ) 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="v18") try: main(googleads_client, args.customer_id, args.audience_id) except GoogleAdsException as ex: print( f'Request with ID "{ex.request_id}" failed with status ' f'"{ex.error.code().name}" and includes the following errors:' ) for error in ex.failure.errors: print(f'Error with message "{error.message}".') if error.location: for field_path_element in error.location.field_path_elements: print(f"\t\tOn field: {field_path_element.field_name}") sys.exit(1)
Ruby
#!/usr/bin/env ruby # Encoding: utf-8 # # Copyright 2021 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # This example shows how to create a Performance Max campaign. # # For more information about Performance Max campaigns, see # https://developers.google.com/google-ads/api/docs/performance-max/overview # # Prerequisites: # - You must have at least one conversion action in the account. For # more about conversion actions, see # https://developers.google.com/google-ads/api/docs/conversions/overview#conversion_actions # # This example uses the default customer conversion goals. For an example # of setting campaign-specific conversion goals, see # shopping_ads/add_performance_max_retail_campaign.rb require 'optparse' require 'date' require 'open-uri' require 'google/ads/google_ads' # We specify temporary IDs that are specific to a single mutate request. # Temporary IDs are always negative and unique within one mutate request. # # See https://developers.google.com/google-ads/api/docs/mutating/best-practices # for further details. # # These temporary IDs are fixed because they are used in multiple places. BUDGET_TEMPORARY_ID = "-1" PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID = "-2" ASSET_GROUP_TEMPORARY_ID = "-3" # There are also entities that will be created in the same request but do not # need to be fixed temporary IDs because they are referenced only once. def next_temp_id @id ||= ASSET_GROUP_TEMPORARY_ID.to_i @id -= 1 end def add_performance_max_campaign(customer_id, audience_id) # GoogleAdsClient will read a config file from # ENV['HOME']/google_ads_config.rb when called without parameters client = Google::Ads::GoogleAds::GoogleAdsClient.new # Performance Max campaigns require that repeated assets such as headlines # and descriptions be created before the campaign. # For the list of required assets for a Performance Max campaign, see # https://developers.google.com/google-ads/api/docs/performance-max/assets # # Create the headlines. headline_asset_resource_names = create_multiple_text_assets( client, customer_id, [ "Travel", "Travel Reviews", "Book travel", ]) # Create the descriptions. description_asset_resource_names = create_multiple_text_assets( client, customer_id, [ "Take to the air!", "Fly to the sky!", ]) # 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 Performance Max # 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, ) performance_max_campaign_operation = create_performance_max_campaign_operation( client, customer_id, ) campaign_criterion_operations = create_campaign_criterion_operations( client, customer_id, ) asset_group_operations = create_asset_group_operation( client, customer_id, headline_asset_resource_names, description_asset_resource_names, ) asset_group_signal_operations = create_asset_group_signal_operations( client, customer_id, audience_id, ) # Send the operations in a single Mutate request. response = client.service.google_ads.mutate( customer_id: customer_id, mutate_operations: [ # It's important to create these entities in this order because # they depend on each other. campaign_budget_operation, performance_max_campaign_operation, # Expand the list of multiple operations into the list of # other mutate operations campaign_criterion_operations, asset_group_operations, asset_group_signal_operations, ].flatten) print_response_details(response) end # 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. def create_campaign_budget_operation(client, customer_id) client.operation.mutate do |m| m.campaign_budget_operation = client.operation.create_resource.campaign_budget do |cb| cb.name = "Performance Max campaign budget #{SecureRandom.uuid}" # The budget period already defaults to DAILY. cb.amount_micros = 50_000_000 cb.delivery_method = :STANDARD # A Performance Max campaign cannot use a shared campaign budget. cb.explicitly_shared = false # Set 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 end # Creates a MutateOperation that creates a new Performance Max 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_performance_max_campaign_operation(client, customer_id) client.operation.mutate do |m| m.campaign_operation = client.operation.create_resource.campaign do |c| c.name = "Performance Max campaign #{SecureRandom.uuid}" # Set the campaign status as PAUSED. The campaign is the only entity in # the mutate request that should have its status set. c.status = :PAUSED # All Performance Max campaigns have an advertising_channel_type of # PERFORMANCE_MAX. The advertising_channel_sub_type should not be set. c.advertising_channel_type = :PERFORMANCE_MAX # Bidding strategy must be set directly on the campaign. # Setting a portfolio bidding strategy by resource name is not supported. # Max Conversion and Maximize Conversion Value are the only strategies # supported for Performance Max campaigns. # An optional ROAS (Return on Advertising Spend) can be set for # maximize_conversion_value. The ROAS value must be specified as a ratio in # the API. It is calculated by dividing "total value" by "total spend". # For more information on Maximize Conversion Value, see the support # article: http://support.google.com/google-ads/answer/7684216. # A target_roas of 3.5 corresponds to a 350% return on ad spend. c.bidding_strategy_type = :MAXIMIZE_CONVERSION_VALUE c.maximize_conversion_value = client.resource.maximize_conversion_value do |mcv| mcv.target_roas = 3.5 end # Set the Final URL expansion opt out. This flag is specific to # Performance Max campaigns. If opted out (true), only the final URLs in # the asset group or URLs specified in the advertiser's Google Merchant # Center or business data feeds are targeted. # If opted in (false), the entire domain will be targeted. For best # results, set this value to false to opt in and allow URL expansions. You # can optionally add exclusions to limit traffic to parts of your website. c.url_expansion_opt_out = false # Assign the resource name with a temporary ID. c.resource_name = client.path.campaign(customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID) # Set the budget using the given budget resource name. c.campaign_budget = client.path.campaign_budget(customer_id, BUDGET_TEMPORARY_ID) # Optional fields c.start_date = DateTime.parse((Date.today + 1).to_s).strftime('%Y%m%d') c.end_date = DateTime.parse(Date.today.next_year.to_s).strftime('%Y%m%d') end end end # Creates a list of MutateOperations that create new campaign criteria. def create_campaign_criterion_operations(client, customer_id) operations = [] # Set the LOCATION campaign criteria. # Target all of New York City except Brooklyn. # Location IDs are listed here: # https://developers.google.com/google-ads/api/reference/data/geotargets # and they can also be retrieved using the GeoTargetConstantService as shown # here: https://developers.google.com/google-ads/api/docs/targeting/location-targeting # # We will add one positive location target for New York City (ID=1023191) # and one negative location target for Brooklyn (ID=1022762). # First, add the positive (negative = false) for New York City. operations << client.operation.mutate do |m| m.campaign_criterion_operation = client.operation.create_resource.campaign_criterion do |cc| cc.campaign = client.path.campaign( customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID) cc.location = client.resource.location_info do |li| li.geo_target_constant = client.path.geo_target_constant("1023191") end cc.negative = false end end # Next add the negative target for Brooklyn. operations << client.operation.mutate do |m| m.campaign_criterion_operation = client.operation.create_resource.campaign_criterion do |cc| cc.campaign = client.path.campaign( customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID) cc.location = client.resource.location_info do |li| li.geo_target_constant = client.path.geo_target_constant("1022762") end cc.negative = true end end # Set the LANGUAGE campaign criterion. operations << client.operation.mutate do |m| m.campaign_criterion_operation = client.operation.create_resource.campaign_criterion do |cc| cc.campaign = client.path.campaign( customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID) # Set the language. # For a list of all language codes, see: # https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-7 cc.language = client.resource.language_info do |li| li.language_constant = client.path.language_constant("1000") # English end end end operations end # Creates multiple text assets and returns the list of resource names. def create_multiple_text_assets(client, customer_id, texts) operations = texts.map do |text| client.operation.mutate do |m| m.asset_operation = client.operation.create_resource.asset do |asset| asset.text_asset = client.resource.text_asset do |text_asset| text_asset.text = text end end end end # Send the operations in a single Mutate request. response = client.service.google_ads.mutate( customer_id: customer_id, mutate_operations: operations, ) asset_resource_names = [] response.mutate_operation_responses.each do |result| if result.asset_result asset_resource_names.append(result.asset_result.resource_name) end end print_response_details(response) asset_resource_names end # Creates a list of MutateOperations that create a new asset_group. # # A temporary ID will be assigned to this asset group so that it can # be referenced by other objects being created in the same Mutate request. def create_asset_group_operation( client, customer_id, headline_asset_resource_names, description_asset_resource_names) operations = [] # Create the AssetGroup operations << client.operation.mutate do |m| m.asset_group_operation = client.operation.create_resource.asset_group do |ag| ag.name = "Performance Max asset group #{SecureRandom.uuid}" ag.campaign = client.path.campaign( customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID) ag.final_urls << "http://www.example.com" ag.final_mobile_urls << "http://www.example.com" ag.status = :PAUSED ag.resource_name = client.path.asset_group( customer_id, ASSET_GROUP_TEMPORARY_ID) end end # For the list of required assets for a Performance Max campaign, see # https://developers.google.com/google-ads/api/docs/performance-max/assets # # An AssetGroup is linked to an Asset by creating a new AssetGroupAsset # and providing: # the resource name of the AssetGroup # the resource name of the Asset # the field_type of the Asset in this AssetGroup. # # To learn more about AssetGroups, see # https://developers.google.com/google-ads/api/docs/performance-max/asset-groups # Link the previously created multiple text assets. # Link the headline assets. headline_asset_resource_names.each do |resource_name| operations << client.operation.mutate do |m| m.asset_group_asset_operation = client.operation.create_resource .asset_group_asset do |aga| aga.field_type = :HEADLINE aga.asset_group = client.path.asset_group( customer_id, ASSET_GROUP_TEMPORARY_ID) aga.asset = resource_name end end end # Link the description assets. description_asset_resource_names.each do |resource_name| operations << client.operation.mutate do |m| m.asset_group_asset_operation = client.operation.create_resource .asset_group_asset do |aga| aga.field_type = :DESCRIPTION aga.asset_group = client.path.asset_group( customer_id, ASSET_GROUP_TEMPORARY_ID) aga.asset = resource_name end end end # Create and link the long headline text asset. operations += create_and_link_text_asset( client, customer_id, "Travel the World", :LONG_HEADLINE) # Create and link the business name text asset. operations += create_and_link_text_asset( client, customer_id, "Interplanetary Cruises", :BUSINESS_NAME) # Create and link the image assets. # Create and link the Logo Asset. operations += create_and_link_image_asset( client, customer_id, "https://gaagl.page.link/bjYi", :LOGO, "Marketing Logo") # Create and link the Marketing Image Asset. operations += create_and_link_image_asset( client, customer_id, "https://gaagl.page.link/Eit5", :MARKETING_IMAGE, "Marketing Image") # Create and link the Square Marketing Image Asset. operations += create_and_link_image_asset( client, customer_id, "https://gaagl.page.link/bjYi", :SQUARE_MARKETING_IMAGE, "Square Marketing Image") operations end # Creates a list of MutateOperations that create a new linked text asset. def create_and_link_text_asset(client, customer_id, text, field_type) operations = [] temp_id = next_temp_id # Create the Text Asset. operations << client.operation.mutate do |m| m.asset_operation = client.operation.create_resource.asset do |a| a.resource_name = client.path.asset(customer_id, temp_id) a.text_asset = client.resource.text_asset do |text_asset| text_asset.text = text end end end # Create an AssetGroupAsset to link the Asset to the AssetGroup. operations << client.operation.mutate do |m| m.asset_group_asset_operation = client.operation.create_resource .asset_group_asset do |aga| aga.field_type = field_type aga.asset_group = client.path.asset_group( customer_id, ASSET_GROUP_TEMPORARY_ID) aga.asset = client.path.asset(customer_id, temp_id) end end operations end # Creates a list of MutateOperations that create a new linked image asset. def create_and_link_image_asset(client, customer_id, url, field_type, asset_name) operations = [] temp_id = next_temp_id # Create the Image Asset. operations << client.operation.mutate do |m| m.asset_operation = client.operation.create_resource.asset do |a| a.resource_name = client.path.asset(customer_id, temp_id) # Provide a unique friendly name to identify your asset. # When there is an existing image asset with the same content but a different # name, the new name will be dropped silently. a.name = asset_name a.type = :IMAGE a.image_asset = client.resource.image_asset do |image_asset| image_asset.data = get_image_bytes(url) end end end # Create an AssetGroupAsset to link the Asset to the AssetGroup. operations << client.operation.mutate do |m| m.asset_group_asset_operation = client.operation.create_resource. asset_group_asset do |aga| aga.field_type = field_type aga.asset_group = client.path.asset_group( customer_id, ASSET_GROUP_TEMPORARY_ID, ) aga.asset = client.path.asset(customer_id, temp_id) end end operations end # Create a list of MutateOperations that create AssetGroupSignals. def create_asset_group_signal_operations(client, customer_id, audience_id) operations = [] return operations if audience_id.nil? operations << client.operation.mutate do |m| m.asset_group_signal_operation = client.operation.create_resource. asset_group_signal do |ags| ags.asset_group = client.path.asset_group( customer_id, ASSET_GROUP_TEMPORARY_ID, ) ags.audience = client.resource.audience_info do |ai| ai.audience = client.path.audience(customer_id, audience_id) end end end operations end # Loads image data from a URL. def get_image_bytes(url) URI.open(url).read end # Prints the details of a MutateGoogleAdsResponse. def print_response_details(response) # Parse the mutate response to print details about the entities that # were created by the request. suffix = "_result" response.mutate_operation_responses.each do |result| result.to_h.select {|k, v| v }.each do |name, value| if name.to_s.end_with?(suffix) name = name.to_s.delete_suffix(suffix) end puts "Created a(n) #{::Google::Ads::GoogleAds::Utils.camelize(name)} " \ "with #{value.to_s.strip}." end end end if __FILE__ == $0 options = {} # The following parameter(s) should be provided to run the example. You can # either specify these by changing the INSERT_XXX_ID_HERE values below, or on # the command line. # # Parameters passed on the command line will override any parameters set in # code. # # Running the example with -h will print the command line usage. options[:customer_id] = 'INSERT_CUSTOMER_ID_HERE' options[:audience_id] = nil 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('-D', '--audience-id AUDIENCE-ID', String, 'Audience ID (optional)') do |v| options[:audience_id] = v end opts.separator '' opts.separator 'Help:' opts.on_tail('-h', '--help', 'Show this message') do puts opts exit end end.parse! begin add_performance_max_campaign( options.fetch(:customer_id).tr("-", ""), options[:audience_id], ) 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 Performance Max campaign. # # For more information about Performance Max campaigns, see # https://developers.google.com/google-ads/api/docs/performance-max/overview. # # Prerequisites: # - You must have at least one conversion action in the account. For # more about conversion actions, see # https://developers.google.com/google-ads/api/docs/conversions/overview#conversion_actions. # # This example uses the default customer conversion goals. For an example of # setting campaign-specific conversion goals, see # shopping_ads/add_performance_max_retail_campaign.pl. 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::MediaUtils; use Google::Ads::GoogleAds::V18::Resources::CampaignBudget; use Google::Ads::GoogleAds::V18::Resources::Campaign; use Google::Ads::GoogleAds::V18::Resources::CampaignCriterion; use Google::Ads::GoogleAds::V18::Resources::Asset; use Google::Ads::GoogleAds::V18::Resources::AssetGroup; use Google::Ads::GoogleAds::V18::Resources::AssetGroupAsset; use Google::Ads::GoogleAds::V18::Resources::AssetGroupSignal; use Google::Ads::GoogleAds::V18::Common::MaximizeConversionValue; use Google::Ads::GoogleAds::V18::Common::LocationInfo; use Google::Ads::GoogleAds::V18::Common::LanguageInfo; use Google::Ads::GoogleAds::V18::Common::TextAsset; use Google::Ads::GoogleAds::V18::Common::ImageAsset; use Google::Ads::GoogleAds::V18::Common::AudienceInfo; use Google::Ads::GoogleAds::V18::Enums::BudgetDeliveryMethodEnum qw(STANDARD); use Google::Ads::GoogleAds::V18::Enums::CampaignStatusEnum; use Google::Ads::GoogleAds::V18::Enums::AdvertisingChannelTypeEnum qw(PERFORMANCE_MAX); use Google::Ads::GoogleAds::V18::Enums::AssetGroupStatusEnum; use Google::Ads::GoogleAds::V18::Enums::AssetFieldTypeEnum qw(HEADLINE DESCRIPTION LONG_HEADLINE BUSINESS_NAME LOGO MARKETING_IMAGE SQUARE_MARKETING_IMAGE); use Google::Ads::GoogleAds::V18::Services::GoogleAdsService::MutateOperation; use Google::Ads::GoogleAds::V18::Services::CampaignBudgetService::CampaignBudgetOperation; use Google::Ads::GoogleAds::V18::Services::CampaignService::CampaignOperation; use Google::Ads::GoogleAds::V18::Services::CampaignCriterionService::CampaignCriterionOperation; use Google::Ads::GoogleAds::V18::Services::AssetService::AssetOperation; use Google::Ads::GoogleAds::V18::Services::AssetGroupService::AssetGroupOperation; use Google::Ads::GoogleAds::V18::Services::AssetGroupAssetService::AssetGroupAssetOperation; use Google::Ads::GoogleAds::V18::Services::AssetGroupSignalService::AssetGroupSignalOperation; use Google::Ads::GoogleAds::V18::Utils::ResourceNames; use Getopt::Long qw(:config auto_help); use Pod::Usage; use Cwd qw(abs_path); use Data::Uniqid qw(uniqid); use POSIX qw(strftime); # We specify temporary IDs that are specific to a single mutate request. # Temporary IDs are always negative and unique within one mutate request. # # See https://developers.google.com/google-ads/api/docs/mutating/best-practices # for further details. # # These temporary IDs are fixed because they are used in multiple places. use constant BUDGET_TEMPORARY_ID => -1; use constant PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID => -2; use constant ASSET_GROUP_TEMPORARY_ID => -3; # There are also entities that will be created in the same request but do not # need to be fixed temporary IDs because they are referenced only once. our $next_temp_id = ASSET_GROUP_TEMPORARY_ID - 1; # The following parameter(s) should be provided to run the example. You can # either specify these by changing the INSERT_XXX_ID_HERE values below, or on # the command line. # # Parameters passed on the command line will override any parameters set in # code. # # Running the example with -h will print the command line usage. my $customer_id = "INSERT_CUSTOMER_ID_HERE"; my $audience_id = undef; sub add_performance_max_campaign { my ($api_client, $customer_id, $audience_id) = @_; # Performance Max campaigns require that repeated assets such as headlines # and descriptions be created before the campaign. # For the list of required assets for a Performance Max campaign, see # https://developers.google.com/google-ads/api/docs/performance-max/assets. # # Create the headlines. my $headline_asset_resource_names = create_multiple_text_assets($api_client, $customer_id, ["Travel", "Travel Reviews", "Book travel"]); # Create the descriptions. my $description_asset_resource_names = create_multiple_text_assets($api_client, $customer_id, ["Take to the air!", "Fly to the sky!"]); # It's important to create the below entities in this order because they depend # on each other. my $operations = []; # 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 Performance Max 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. push @$operations, create_campaign_budget_operation($customer_id); push @$operations, create_performance_max_campaign_operation($customer_id); push @$operations, @{create_campaign_criterion_operations($customer_id)}; push @$operations, @{ create_asset_group_operations( $customer_id, $headline_asset_resource_names, $description_asset_resource_names )}; push @$operations, @{create_asset_group_signal_operations($customer_id, $audience_id)}; # Issue a mutate request to create everything and print its information. my $mutate_google_ads_response = $api_client->GoogleAdsService()->mutate({ customerId => $customer_id, mutateOperations => $operations }); print_response_details($mutate_google_ads_response); return 1; } # Creates a MutateOperation that creates a new CampaignBudget. # # A temporary ID will be assigned to this campaign budget so that it can be # referenced by other objects being created in the same mutate request. sub create_campaign_budget_operation { my ($customer_id) = @_; # Create a mutate operation that creates a campaign budget operation. return Google::Ads::GoogleAds::V18::Services::GoogleAdsService::MutateOperation-> new({ campaignBudgetOperation => Google::Ads::GoogleAds::V18::Services::CampaignBudgetService::CampaignBudgetOperation ->new({ create => Google::Ads::GoogleAds::V18::Resources::CampaignBudget->new( { # Set a temporary ID in the budget's resource name so it can be # referenced by the campaign in later steps. resourceName => Google::Ads::GoogleAds::V18::Utils::ResourceNames::campaign_budget( $customer_id, BUDGET_TEMPORARY_ID ), name => "Performance Max campaign budget #" . uniqid(), # The budget period already defaults to DAILY. amountMicros => 50000000, deliveryMethod => STANDARD, # A Performance Max campaign cannot use a shared campaign budget. explicitlyShared => "false", })})}); } # Creates a MutateOperation that creates a new Performance Max campaign. # # A temporary ID will be assigned to this campaign so that it can be referenced # by other objects being created in the same mutate request. sub create_performance_max_campaign_operation { my ($customer_id) = @_; # Create a mutate operation that creates a campaign operation. return Google::Ads::GoogleAds::V18::Services::GoogleAdsService::MutateOperation-> new({ campaignOperation => Google::Ads::GoogleAds::V18::Services::CampaignService::CampaignOperation ->new({ create => Google::Ads::GoogleAds::V18::Resources::Campaign->new({ # Assign the resource name with a temporary ID. resourceName => Google::Ads::GoogleAds::V18::Utils::ResourceNames::campaign( $customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID ), name => "Performance Max campaign #" . uniqid(), # Set the budget using the given budget resource name. campaignBudget => Google::Ads::GoogleAds::V18::Utils::ResourceNames::campaign_budget( $customer_id, BUDGET_TEMPORARY_ID ), # Set the campaign status as PAUSED. The campaign is the only entity in # the mutate request that should have its status set. status => Google::Ads::GoogleAds::V18::Enums::CampaignStatusEnum::PAUSED, # All Performance Max campaigns have an advertisingChannelType of # PERFORMANCE_MAX. The advertisingChannelSubType should not be set. advertisingChannelType => PERFORMANCE_MAX, # Bidding strategy must be set directly on the campaign. # Setting a portfolio bidding strategy by resource name is not supported. # Max Conversion and Maximize Conversion Value are the only strategies # supported for Performance Max campaigns. # An optional ROAS (Return on Advertising Spend) can be set for # maximizeConversionValue. The ROAS value must be specified as a ratio in # the API. It is calculated by dividing "total value" by "total spend". # For more information on Maximize Conversion Value, see the support # article: http://support.google.com/google-ads/answer/7684216. # A targetRoas of 3.5 corresponds to a 350% return on ad spend. maximizeConversionValue => Google::Ads::GoogleAds::V18::Common::MaximizeConversionValue-> new({ targetRoas => 3.5 } ), # Set the final URL expansion opt out. This flag is specific to # Performance Max campaigns. If opted out (true), only the final URLs in # the asset group or URLs specified in the advertiser's Google Merchant # Center or business data feeds are targeted. # If opted in (false), the entire domain will be targeted. For best # results, set this value to false to opt in and allow URL expansions. You # can optionally add exclusions to limit traffic to parts of your website. urlExpansionOptOut => "false", # Optional fields. startDate => strftime("%Y%m%d", localtime(time + 60 * 60 * 24)), endDate => strftime("%Y%m%d", localtime(time + 60 * 60 * 24 * 365)), })})}); } # Creates a list of MutateOperations that create new campaign criteria. sub create_campaign_criterion_operations { my ($customer_id) = @_; my $operations = []; # Set the LOCATION campaign criteria. # Target all of New York City except Brooklyn. # Location IDs are listed here: # https://developers.google.com/google-ads/api/reference/data/geotargets # and they can also be retrieved using the GeoTargetConstantService as shown # here: https://developers.google.com/google-ads/api/docs/targeting/location-targeting. # # We will add one positive location target for New York City (ID=1023191) # and one negative location target for Brooklyn (ID=1022762). # First, add the positive (negative = false) for New York City. push @$operations, Google::Ads::GoogleAds::V18::Services::GoogleAdsService::MutateOperation-> new({ campaignCriterionOperation => Google::Ads::GoogleAds::V18::Services::CampaignCriterionService::CampaignCriterionOperation ->new({ create => Google::Ads::GoogleAds::V18::Resources::CampaignCriterion->new({ campaign => Google::Ads::GoogleAds::V18::Utils::ResourceNames::campaign( $customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID ), location => Google::Ads::GoogleAds::V18::Common::LocationInfo->new({ geoTargetConstant => Google::Ads::GoogleAds::V18::Utils::ResourceNames::geo_target_constant( 1023191)} ), negative => "false" })})}); # Next add the negative target for Brooklyn. push @$operations, Google::Ads::GoogleAds::V18::Services::GoogleAdsService::MutateOperation-> new({ campaignCriterionOperation => Google::Ads::GoogleAds::V18::Services::CampaignCriterionService::CampaignCriterionOperation ->new({ create => Google::Ads::GoogleAds::V18::Resources::CampaignCriterion->new({ campaign => Google::Ads::GoogleAds::V18::Utils::ResourceNames::campaign( $customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID ), location => Google::Ads::GoogleAds::V18::Common::LocationInfo->new({ geoTargetConstant => Google::Ads::GoogleAds::V18::Utils::ResourceNames::geo_target_constant( 1022762)} ), negative => "true" })})}); # Set the LANGUAGE campaign criterion. push @$operations, Google::Ads::GoogleAds::V18::Services::GoogleAdsService::MutateOperation-> new({ campaignCriterionOperation => Google::Ads::GoogleAds::V18::Services::CampaignCriterionService::CampaignCriterionOperation ->new({ create => Google::Ads::GoogleAds::V18::Resources::CampaignCriterion->new({ campaign => Google::Ads::GoogleAds::V18::Utils::ResourceNames::campaign( $customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID ), # Set the language. # For a list of all language codes, see: # https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-7. language => Google::Ads::GoogleAds::V18::Common::LanguageInfo->new({ languageConstant => Google::Ads::GoogleAds::V18::Utils::ResourceNames::language_constant( 1000) # English })})})}); return $operations; } # Creates multiple text assets and returns the list of resource names. sub create_multiple_text_assets { my ($api_client, $customer_id, $texts) = @_; # Here again we use the GoogleAdService to create multiple text assets in a # single request. my $operations = []; foreach my $text (@$texts) { # Create a mutate operation for a text asset. push @$operations, Google::Ads::GoogleAds::V18::Services::GoogleAdsService::MutateOperation ->new({ assetOperation => Google::Ads::GoogleAds::V18::Services::AssetService::AssetOperation-> new({ create => Google::Ads::GoogleAds::V18::Resources::Asset->new({ textAsset => Google::Ads::GoogleAds::V18::Common::TextAsset->new({ text => $text })})})}); } # Issue a mutate request to add all assets. my $mutate_google_ads_response = $api_client->GoogleAdsService()->mutate({ customerId => $customer_id, mutateOperations => $operations }); my $asset_resource_names = []; foreach my $response (@{$mutate_google_ads_response->{mutateOperationResponses}}) { push @$asset_resource_names, $response->{assetResult}{resourceName}; } print_response_details($mutate_google_ads_response); return $asset_resource_names; } # Creates a list of MutateOperations that create a new asset group. # # A temporary ID will be assigned to this asset group so that it can be referenced # by other objects being created in the same mutate request. sub create_asset_group_operations { my ( $customer_id, $headline_asset_resource_names, $description_asset_resource_names ) = @_; my $operations = []; # Create a mutate operation that creates an asset group operation. push @$operations, Google::Ads::GoogleAds::V18::Services::GoogleAdsService::MutateOperation-> new({ assetGroupOperation => Google::Ads::GoogleAds::V18::Services::AssetGroupService::AssetGroupOperation ->new({ create => Google::Ads::GoogleAds::V18::Resources::AssetGroup->new({ resourceName => Google::Ads::GoogleAds::V18::Utils::ResourceNames::asset_group( $customer_id, ASSET_GROUP_TEMPORARY_ID ), name => "Performance Max asset group #" . uniqid(), campaign => Google::Ads::GoogleAds::V18::Utils::ResourceNames::campaign( $customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID ), finalUrls => ["http://www.example.com"], finalMobileUrls => ["http://www.example.com"], status => Google::Ads::GoogleAds::V18::Enums::AssetGroupStatusEnum::PAUSED })})}); # For the list of required assets for a Performance Max campaign, see # https://developers.google.com/google-ads/api/docs/performance-max/assets. # An AssetGroup is linked to an Asset by creating a new AssetGroupAsset # and providing: # - the resource name of the AssetGroup # - the resource name of the Asset # - the fieldType of the Asset in this AssetGroup # # To learn more about AssetGroups, see # https://developers.google.com/google-ads/api/docs/performance-max/asset-groups. # Link the previously created multiple text assets. # Link the headline assets. foreach my $resource_name (@$headline_asset_resource_names) { push @$operations, Google::Ads::GoogleAds::V18::Services::GoogleAdsService::MutateOperation ->new({ assetGroupAssetOperation => Google::Ads::GoogleAds::V18::Services::AssetGroupAssetService::AssetGroupAssetOperation ->new({ create => Google::Ads::GoogleAds::V18::Resources::AssetGroupAsset->new({ asset => $resource_name, assetGroup => Google::Ads::GoogleAds::V18::Utils::ResourceNames::asset_group( $customer_id, ASSET_GROUP_TEMPORARY_ID ), fieldType => HEADLINE })})}); } # Link the description assets. foreach my $resource_name (@$description_asset_resource_names) { push @$operations, Google::Ads::GoogleAds::V18::Services::GoogleAdsService::MutateOperation ->new({ assetGroupAssetOperation => Google::Ads::GoogleAds::V18::Services::AssetGroupAssetService::AssetGroupAssetOperation ->new({ create => Google::Ads::GoogleAds::V18::Resources::AssetGroupAsset->new({ asset => $resource_name, assetGroup => Google::Ads::GoogleAds::V18::Utils::ResourceNames::asset_group( $customer_id, ASSET_GROUP_TEMPORARY_ID ), fieldType => DESCRIPTION })})}); } # Create and link the long headline text asset. push @$operations, @{create_and_link_text_asset($customer_id, "Travel the World", LONG_HEADLINE)}; # Create and link the business name text asset. push @$operations, @{ create_and_link_text_asset($customer_id, "Interplanetary Cruises", BUSINESS_NAME)}; # Create and link the image assets. # Create and link the logo asset. push @$operations, @{ create_and_link_image_asset($customer_id, "https://gaagl.page.link/bjYi", LOGO, "Marketing Logo")}; # Create and link the marketing image asset. push @$operations, @{ create_and_link_image_asset( $customer_id, "https://gaagl.page.link/Eit5", MARKETING_IMAGE, "Marketing Image" )}; # Create and link the square marketing image asset. push @$operations, @{ create_and_link_image_asset( $customer_id, "https://gaagl.page.link/bjYi", SQUARE_MARKETING_IMAGE, "Square Marketing Image" )}; return $operations; } # Creates a list of MutateOperations that create a new linked text asset. sub create_and_link_text_asset { my ($customer_id, $text, $field_type) = @_; my $operations = []; # Create a new mutate operation for a text asset. push @$operations, Google::Ads::GoogleAds::V18::Services::GoogleAdsService::MutateOperation-> new({ assetOperation => Google::Ads::GoogleAds::V18::Services::AssetService::AssetOperation-> new({ create => Google::Ads::GoogleAds::V18::Resources::Asset->new({ resourceName => Google::Ads::GoogleAds::V18::Utils::ResourceNames::asset( $customer_id, $next_temp_id ), textAsset => Google::Ads::GoogleAds::V18::Common::TextAsset->new({ text => $text })})})}); # Create an asset group asset to link the asset to the asset group. push @$operations, Google::Ads::GoogleAds::V18::Services::GoogleAdsService::MutateOperation-> new({ assetGroupAssetOperation => Google::Ads::GoogleAds::V18::Services::AssetGroupAssetService::AssetGroupAssetOperation ->new({ create => Google::Ads::GoogleAds::V18::Resources::AssetGroupAsset->new({ asset => Google::Ads::GoogleAds::V18::Utils::ResourceNames::asset( $customer_id, $next_temp_id ), assetGroup => Google::Ads::GoogleAds::V18::Utils::ResourceNames::asset_group( $customer_id, ASSET_GROUP_TEMPORARY_ID ), fieldType => $field_type })})}); $next_temp_id--; return $operations; } # Creates a list of MutateOperations that create a new linked image asset. sub create_and_link_image_asset { my ($customer_id, $url, $field_type, $asset_name) = @_; my $operations = []; # Create a new mutate operation for an image asset. push @$operations, Google::Ads::GoogleAds::V18::Services::GoogleAdsService::MutateOperation-> new({ assetOperation => Google::Ads::GoogleAds::V18::Services::AssetService::AssetOperation-> new({ create => Google::Ads::GoogleAds::V18::Resources::Asset->new({ resourceName => Google::Ads::GoogleAds::V18::Utils::ResourceNames::asset( $customer_id, $next_temp_id ), # Provide a unique friendly name to identify your asset. # When there is an existing image asset with the same content but a different # name, the new name will be dropped silently. name => $asset_name, imageAsset => Google::Ads::GoogleAds::V18::Common::ImageAsset->new({ data => get_base64_data_from_url($url)})})})}); # Create an asset group asset to link the asset to the asset group. push @$operations, Google::Ads::GoogleAds::V18::Services::GoogleAdsService::MutateOperation-> new({ assetGroupAssetOperation => Google::Ads::GoogleAds::V18::Services::AssetGroupAssetService::AssetGroupAssetOperation ->new({ create => Google::Ads::GoogleAds::V18::Resources::AssetGroupAsset->new({ asset => Google::Ads::GoogleAds::V18::Utils::ResourceNames::asset( $customer_id, $next_temp_id ), assetGroup => Google::Ads::GoogleAds::V18::Utils::ResourceNames::asset_group( $customer_id, ASSET_GROUP_TEMPORARY_ID ), fieldType => $field_type })})}); $next_temp_id--; return $operations; } # Creates a list of MutateOperations that create asset group signals. sub create_asset_group_signal_operations { my ($customer_id, $audience_id) = @_; my $operations = []; return $operations if not defined $audience_id; push @$operations, Google::Ads::GoogleAds::V18::Services::GoogleAdsService::MutateOperation-> new({ assetGroupSignalOperation => Google::Ads::GoogleAds::V18::Services::AssetGroupSignalService::AssetGroupSignalOperation ->new({ # To learn more about Audience Signals, see: # https://developers.google.com/google-ads/api/docs/performance-max/asset-groups#audience_signals create => Google::Ads::GoogleAds::V18::Resources::AssetGroupSignal->new({ assetGroup => Google::Ads::GoogleAds::V18::Utils::ResourceNames::asset_group( $customer_id, ASSET_GROUP_TEMPORARY_ID ), audience => Google::Ads::GoogleAds::V18::Common::AudienceInfo->new({ audience => Google::Ads::GoogleAds::V18::Utils::ResourceNames::audience( $customer_id, $audience_id )})})})}); return $operations; } # 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. sub print_response_details { my ($mutate_google_ads_response) = @_; foreach my $response (@{$mutate_google_ads_response->{mutateOperationResponses}}) { my $result_type = [keys %$response]->[0]; printf "Created a(n) %s with '%s'.\n", ucfirst $result_type =~ s/Result$//r, $response->{$result_type}{resourceName}; } } # Don't run the example if the file is being included. if (abs_path($0) ne abs_path(__FILE__)) { return 1; } # Get Google Ads Client, credentials will be read from ~/googleads.properties. my $api_client = Google::Ads::GoogleAds::Client->new(); # By default examples are set to die on any server returned fault. $api_client->set_die_on_faults(1); # Parameters passed on the command line will override any parameters set in code. GetOptions( "customer_id=s" => \$customer_id, "audience_id=i" => \$audience_id ); # Print the help message if the parameters are not initialized in the code nor # in the command line. pod2usage(2) if not check_params($customer_id); # Call the example. add_performance_max_campaign($api_client, $customer_id =~ s/-//gr, $audience_id); =pod =head1 NAME add_performance_max_campaign =head1 DESCRIPTION This example shows how to create a Performance Max campaign. For more information about Performance Max campaigns, see https://developers.google.com/google-ads/api/docs/performance-max/overview. Prerequisites: - You must have at least one conversion action in the account. For more about conversion actions, see https://developers.google.com/google-ads/api/docs/conversions/overview#conversion_actions. This example uses the default customer conversion goals. For an example of setting campaign-specific conversion goals, see shopping_ads/add_performance_max_retail_campaign.pl. =head1 SYNOPSIS add_performance_max_campaign.pl [options] -help Show the help message. -customer_id The Google Ads customer ID. -audience_id [optional] An audience ID to use to improve the targeting of the Performance Max campaign. =cut