Google Ads API でリソースを変更する際に、パフォーマンスを最適化し、オペレーション間の依存関係を管理し、レスポンスを処理するためのベスト プラクティスを以下に示します。
一時的なリソース名
Both GoogleAdsService.Mutate and
BatchJobService はどちらも、後続のオペレーションで参照できる一時的なリソース名をサポートしています。これにより、1
つの変更リクエストまたはバッチジョブで、キャンペーンとそれに関連する広告グループ、広告、キーワードを作成できます。
同じ変更リクエストまたはバッチジョブ内で新しく作成されたリソースを参照するには、新しいリソースの resource_name フィールドに負の整数 ID(0 を除く -1 や -2 など)を指定します。たとえば、バッチリクエストでキャンペーンを作成する場合は、リソース名を
customers/CUSTOMER_ID/campaigns/-1
に設定します。
同じリクエスト内の後続のオペレーションで広告グループを作成する場合は、親キャンペーンとして
customers/CUSTOMER_ID/campaigns/-1 を参照します。API は、作成時に生成された実際のキャンペーン
ID で -1 を自動的に置き換えます。
使用上の制約
一時的なリソース名を使用する場合は、次のルールに注意してください。
- 順序が重要: 一時的なリソース名は、定義した後にのみ参照できます。オペレーションのリストでは、依存オペレーション(広告グループの作成など)は、親リソースを作成するオペレーション(キャンペーンの作成など)の後に記述する必要があります。
- 単一リクエストまたはバッチジョブのスコープ: 一時的なリソース名は、個別のジョブや変更リクエストでは保持されません。以前のジョブまたは変更リクエストで作成されたリソースを参照するには、システム生成の実際のリソース名を使用します。
- グローバルな一意性: 1 つのジョブまたは変更リクエスト内で、一時的なリソース名には、すべてのリソースタイプで一意の負の整数を使用する必要があります。
たとえば、同じリクエストでキャンペーンと広告グループの両方に
-1を割り当てることはできません。同じリクエストまたはバッチジョブ内で一時 ID を再利用すると、NewResourceCreationError.DUPLICATE_TEMP_IDSエラーが返されます。
ペイロードの例
1 つの API リクエストまたはバッチジョブで、キャンペーン、広告グループ、広告を追加するとします。次の REST JSON の例に示すように、GoogleAdsService.Mutate または BatchJobService.AddBatchJobOperations リクエスト ペイロードの mutateOperations 配列を構造化できます(簡潔にするため、他の必須リソース フィールドは省略しています)。
{
"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"
}
}
}
]
}
この例では、次の重要な詳細を示しています。
-1はすでにキャンペーンに割り当てられているため、広告グループは新しい一時 ID(-2)を使用します。- 広告グループは
customers/CUSTOMER_ID/campaigns/-1を参照して、前のオペレーションで作成されたキャンペーンにリンクします。 adGroupAdOperationはcustomers/CUSTOMER_ID/adGroups/-2を参照し、リクエスト内の後続のオペレーションで新しい広告を参照しないため、resourceNameは省略されています。
同じタイプのオペレーションをグループ化する
GoogleAdsService.Mutate を使用する場合は、親
と子の依存関係を考慮しながら、繰り返される mutate_operations 配列で
リソースタイプごとにオペレーションをグループ化します。このメソッドは、異なるリソースタイプが検出されるまでオペレーションを順番に読み取り、同じタイプの連続するオペレーションをすべて 1 つのバックエンド サービス リクエストにバッチ処理します。
たとえば、繰り返される mutate_operations フィールドに 5 つのキャンペーン オペレーションと 10 個の広告グループ
オペレーションを含めると、システムは 2 回のバックエンド
呼び出しを行います。1 回は 5 つのキャンペーン
オペレーションに対して CampaignService を呼び出し、2 回目は 10 個の広告
グループ オペレーションに対して AdGroupService を呼び出します。
一方、オペレーションを [campaign, ad group,
campaign, ad group] のように並べ替えてインターリーブすると、4 回の個別のバックエンド呼び出しが発生します。インターリーブされた呼び出しは API
のパフォーマンスを低下させ、大規模なバッチでリクエストのタイムアウトを引き起こす可能性があります。
部分的な失敗とバッチの上限を処理する
デフォルトでは、GoogleAdsService.Mutate はリクエスト全体をロールバックします。同じリクエスト内の他のオペレーションが失敗した場合でも有効なオペレーションをコミットするには、リクエストでpartial_failureをtrueに設定し、レスポンスでpartial_failure_errorを確認します。partial_failure が true の場合、一時 ID(customers/CUSTOMER_ID/campaigns/-1 など)を定義する親オペレーションの検証に失敗すると、同じリクエストでその一時 ID を参照する依存する子オペレーションも NewResourceCreationError.TEMP_ID_RESOURCE_HAD_ERRORS で失敗します。詳細については、
部分的な失敗に関するガイドをご覧ください。
リクエスト サイズ、サブバッチ処理、レート制限にも注意してください。
- リクエストとチャンクのサイズ上限: 1 つの
GoogleAdsService.Mutateリクエストでは、変更オペレーションの上限が 10,000 件(リクエスト内のすべてのオペレーションがAdGroupCriterionOperationの場合は最大 20,000 件。上限を超えるとRequestError.TOO_MANY_MUTATE_OPERATIONSが返されます)に設定され、アクション オペレーションの上限は 100 件(RequestError.TOO_MANY_ACTION_OPERATIONS)です。BatchJobService.AddBatchJobOperationsでは、呼び出しごとに最大 10,000 件のオペレーション、MutateOperationごとに 10,484,504 バイト、AddBatchJobOperationsRequestごとに 41,937,920 バイトが適用されます(上限を超えるとBatchJobError.REQUEST_TOO_LARGEが返されます。バッチジョブあたりのオペレーションの合計数は最大 1,000,000 件です)。同じキャンペーンまたはアカウントを対象とする同時変更は、DatabaseError.CONCURRENT_MODIFICATIONエラーを引き起こす可能性があります。 BatchJobServiceのアトミック サブバッチ処理: バッチジョブは部分的な失敗のセマンティクスで実行されますが(内部サブバッチあたり 1,000 件のオペレーションがデフォルト)、BatchJobServiceは、同じ親 ID の連続する依存オペレーションをアトミック サブバッチに自動的にグループ化します。AssetGroupOperation(create)の後に、同じAssetGroupID の連続するAssetGroupAssetOperation(create)オペレーションが続きます(合計 1,000 件のオペレーション。BatchJobError.ASSET_GROUP_AND_ASSET_GROUP_ASSET_TRANSACTION_FAILUREでアトミックに失敗します。 各AssetGroupOperationupdateまたはremoveは、スタンドアロンの 単一オペレーション サブバッチで実行されます)。- P-MAX の
CampaignOperation(create、ブランド ガイドライン が有効な場合。brand_guidelines_enabledがfalseに設定されていない限り、またはhotel_property_asset_setが設定されていない限り、デフォルトで有効)の後に、同じCampaignID の連続するCampaignAssetOperation(create)オペレーションが続きます(合計 1,000 件のオペレーション。BatchJobError.CAMPAIGN_AND_CAMPAIGN_ASSET_TRANSACTION_FAILUREでアトミックに失敗します)。 - 同じ親(
AssetGroupまたはAdGroup)に対する連続するAssetGroupListingGroupFilterOperation(max 10,000。BatchJobError.ASSET_GROUP_LISTING_GROUP_FILTER_TRANSACTION_FAILUREでアトミックに失敗します)またはAdGroupCriterionOperation(listing_group、max 20,000。CriterionError.LISTING_GROUP_ERROR_IN_ANOTHER_OPERATIONでアトミックに失敗します)オペレーション。
レスポンスから変更可能な属性を取得する
変更リクエストのresponse_content_typeを
MUTABLE_RESOURCEに設定すると、レスポンスには、リクエストによって作成または
更新(削除されない)されたサポート対象オブジェクトごとに、
resource_nameと、その変更可能なフィールドが入力されたリソース オブジェクト(および、返されたリソースの主要なシステム入力フィールド(
ExperimentArm.in_design_campaignsなど))が含まれます。remove オペレーションの場合、または MUTABLE_RESOURCE
の返りをサポートしていないリソースタイプの場合、レスポンスは常に resource_name
のみを返します。この機能を使用すると、変更呼び出しのたびに追加の
Search または SearchStream リクエストを送信する必要がなくなります。
response_content_type を設定しない場合、Google Ads API はデフォルトで RESOURCE_NAME_ONLY
になり、変更された各リソースの resource_name のみが返されます。
次の例は、変更呼び出しから変更可能なリソースを取得する方法を示しています。
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]; }