Segui queste best practice per ottimizzare il rendimento, gestire le dipendenze tra le operazioni e gestire le risposte durante la modifica delle risorse nell'API Google Ads.
Nomi delle risorse temporanee
Sia GoogleAdsService.Mutate che BatchJobService supportano nomi di 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 associati in un'unica richiesta di modifica o un unico 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 nome della 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 nomi di 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 di una singola richiesta o di un job batch: i nomi delle risorse temporanee non vengono mantenuti tra job o richieste di modifica separati. Per fare riferimento a una risorsa creata in un job o una richiesta di modifica precedente, utilizza il nome della risorsa 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 numero 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 un'unica richiesta API o un unico job batch. Puoi strutturare l'array mutateOperations in un payload della richiesta GoogleAdsService.Mutate o BatchJobService.AddBatchJobOperations, come mostrato nell'esempio JSON REST seguente (con altri campi delle 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 in sequenza le operazioni finché non incontra un tipo di risorsa diverso, quindi raggruppa tutte le operazioni contigue dello stesso tipo in un'unica richiesta di servizio di backend.
Ad esempio, se includi 5 operazioni della campagna seguite da 10 operazioni del gruppo di annunci nel campo ripetuto mutate_operations, il sistema esegue due chiamate di backend: una a CampaignService per le 5 operazioni della campagna e una seconda a AdGroupService per le 10 operazioni del gruppo di annunci.
Al contrario, l'interfoliazione delle operazioni ordinandole come [campaign, ad group,
campaign, ad group] genera quattro chiamate separate al backend. Le chiamate interleaved peggiorano le prestazioni dell'API e possono causare timeout delle richieste su 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 nella richiesta e controlla 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 secondarie dipendenti che fanno riferimento a quell'ID temporaneo nella stessa richiesta non superano la convalida con NewResourceCreationError.TEMP_ID_RESOURCE_HAD_ERRORS. Per maggiori dettagli, consulta la guida agli errori parziali.
Tieni inoltre presente le dimensioni delle richieste, il sub-batching e i limiti di frequenza:
- Limiti di dimensioni delle richieste e dei blocchi: una singola richiesta
GoogleAdsService.Mutateapplica un limite di 10.000 operazioni di modifica (o fino a 20.000 quando tutte le operazioni nella richiesta sonoAdGroupCriterionOperation, restituendoRequestError.TOO_MANY_MUTATE_OPERATIONSse superato) e un massimo di 100 operazioni di azione (RequestError.TOO_MANY_ACTION_OPERATIONS).BatchJobService.AddBatchJobOperationsapplica un massimo di 10.000 operazioni per chiamata, 10.484.504 byte per singolaMutateOperatione 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 mutazioni simultanee che hanno come target la stessa campagna o lo stesso account possono attivare erroriDatabaseError.CONCURRENT_MODIFICATION. - Suddivisione in batch secondari atomici di
BatchJobService: sebbene i job batch vengano eseguiti con semantica di errore parziale (per impostazione predefinita,1000 operazioni per batch secondario interno),BatchJobServiceraggruppa automaticamente determinate operazioni dipendenti contigue per lo stesso ID principale in batch secondari atomici:- Un'operazione
AssetGroupOperation(create) seguita da operazioniAssetGroupAssetOperation(create) contigue per lo stesso IDAssetGroup(fino a un totale di 1000 operazioni, con errore atomico conBatchJobError.ASSET_GROUP_AND_ASSET_GROUP_ASSET_TRANSACTION_FAILURE; ogniAssetGroupOperationupdateoremoveviene eseguita in un sub-batch autonomo a singola operazione). - Una campagna 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 in totale, con errore atomico conBatchJobError.CAMPAIGN_AND_CAMPAIGN_ASSET_TRANSACTION_FAILURE). - Operazioni consecutive
AssetGroupListingGroupFilterOperation(max 10,000, che non vanno a buon fine in modo atomico conBatchJobError.ASSET_GROUP_LISTING_GROUP_FILTER_TRANSACTION_FAILURE) oAdGroupCriterionOperation(listing_group,max 20,000, che non vanno a buon fine in modo atomico conCriterionError.LISTING_GROUP_ERROR_IN_ANOTHER_OPERATION) per lo stesso elemento principale (AssetGroupoAdGroup).
- Un'operazione
Recupera gli attributi modificabili dalla risposta
Se imposti response_content_type della richiesta di modifica su MUTABLE_RESOURCE, la risposta contiene resource_name e l'oggetto della 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 una richiesta Search o SearchStream aggiuntiva 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 il resource_name di ogni risorsa
modificata.
L'esempio seguente mostra come recuperare una risorsa modificabile da una chiamata mutate:
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.V26.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| # In standard campaign experiments, creating the treatment arm automatically # generates a draft campaign 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 second, it will be the second 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::V26::Services::ExperimentArmService::ExperimentArmOperation ->new({ create => Google::Ads::GoogleAds::V26::Resources::ExperimentArm->new({ # The "control" arm references an already-existing campaign. control => "true", campaigns => [ Google::Ads::GoogleAds::V26::Utils::ResourceNames::campaign( $customer_id, $base_campaign_id ) ], experiment => $experiment, name => "control arm", trafficSplit => 40 })}); push @$operations, Google::Ads::GoogleAds::V26::Services::ExperimentArmService::ExperimentArmOperation ->new({ create => Google::Ads::GoogleAds::V26::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]; }