Suivez ces bonnes pratiques pour optimiser les performances, gérer les dépendances entre les opérations et traiter les réponses lorsque vous modifiez des ressources dans l'API Google Ads.
Noms de ressources temporaires
GoogleAdsService.Mutate et BatchJobService acceptent les noms de ressources temporaires qui peuvent être référencés dans les opérations ultérieures. Cela vous permet de créer une campagne et ses groupes d'annonces, annonces et mots clés associés dans une seule requête de modification ou un seul job par lot.
Pour référencer une ressource nouvellement créée dans la même requête de modification ou le même job par lot, spécifiez un ID entier négatif (tel que -1 ou -2, à l'exclusion de 0) dans le champ resource_name de la nouvelle ressource. Par exemple, lorsque vous créez une campagne dans une requête par lot, définissez son nom de ressource sur customers/CUSTOMER_ID/campaigns/-1.
Lorsque vous créez un groupe d'annonces dans une opération ultérieure de la même requête, référencez customers/CUSTOMER_ID/campaigns/-1 comme campagne parente. L'API remplace automatiquement -1 par l'ID de campagne réel généré lors de la création.
Contraintes d'utilisation
Tenez compte des règles suivantes lorsque vous utilisez des noms de ressources temporaires :
- L'ordre est important : vous ne pouvez faire référence à un nom de ressource temporaire qu'après l'avoir défini. Dans une liste d'opérations, l'opération dépendante (par exemple, la création d'un groupe d'annonces) doit apparaître après l'opération qui crée sa ressource parente (par exemple, la création d'une campagne).
- Portée d'une requête unique ou d'un job par lot : les noms de ressources temporaires ne sont pas conservés entre les jobs distincts ni les requêtes de mutation. Pour référencer une ressource créée dans une tâche ou une requête de modification précédente, utilisez son nom de ressource réel généré par le système.
- Unicité globale : dans une même requête de job ou de mutation, chaque nom de ressource temporaire doit utiliser un entier négatif unique pour tous les types de ressources.
Par exemple, vous ne pouvez pas attribuer
-1à la fois à une campagne et à un groupe d'annonces dans la même requête. Si vous réutilisez un ID temporaire dans la même requête ou le même job par lot, une erreurNewResourceCreationError.DUPLICATE_TEMP_IDSs'affiche.
Exemple de charge utile
Supposons que vous souhaitiez ajouter une campagne, un groupe d'annonces et une annonce dans une seule requête API ou un seul job par lot. Vous pouvez structurer le tableau mutateOperations dans une charge utile de requête GoogleAdsService.Mutate ou BatchJobService.AddBatchJobOperations, comme illustré dans l'exemple JSON REST suivant (avec d'autres champs de ressources requis omis par souci de concision) :
{
"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"
}
}
}
]
}
Cet exemple illustre les principaux détails suivants :
- Le groupe d'annonces utilise un nouvel ID temporaire (
-2), car-1est déjà attribué à la campagne. - Le groupe d'annonces fait référence à
customers/CUSTOMER_ID/campaigns/-1pour se lier à la campagne créée lors de l'opération précédente. adGroupAdOperationfait référence àcustomers/CUSTOMER_ID/adGroups/-2et ometresourceName, car aucune opération ultérieure dans la requête ne fait référence à la nouvelle annonce.
Regrouper les opérations de même type
Lorsque vous utilisez GoogleAdsService.Mutate, regroupez les opérations par type de ressource dans le tableau mutate_operations répété tout en respectant les dépendances parent-enfant. Cette méthode lit les opérations de manière séquentielle jusqu'à ce qu'elle rencontre un type de ressource différent, puis regroupe toutes les opérations contiguës du même type dans une seule requête de service de backend.
Par exemple, si vous incluez cinq opérations de campagne suivies de dix opérations de groupe d'annonces dans le champ mutate_operations répété, le système effectue deux appels de backend : un à CampaignService pour les cinq opérations de campagne et un second à AdGroupService pour les dix opérations de groupe d'annonces.
En revanche, l'entrelacement des opérations en les ordonnant comme [campaign, ad group,
campaign, ad group] entraîne quatre appels de backend distincts. Les appels entrelacés dégradent les performances de l'API et peuvent entraîner des délais d'expiration des requêtes sur les grands lots.
Gérer les échecs partiels et les limites de lot
Par défaut, GoogleAdsService.Mutate annule l'intégralité de la requête si une seule opération échoue. Pour valider les opérations valides même lorsque d'autres opérations de la même requête échouent, définissez partial_failure sur true dans la requête et inspectez partial_failure_error dans la réponse. Lorsque partial_failure est défini sur true, si une opération parente qui définit un ID temporaire (comme customers/CUSTOMER_ID/campaigns/-1) échoue à la validation, toutes les opérations enfants dépendantes qui font référence à cet ID temporaire dans la même requête échouent également avec NewResourceCreationError.TEMP_ID_RESOURCE_HAD_ERRORS. Pour en savoir plus, consultez le guide sur les échecs partiels.
Tenez également compte de la taille des requêtes, des sous-lots et des limites de débit :
- Limites de taille des requêtes et des blocs : une seule requête
GoogleAdsService.Mutateimpose une limite de 10 000 opérations de mutation (ou jusqu'à 20 000 lorsque toutes les opérations de la requête sont desAdGroupCriterionOperation, renvoyantRequestError.TOO_MANY_MUTATE_OPERATIONSen cas de dépassement) et d'au plus 100 opérations d'action (RequestError.TOO_MANY_ACTION_OPERATIONS).BatchJobService.AddBatchJobOperationsimpose une limite maximale de 10 000 opérations par appel, de 10 484 504 octets parMutateOperationet de 41 937 920 octets parAddBatchJobOperationsRequest(renvoyantBatchJobError.REQUEST_TOO_LARGEen cas de dépassement de l'une de ces limites, avec un maximum de 1 000 000 d'opérations au total par job par lot). Les mutations simultanées ciblant la même campagne ou le même compte peuvent déclencher des erreursDatabaseError.CONCURRENT_MODIFICATION. BatchJobServiceSous-batchs atomiques : bien que les jobs par lot s'exécutent avec une sémantique d'échec partiel (par défaut,1 000 opérations par sous-batch interne),BatchJobServiceregroupe automatiquement certaines opérations dépendantes contiguës pour le même ID parent dans des sous-batchs atomiques :- Un
AssetGroupOperation(create) suivi d'opérationsAssetGroupAssetOperation(create) contiguës pour le même IDAssetGroup(jusqu'à 1 000 opérations au total, avec échec atomiqueBatchJobError.ASSET_GROUP_AND_ASSET_GROUP_ASSET_TRANSACTION_FAILURE; chaqueAssetGroupOperationupdateouremoves'exécute dans un sous-lot d'opération unique autonome). - Une opération
CampaignOperationPerformance Max (create, lorsque les consignes relatives à la marque sont activées, ce qui est le cas par défaut, sauf sibrand_guidelines_enabledest défini surfalseou sihotel_property_asset_setest défini) suivie d'opérationsCampaignAssetOperation(create) contiguës pour le même IDCampaign(jusqu'à 1 000 opérations au total, avec échec atomique etBatchJobError.CAMPAIGN_AND_CAMPAIGN_ASSET_TRANSACTION_FAILURE). - Opérations
AssetGroupListingGroupFilterOperation(max 10,000, échec atomique avecBatchJobError.ASSET_GROUP_LISTING_GROUP_FILTER_TRANSACTION_FAILURE) ouAdGroupCriterionOperation(listing_group,max 20,000, échec atomique avecCriterionError.LISTING_GROUP_ERROR_IN_ANOTHER_OPERATION) consécutives pour le même parent (AssetGroupouAdGroup).
- Un
Récupérer les attributs modifiables à partir de la réponse
Si vous définissez le response_content_type de votre requête de modification sur MUTABLE_RESOURCE, la réponse contient le resource_name et l'objet de ressource renseigné avec ses champs mutable (ainsi que les champs clés renseignés par le système sur la ressource renvoyée, tels que ExperimentArm.in_design_campaigns) pour chaque objet compatible créé ou mis à jour (et non supprimé) par la requête. Pour les opérations remove ou pour les types de ressources qui ne sont pas compatibles avec le renvoi de MUTABLE_RESOURCE, la réponse ne renvoie toujours que resource_name. Utilisez cette fonctionnalité pour éviter d'envoyer une requête Search ou SearchStream supplémentaire après chaque appel de mutation.
Si vous ne définissez pas response_content_type, l'API Google Ads utilise par défaut RESOURCE_NAME_ONLY et ne renvoie que le resource_name de chaque ressource modifiée.
L'exemple suivant montre comment récupérer une ressource mutable à partir d'un appel 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.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]; }