Siga estas práticas recomendadas para otimizar a performance, gerenciar dependências entre operações e processar respostas ao modificar recursos na API Google Ads.
Nomes de recursos temporários
Os métodos GoogleAdsService.Mutate e
BatchJobService aceitam nomes de recursos temporários que podem
ser referenciados em operações subsequentes. Isso permite criar uma campanha e os grupos de anúncios, anúncios e palavras-chave associados em uma única solicitação de mutação ou job em lote.
Para referenciar um recurso recém-criado na mesma solicitação de mutação ou job em lote, especifique um ID de número inteiro negativo (como -1 ou -2, excluindo 0) no campo resource_name do novo recurso. Por exemplo, ao criar uma campanha em uma solicitação em lote, defina o nome do recurso como customers/CUSTOMER_ID/campaigns/-1.
Ao criar um grupo de anúncios em uma operação posterior na mesma solicitação, faça referência a customers/CUSTOMER_ID/campaigns/-1 como a campanha mãe. A API substitui automaticamente -1 pelo ID da campanha real gerado após a criação.
Restrições de uso
Considere as seguintes regras ao usar nomes de recursos temporários:
- A ordem é importante:só é possível referenciar um nome de recurso temporário depois de defini-lo. Em uma lista de operações, a operação dependente (como a criação de um grupo de anúncios) precisa aparecer após a operação que cria o recurso mãe (como a criação de uma campanha).
- Escopo de solicitação única ou job em lote:os nomes de recursos temporários não persistem em jobs ou solicitações de mutação separados. Para referenciar um recurso criado em um job ou solicitação de mutação anterior, use o nome de recurso real gerado pelo sistema.
- Exclusividade global:em um único job ou solicitação de mutação, cada nome de recurso temporário precisa usar um número inteiro negativo exclusivo em todos os tipos de recursos.
Por exemplo, não é possível atribuir
-1a uma campanha e a um grupo de anúncios na mesma solicitação. A reutilização de um ID temporário na mesma solicitação ou job em lote retorna um erroNewResourceCreationError.DUPLICATE_TEMP_IDS.
Exemplo de payload
Suponha que você queira adicionar uma campanha, um grupo de anúncios e um anúncio em uma única solicitação de API ou job em lote. É possível estruturar a matriz mutateOperations em um payload de solicitação GoogleAdsService.Mutate ou BatchJobService.AddBatchJobOperations, conforme mostrado no exemplo JSON REST a seguir (com outros campos de recursos obrigatórios omitidos para maior clareza):
{
"mutateOperations": [
{
"campaignOperation": {
"create": {
"resourceName": "customers/CUSTOMER_ID/campaigns/-1"
}
}
},
{
"adGroupOperation": {
"create": {
"resourceName": "customers/CUSTOMER_ID/adGroups/-2",
"campaign": "customers/CUSTOMER_ID/campaigns/-1"
}
}
},
{
"adGroupAdOperation": {
"create": {
"adGroup": "customers/CUSTOMER_ID/adGroups/-2"
}
}
}
]
}
Este exemplo demonstra os seguintes detalhes principais:
- O grupo de anúncios usa um novo ID temporário (
-2) porque-1já está atribuído à campanha. - O grupo de anúncios faz referência a
customers/CUSTOMER_ID/campaigns/-1para se vincular à campanha criada na operação anterior. - O
adGroupAdOperationfaz referência acustomers/CUSTOMER_ID/adGroups/-2e omiteresourceNameporque nenhuma operação subsequente na solicitação faz referência ao novo anúncio.
Agrupar operações do mesmo tipo
Ao usar GoogleAdsService.Mutate, agrupe as operações por
tipo de recurso na matriz mutate_operations repetida, respeitando as dependências mãe
e filho. Esse método lê as operações sequencialmente até encontrar um tipo de recurso diferente e, em seguida, agrupa todas as operações contíguas do mesmo tipo em uma única solicitação de serviço de back-end.
Por exemplo, se você incluir cinco operações de campanha seguidas de dez operações de grupo de anúncios no campo mutate_operations repetido, o sistema vai realizar duas chamadas de back-end: uma para CampaignService para as cinco operações de campanha e uma segunda para AdGroupService para as dez operações de grupo de anúncios.
Por outro lado, a intercalação de operações, ordenando-as como [campaign, ad group,
campaign, ad group], resulta em quatro chamadas de back-end separadas. As chamadas intercaladas degradam a performance da API e podem levar a tempos limite de solicitação em lotes grandes.
Processar falhas parciais e limites de lote
Por padrão, GoogleAdsService.Mutate reverte toda a
solicitação se qualquer operação falhar. Para confirmar operações válidas mesmo quando
outras operações na mesma solicitação falham, defina partial_failure como true na
solicitação e inspecione partial_failure_error na resposta. Quando partial_failure é true, se uma operação mãe que define um ID temporário (como customers/CUSTOMER_ID/campaigns/-1) falhar na validação, todas as operações filhas dependentes que fazem referência a esse ID temporário na mesma solicitação também falham com NewResourceCreationError.TEMP_ID_RESOURCE_HAD_ERRORS. Para mais detalhes,
consulte o guia de falhas parciais.
Considere também o tamanho da solicitação, o sub-lote e os limites de limitação de taxa:
- Limites de tamanho de solicitação e de bloco:uma única
GoogleAdsService.Mutatesolicitação impõe um limite de 10.000 operações de mutação (ou até 20.000 quando todas as operações na solicitação sãoAdGroupCriterionOperations, retornandoRequestError.TOO_MANY_MUTATE_OPERATIONSse excedido) e no máximo 100 operações de ação (RequestError.TOO_MANY_ACTION_OPERATIONS).BatchJobService.AddBatchJobOperationsimpõe um máximo de 10.000 operações por chamada, 10.484.504 bytes porMutateOperationindividual e 41.937.920 bytes porAddBatchJobOperationsRequest(retornandoBatchJobError.REQUEST_TOO_LARGEse qualquer limite for excedido, com até 1.000.000 de operações no total por job em lote). Mutações simultâneas direcionadas à mesma campanha ou conta podem acionar errosDatabaseError.CONCURRENT_MODIFICATION. BatchJobServicesub-lote atômico: embora os jobs em lote sejam executados em semântica de falha parcial (padrão de 1.000 operações por sub-lote interno),BatchJobServiceagrupa automaticamente determinadas operações dependentes contíguas para o mesmo ID mãe em sub-lotes atômicos:- Uma
AssetGroupOperation(create) seguida de operaçõesAssetGroupAssetOperation(create) contíguas para o mesmoAssetGroupID (até 1.000 operações no total, falhando atomicamente comBatchJobError.ASSET_GROUP_AND_ASSET_GROUP_ASSET_TRANSACTION_FAILURE; cadaAssetGroupOperationupdateouremoveé executada em um sub-lote de operação única independente). - Uma
CampaignOperationdo Performance Max (create, quando as diretrizes de marca estão ativadas, que é o padrão, a menos quebrand_guidelines_enabledesteja definido comofalseouhotel_property_asset_setesteja definido) seguida de operaçõesCampaignAssetOperation(create) contíguas para o mesmo IDCampaign(até 1.000 operações no total, falhando atomicamente comBatchJobError.CAMPAIGN_AND_CAMPAIGN_ASSET_TRANSACTION_FAILURE). - Operações consecutivas
AssetGroupListingGroupFilterOperation(max 10,000, falhando atomicamente comBatchJobError.ASSET_GROUP_LISTING_GROUP_FILTER_TRANSACTION_FAILURE) ouAdGroupCriterionOperation(listing_group,max 20,000, falhando atomicamente comCriterionError.LISTING_GROUP_ERROR_IN_ANOTHER_OPERATION) para o mesmo pai (AssetGroupouAdGroup).
- Uma
Recuperar atributos mutáveis da resposta
Se você definir o response_content_type da solicitação de mutação como
MUTABLE_RESOURCE, a resposta vai conter o
resource_name e o objeto de recurso preenchido com os campos mutáveis (bem
como os campos principais preenchidos pelo sistema no recurso retornado, como
ExperimentArm.in_design_campaigns) para cada objeto compatível criado ou
atualizado (não removido) pela solicitação. Para operações remove ou para tipos de recursos que não aceitam o retorno de MUTABLE_RESOURCE, a resposta sempre retorna apenas o resource_name. Use esse recurso para evitar o envio de uma solicitação
adicional Search ou SearchStream após cada chamada de mutação.
Se você não definir response_content_type, a API Google Ads vai usar RESOURCE_NAME_ONLY como padrão e retornar apenas o resource_name de cada recurso modificado.
O exemplo a seguir demonstra como recuperar um recurso mutável de uma chamada de mutação:
Java
private String createExperimentArms( GoogleAdsClient googleAdsClient, long customerId, long campaignId, String experiment) { List<ExperimentArmOperation> operations = new ArrayList<>(); operations.add( ExperimentArmOperation.newBuilder() .setCreate( // The "control" arm references an already-existing campaign. ExperimentArm.newBuilder() .setControl(true) .addCampaigns(ResourceNames.campaign(customerId, campaignId)) .setExperiment(experiment) .setName("control arm") .setTrafficSplit(40) .build()) .build()); operations.add( ExperimentArmOperation.newBuilder() .setCreate( // In standard campaign experiments, creating the treatment arm automatically // generates a draft campaign that you can modify before starting the experiment. ExperimentArm.newBuilder() .setControl(false) .setExperiment(experiment) .setName("experiment arm") .setTrafficSplit(60) .build()) .build()); try (ExperimentArmServiceClient experimentArmServiceClient = googleAdsClient.getLatestVersion().createExperimentArmServiceClient()) { // Constructs the mutate request. MutateExperimentArmsRequest mutateRequest = MutateExperimentArmsRequest.newBuilder() .setCustomerId(Long.toString(customerId)) .addAllOperations(operations) // We want to fetch the draft campaign IDs from the treatment arm, so the easiest way // to do that is to have the response return the newly created entities. .setResponseContentType(ResponseContentType.MUTABLE_RESOURCE) .build(); // Sends the mutate request. MutateExperimentArmsResponse response = experimentArmServiceClient.mutateExperimentArms(mutateRequest); // Results always return in the order that you specify them in the request. Since we created // the treatment arm last, it will be the last result. If you don't remember which arm is the // treatment arm, you can always filter the query in the next section with // `experiment_arm.control = false`. MutateExperimentArmResult controlArmResult = response.getResults(0); MutateExperimentArmResult treatmentArmResult = response.getResults(response.getResultsCount() - 1); System.out.printf( "Created control arm with resource name '%s'%n", controlArmResult.getResourceName()); System.out.printf( "Created treatment arm with resource name '%s'%n", treatmentArmResult.getResourceName()); return treatmentArmResult.getExperimentArm().getInDesignCampaigns(0); } }
C#
private static (MutateExperimentArmResult, MutateExperimentArmResult) CreateExperimentArms(GoogleAdsClient client, long customerId, long baseCampaignId, string experimentResourceName) { // Get the ExperimentArmService. ExperimentArmServiceClient experimentService = client.GetService( Services.V25.ExperimentArmService); // Create the control arm. The control arm references an already-existing campaign. ExperimentArmOperation controlArmOperation = new ExperimentArmOperation() { Create = new ExperimentArm() { Control = true, Campaigns = { ResourceNames.Campaign(customerId, baseCampaignId) }, Experiment = experimentResourceName, Name = "Control Arm", TrafficSplit = 40 } }; // Create the non-control arm. // In standard campaign experiments, creating the treatment arm automatically // generates a draft campaign that you can modify before starting the experiment. ExperimentArmOperation treatmentArmOperation = new ExperimentArmOperation() { Create = new ExperimentArm() { Control = false, Experiment = experimentResourceName, Name = "Experiment Arm", TrafficSplit = 60 } }; // We want to fetch the draft campaign IDs from the treatment arm, so the // easiest way to do that is to have the response return the newly created // entities. MutateExperimentArmsRequest request = new MutateExperimentArmsRequest { CustomerId = customerId.ToString(), Operations = { controlArmOperation, treatmentArmOperation }, ResponseContentType = ResponseContentType.MutableResource }; MutateExperimentArmsResponse response = experimentService.MutateExperimentArms( request ); // Results always return in the order that you specify them in the request. // Since we created the treatment arm last, it will be the last result. MutateExperimentArmResult controlArm = response.Results.First(); MutateExperimentArmResult treatmentArm = response.Results.Last(); Console.WriteLine($"Created control arm with resource name " + $"'{controlArm.ResourceName}'."); Console.WriteLine($"Created treatment arm with resource name" + $" '{treatmentArm.ResourceName}'."); return (controlArm, treatmentArm); }
PHP
private static function createExperimentArms( GoogleAdsClient $googleAdsClient, int $customerId, int $campaignId, string $experimentResourceName ): string { $operations = []; $experimentArm1 = new ExperimentArm( [ // The "control" arm references an already-existing campaign. 'control' => true, 'campaigns' => [ResourceNames::forCampaign($customerId, $campaignId)], 'experiment' => $experimentResourceName, 'name' => 'control arm', 'traffic_split' => 40 ] ); $operations[] = new ExperimentArmOperation(['create' => $experimentArm1]); $experimentArm2 = new ExperimentArm( [ // The non-"control" arm, also called a "treatment" arm, will automatically // generate draft campaigns that you can modify before starting the // experiment. 'control' => false, 'experiment' => $experimentResourceName, 'name' => 'experiment arm', 'traffic_split' => 60 ] ); $operations[] = new ExperimentArmOperation(['create' => $experimentArm2]); // Issues a request to create the experiment arms. $experimentArmServiceClient = $googleAdsClient->getExperimentArmServiceClient(); $response = $experimentArmServiceClient->mutateExperimentArms( MutateExperimentArmsRequest::build($customerId, $operations) // We want to fetch the draft campaign IDs from the treatment arm, so the easiest // way to do that is to have the response return the newly created entities. ->setResponseContentType(ResponseContentType::MUTABLE_RESOURCE) ); // Results always return in the order that you specify them in the request. // Since we created the treatment arm last, it will be the last result. $controlArmResourceName = $response->getResults()[0]->getResourceName(); $treatmentArm = $response->getResults()[count($operations) - 1]; print "Created control arm with resource name '$controlArmResourceName'" . PHP_EOL; print "Created treatment arm with resource name '{$treatmentArm->getResourceName()}'" . PHP_EOL; return $treatmentArm->getExperimentArm()->getInDesignCampaigns()[0]; }
Python
def create_experiment_arms( client: GoogleAdsClient, customer_id: str, base_campaign_id: str, experiment: str, ) -> str: """Creates a control and treatment experiment arms. Args: client: an initialized GoogleAdsClient instance. customer_id: a client customer ID. base_campaign_id: the campaign ID to associate with the control arm of the experiment. experiment: the resource name for an experiment. Returns: the resource name for the new treatment experiment arm. """ operations: List[ExperimentArmOperation] = [] campaign_service: CampaignServiceClient = client.get_service( "CampaignService" ) # The "control" arm references an already-existing campaign. operation_1: ExperimentArmOperation = client.get_type( "ExperimentArmOperation" ) exa_1: ExperimentArm = operation_1.create exa_1.control = True exa_1.campaigns.append( campaign_service.campaign_path(customer_id, base_campaign_id) ) exa_1.experiment = experiment exa_1.name = "control arm" exa_1.traffic_split = 40 operations.append(operation_1) # In standard campaign experiments, creating the treatment arm automatically # generates a draft campaign that you can modify before starting the experiment. operation_2: ExperimentArmOperation = client.get_type( "ExperimentArmOperation" ) exa_2: ExperimentArm = operation_2.create exa_2.control = False exa_2.experiment = experiment exa_2.name = "experiment arm" exa_2.traffic_split = 60 operations.append(operation_2) experiment_arm_service: ExperimentArmServiceClient = client.get_service( "ExperimentArmService" ) request: MutateExperimentArmsRequest = client.get_type( "MutateExperimentArmsRequest" ) request.customer_id = customer_id request.operations = operations # We want to fetch the draft campaign IDs from the treatment arm, so the # easiest way to do that is to have the response return the newly created # entities. request.response_content_type = ( client.enums.ResponseContentTypeEnum.MUTABLE_RESOURCE ) response: MutateExperimentArmsResponse = ( experiment_arm_service.mutate_experiment_arms(request=request) ) # Results always return in the order that you specify them in the request. # Since we created the treatment arm second, it will be the second result. control_arm_result: Any = response.results[0] treatment_arm_result: Any = response.results[1] print( f"Created control arm with resource name {control_arm_result.resource_name}" ) print( f"Created treatment arm with resource name {treatment_arm_result.resource_name}" ) return treatment_arm_result.experiment_arm.in_design_campaigns[0]
Ruby
def create_experiment_arms(client, customer_id, base_campaign_id, experiment) operations = [] operations << client.operation.create_resource.experiment_arm do |ea| # The "control" arm references an already-existing campaign. ea.control = true ea.campaigns << client.path.campaign(customer_id, base_campaign_id) ea.experiment = experiment ea.name = 'control arm' ea.traffic_split = 40 end operations << client.operation.create_resource.experiment_arm do |ea| # The non-"control" arm, also called a "treatment" arm, will automatically # generate draft campaigns that you can modify before starting the # experiment. ea.control = false ea.experiment = experiment ea.name = 'experiment arm' ea.traffic_split = 60 end response = client.service.experiment_arm.mutate_experiment_arms( customer_id: customer_id, operations: operations, # We want to fetch the draft campaign IDs from the treatment arm, so the # easiest way to do that is to have the response return the newly created # entities. response_content_type: :MUTABLE_RESOURCE, ) # Results always return in the order that you specify them in the request. # Since we created the treatment arm last, it will be the last result. control_arm_result = response.results.first treatment_arm_result = response.results.last puts "Created control arm with resource name #{control_arm_result.resource_name}." puts "Created treatment arm with resource name #{treatment_arm_result.resource_name}." treatment_arm_result.experiment_arm.in_design_campaigns.first end
Perl
sub create_experiment_arms { my ($api_client, $customer_id, $base_campaign_id, $experiment) = @_; my $operations = []; push @$operations, Google::Ads::GoogleAds::V25::Services::ExperimentArmService::ExperimentArmOperation ->new({ create => Google::Ads::GoogleAds::V25::Resources::ExperimentArm->new({ # The "control" arm references an already-existing campaign. control => "true", campaigns => [ Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign( $customer_id, $base_campaign_id ) ], experiment => $experiment, name => "control arm", trafficSplit => 40 })}); push @$operations, Google::Ads::GoogleAds::V25::Services::ExperimentArmService::ExperimentArmOperation ->new({ create => Google::Ads::GoogleAds::V25::Resources::ExperimentArm->new({ # The non-"control" arm, also called a "treatment" arm, will automatically # generate draft campaigns that you can modify before starting the # experiment. control => "false", experiment => $experiment, name => "experiment arm", trafficSplit => 60 })}); my $response = $api_client->ExperimentArmService()->mutate({ customerId => $customer_id, operations => $operations, # We want to fetch the draft campaign IDs from the treatment arm, so the # easiest way to do that is to have the response return the newly created # entities. responseContentType => MUTABLE_RESOURCE }); # Results always return in the order that you specify them in the request. # Since we created the treatment arm last, it will be the last result. my $control_arm_result = $response->{results}[0]; my $treatment_arm_result = $response->{results}[1]; printf "Created control arm with resource name '%s'.\n", $control_arm_result->{resourceName}; printf "Created treatment arm with resource name '%s'.\n", $treatment_arm_result->{resourceName}; return $treatment_arm_result->{experimentArm}{inDesignCampaigns}[0]; }