Per ottimizzare il rendimento, gestire le dipendenze tra le operazioni e gestire le risposte quando modifichi le risorse nell'API Google Ads, attieniti alle seguenti best practice.
Nomi delle risorse temporanei
Sia GoogleAdsService.Mutate sia
BatchJobService supportano i nomi delle risorse temporanei a cui è possibile
fare riferimento nelle operazioni successive. In questo modo, puoi creare una campagna e i relativi gruppi di annunci, annunci e parole chiave in una singola richiesta di modifica o in un singolo job batch.
Per fare riferimento a una risorsa appena creata all'interno della stessa richiesta di modifica o dello stesso job batch, specifica un ID intero negativo (ad esempio -1 o -2, escluso 0) nel campo resource_name della nuova risorsa. Ad esempio, quando crei una campagna in una richiesta batch, imposta il relativo nome risorsa su customers/CUSTOMER_ID/campaigns/-1.
Quando crei un gruppo di annunci in un'operazione successiva all'interno della stessa richiesta, fai riferimento a customers/CUSTOMER_ID/campaigns/-1 come campagna principale. L'API sostituisce automaticamente -1 con l'ID campagna effettivo generato al momento della creazione.
Vincoli di utilizzo
Quando utilizzi i nomi delle risorse temporanei, tieni presente le seguenti regole:
- L'ordine è importante: puoi fare riferimento a un nome di risorsa temporaneo solo dopo averlo definito. In un elenco di operazioni, l'operazione dipendente (ad esempio la creazione di un gruppo di annunci) deve essere visualizzata dopo l'operazione che crea la risorsa principale (ad esempio la creazione di una campagna).
- Ambito della richiesta singola o del job batch: i nomi delle risorse temporanei non vengono mantenuti tra job o richieste di modifica separati. Per fare riferimento a una risorsa creata in un job o in una richiesta di modifica precedente, utilizza il relativo nome risorsa effettivo generato dal sistema.
- Unicità globale: all'interno di un singolo job o di una singola richiesta di modifica, ogni nome di risorsa temporaneo deve utilizzare un intero negativo univoco per tutti i tipi di risorse.
Ad esempio, non puoi assegnare
-1sia a una campagna sia a un gruppo di annunci nella stessa richiesta. Il riutilizzo di un ID temporaneo all'interno della stessa richiesta o dello stesso job batch restituisce un erroreNewResourceCreationError.DUPLICATE_TEMP_IDS.
Esempio di payload
Supponiamo che tu voglia aggiungere una campagna, un gruppo di annunci e un annuncio in una singola richiesta API o in un singolo job batch. Puoi strutturare l'array mutateOperations in un payload di richiesta GoogleAdsService.Mutate o BatchJobService.AddBatchJobOperations come mostrato nell'esempio JSON REST seguente (con altri campi di risorse obbligatori omessi per brevità):
{
"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"
}
}
}
]
}
Questo esempio mostra i seguenti dettagli chiave:
- Il gruppo di annunci utilizza un nuovo ID temporaneo (
-2) perché-1è già assegnato alla campagna. - Il gruppo di annunci fa riferimento a
customers/CUSTOMER_ID/campaigns/-1per collegarsi alla campagna creata nell'operazione precedente. adGroupAdOperationfa riferimento acustomers/CUSTOMER_ID/adGroups/-2e ometteresourceNameperché nessuna operazione successiva nella richiesta fa riferimento al nuovo annuncio.
Raggruppa le operazioni dello stesso tipo
Quando utilizzi GoogleAdsService.Mutate, raggruppa le operazioni per
tipo di risorsa nell'array mutate_operations ripetuto rispettando le dipendenze padre
e figlio. Questo metodo legge le operazioni in sequenza finché non rileva un tipo di risorsa diverso, quindi raggruppa tutte le operazioni contigue dello stesso tipo in una singola richiesta di servizio di backend.
Ad esempio, se includi 5 operazioni di campagna seguite da 10 operazioni di gruppo di annunci nel campo mutate_operations ripetuto, il sistema esegue due chiamate backend: una a CampaignService per le 5 operazioni di campagna e una seconda a AdGroupService per le 10 operazioni di gruppo di annunci.
Al contrario, l'interleaving delle operazioni ordinandole come [campaign, ad group,
campaign, ad group] genera quattro chiamate backend separate. Le chiamate interleaved peggiorano il rendimento dell'API e possono causare timeout delle richieste nei batch di grandi dimensioni.
Gestisci gli errori parziali e i limiti dei batch
Per impostazione predefinita, GoogleAdsService.Mutate esegue il rollback dell'intera
richiesta se una singola operazione non va a buon fine. Per eseguire il commit delle operazioni valide anche quando
altre operazioni nella stessa richiesta non vanno a buon fine, imposta partial_failure su true su
la richiesta ed esamina partial_failure_error nella risposta. Quando partial_failure è true, se un'operazione principale che definisce un ID temporaneo (ad esempio customers/CUSTOMER_ID/campaigns/-1) non supera la convalida, anche tutte le operazioni figlio dipendenti che fanno riferimento a quell'ID temporaneo nella stessa richiesta non vanno a buon fine con NewResourceCreationError.TEMP_ID_RESOURCE_HAD_ERRORS. Per maggiori dettagli,
consulta la guida agli errori parziali.
Tieni presente anche le dimensioni delle richieste, la suddivisione in batch e i limiti di frequenza:
- Limiti delle dimensioni delle richieste e dei blocchi: una singola
GoogleAdsService.Mutaterichiesta applica un limite di 10.000 operazioni di modifica (o fino a 20.000 se tutte le operazioni nella richiesta sonoAdGroupCriterionOperations, restituendoRequestError.TOO_MANY_MUTATE_OPERATIONSse viene superato) e al massimo 100 operazioni di azione (RequestError.TOO_MANY_ACTION_OPERATIONS).BatchJobService.AddBatchJobOperationsapplica un massimo di 10.000 operazioni per chiamata, 10.484.504 byte per ogniMutateOperation, e 41.937.920 byte perAddBatchJobOperationsRequest(restituendoBatchJobError.REQUEST_TOO_LARGEse viene superato un limite, con un massimo di 1.000.000 di operazioni totali per job batch). Le modifiche simultanee che hanno come target la stessa campagna o lo stesso account possono attivare erroriDatabaseError.CONCURRENT_MODIFICATION. BatchJobServicesuddivisione in batch atomici: sebbene i job batch vengano eseguiti con semantica di errore parziale (per impostazione predefinita 1000 operazioni per batch secondario interno),BatchJobServiceraggruppa automaticamente alcune operazioni dipendenti contigue per lo stesso ID principale in batch secondari atomici:- Un'operazione
AssetGroupOperation(create) seguita da operazioniAssetGroupAssetOperation(create) contigue per lo stessoAssetGroupID (fino a 1000 operazioni totali, con errore atomico conBatchJobError.ASSET_GROUP_AND_ASSET_GROUP_ASSET_TRANSACTION_FAILURE; ogniAssetGroupOperationupdateoremoveviene eseguita in un batch secondario autonomo a operazione singola). - Un'operazione Performance Max
CampaignOperation(create, quando le linee guida per il brand sono attivate,ovvero l'impostazione predefinita a meno chebrand_guidelines_enablednon sia impostato sufalseohotel_property_asset_setnon sia impostato) seguita da operazioniCampaignAssetOperation(create) contigue per lo stesso IDCampaign(fino a 1000 operazioni totali, con errore atomico conBatchJobError.CAMPAIGN_AND_CAMPAIGN_ASSET_TRANSACTION_FAILURE). - Operazioni
AssetGroupListingGroupFilterOperation(max 10,000, con errore atomico conBatchJobError.ASSET_GROUP_LISTING_GROUP_FILTER_TRANSACTION_FAILURE) oAdGroupCriterionOperation(listing_group,max 20,000, con errore atomico conCriterionError.LISTING_GROUP_ERROR_IN_ANOTHER_OPERATION) consecutive per lo stesso elemento principale (AssetGroupoAdGroup).
- Un'operazione
Recupera gli attributi modificabili dalla risposta
Se imposti il response_content_type della richiesta di modifica su
MUTABLE_RESOURCE, la risposta contiene il
resource_name e l'oggetto risorsa compilato con i relativi campi modificabili (nonché i campi chiave compilati dal sistema nella risorsa restituita, ad esempio
ExperimentArm.in_design_campaigns) per ogni oggetto supportato creato o
aggiornato (non rimosso) dalla richiesta. Per le operazioni remove o per i tipi di risorse che non supportano la restituzione di MUTABLE_RESOURCE, la risposta restituisce sempre solo resource_name. Utilizza questa funzionalità per evitare di inviare un
ulteriore Search o SearchStream richiesta dopo ogni chiamata di modifica.
Se non imposti response_content_type, l'API Google Ads utilizza per impostazione predefinita RESOURCE_NAME_ONLY e restituisce solo resource_name di ogni risorsa modificata.
L'esempio seguente mostra come recuperare una risorsa modificabile da una chiamata di modifica:
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]; }