Siga estas práticas recomendadas para otimizar a performance, gerenciar dependências entre operações e processar respostas ao fazer mutações de recursos na API Google Ads.
Nomes de recursos temporários
GoogleAdsService.Mutate e
BatchJobService são compatíveis com 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 trabalho
em lote, especifique um ID 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 principal. A API
substitui automaticamente -1 pelo ID da campanha real gerado na criação.
Restrições de uso
Considere as seguintes regras ao usar nomes de recursos temporários:
- A ordem é importante:só é possível fazer referência a um nome de recurso temporário depois de defini-lo. Em uma lista de operações, a operação dependente (como criar um grupo de anúncios) precisa aparecer depois da operação que cria o recurso principal (como criar uma campanha).
- Escopo de solicitação única ou job em lote:os nomes de recursos temporários não persistem em jobs separados ou solicitações de mutação. Para fazer referência a um recurso criado em um job ou solicitação de mutação anterior, use o nome de recurso real gerado pelo sistema.
- Unicidade 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. Reutilizar um ID temporário na mesma solicitação ou trabalho 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. Você pode 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 brevidade):
{
"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
de pai 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 por dez operações de grupo de anúncios no campo repetido mutate_operations, o sistema fará duas chamadas de back-end: uma para CampaignService nas cinco operações de campanha e outra para AdGroupService nas dez operações de grupo de anúncios.
Em contraste, intercalar operações ordenando-as como [campaign, ad group,
campaign, ad group] resulta em quatro chamadas de back-end separadas. Chamadas intercaladas
reduzem a performance da API e podem causar tempos limite de solicitação em grandes lotes.
Processar falhas parciais e limites de lote
Por padrão, o GoogleAdsService.Mutate reverte toda a
solicitação se uma única 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 principal 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 vão falhar com NewResourceCreationError.TEMP_ID_RESOURCE_HAD_ERRORS. Para mais detalhes, consulte o guia de falha parcial.
Também considere o tamanho da solicitação, o subagrupamento em lotes e os limites de taxa:
- Limites de tamanho de solicitação e bloco:uma única solicitação
GoogleAdsService.Mutateimpõ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 algum limite for excedido, com até 1.000.000 de operações no total por job em lote). Mutações simultâneas que segmentam a mesma campanha ou conta podem acionar errosDatabaseError.CONCURRENT_MODIFICATION. - Sublotes atômicos
BatchJobService:embora os jobs em lote sejam executados com semântica de falha parcial (padrão de 1.000 operações por sublote interno), oBatchJobServiceagrupa automaticamente determinadas operações contíguas dependentes do mesmo ID principal em sublotes atômicos:- Um
AssetGroupOperation(create) seguido por operações contíguas deAssetGroupAssetOperation(create) para o mesmo ID deAssetGroup(até 1.000 operações no total, falhando atomicamente comBatchJobError.ASSET_GROUP_AND_ASSET_GROUP_ASSET_TRANSACTION_FAILURE; cadaAssetGroupOperationupdateouremoveé executado em um sublote independente de operação única). - Uma campanha Performance Max
CampaignOperation(create, quando as diretrizes de marca estão ativadas, o que é o padrão, a menos quebrand_guidelines_enabledesteja definido comofalseouhotel_property_asset_setesteja definido) seguida por 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 de
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 elemento pai (AssetGroupouAdGroup).
- Um
Recuperar atributos mutáveis da resposta
Se você definir o response_content_type da sua 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 (além
dos 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 Search ou SearchStream adicional após cada chamada de mutação.
Se você não definir response_content_type, a API Google Ads vai usar RESOURCE_NAME_ONLY por padrão e retornar apenas o resource_name de cada recurso mutado.
O exemplo a seguir mostra como extrair 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]; }