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 sont compatibles avec les noms de ressources temporaires qui peuvent
être référencés dans les opérations suivantes. Cela vous permet de créer une campagne, ainsi que les groupes d'annonces, les annonces et les 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 la même tâche 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 référencer 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).
- Champ d'application d'une requête unique ou d'un job par lot : les noms de ressources temporaires ne persistent pas entre les tâches distinctes ni les requêtes de modification. 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 seule tâche ou requête de modification, 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. La réutilisation d'un ID temporaire dans la même requête ou la même tâche par lot renvoie une erreurNewResourceCreationError.DUPLICATE_TEMP_IDS.
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 (les autres champs de ressources obligatoires sont 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 référence
customers/CUSTOMER_ID/campaigns/-1pour se lier à la campagne créée lors de l'opération précédente. adGroupAdOperationréférencecustomers/CUSTOMER_ID/adGroups/-2et ometresourceName, car aucune opération ultérieure de la requête ne référence la nouvelle annonce.
Regrouper les opérations du 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
et 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 10 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 10 opérations de groupe d'annonces.
En revanche, l'entrelacement des opérations en les ordonnant comme suit : [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'attente des requêtes sur les lots volumineux.
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 (tel que customers/CUSTOMER_ID/campaigns/-1) échoue à la validation, toutes les opérations enfants dépendantes qui référencent 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, du sous-traitement par lot et des limites de débit :
- Limites de taille des requêtes et des blocs : une seule
GoogleAdsService.Mutaterequête applique une limite de 10 000 opérations de mutation (ou jusqu'à 20 000 lorsque toutes les opérations de la requête sont desAdGroupCriterionOperations, renvoyantRequestError.TOO_MANY_MUTATE_OPERATIONSsi cette limite est dépassée) et au maximum 100 opérations d'action (RequestError.TOO_MANY_ACTION_OPERATIONS).BatchJobService.AddBatchJobOperationsapplique un maximum de 10 000 opérations par appel, 10 484 504 octets parMutateOperationindividuelle et 41 937 920 octets parAddBatchJobOperationsRequest(renvoyantBatchJobError.REQUEST_TOO_LARGEsi une limite est dépassée, 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-traitement atomique par lot : bien que les jobs par lot s'exécutent selon une sémantique d'échec partiel (par défaut,1 000 opérations par sous-lot interne),BatchJobServiceregroupe automatiquement certaines opérations dépendantes contiguës pour le même ID parent dans des sous-lots atomiques :- Une
AssetGroupOperation(create) suivie d'opérationsAssetGroupAssetOperation(create) contiguës pour le mêmeAssetGroupID (jusqu'à 1 000 opérations au total, échec atomique avecBatchJobError.ASSET_GROUP_AND_ASSET_GROUP_ASSET_TRANSACTION_FAILURE; chaqueAssetGroupOperationupdateouremoves'exécute dans un sous-lot autonome à une seule opération). - Une
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, échec atomique avecBatchJobError.CAMPAIGN_AND_CAMPAIGN_ASSET_TRANSACTION_FAILURE). - Opérations
AssetGroupListingGroupFilterOperationconsécutives (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) pour le même parent (AssetGroupouAdGroup).
- Une
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 rempli avec ses modifiables champs (ainsi
que les champs clés remplis 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 le 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 mutée.
L'exemple suivant montre comment récupérer une ressource modifiable à partir d'un appel de mutation :
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]; }