Page Summary
-
This code example demonstrates how to handle
RateExceededErrorwhen interacting with the Google Ads API. -
The example simulates potential rate limiting by running multiple threads or sequential requests that attempt to validate keywords.
-
The code includes logic to identify
QuotaError.RESOURCE_EXHAUSTEDorQuotaError.RESOURCE_TEMPORARILY_EXHAUSTEDerrors. -
An exponential backoff strategy is used to retry requests after a delay if a rate exceeded error occurs.
-
This example is for illustrative purposes and should not be used to intentionally trigger rate limits.
Java
// Copyright 2019 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package com.google.ads.googleads.examples.errorhandling ; import com.beust.jcommander.Parameter ; import com.google.ads.googleads.examples.utils.ArgumentNames ; import com.google.ads.googleads.examples.utils.CodeSampleParams ; import com.google.ads.googleads.lib.GoogleAdsClient ; import com.google.ads.googleads.v22.common.KeywordInfo ; import com.google.ads.googleads.v22.enums.AdGroupCriterionStatusEnum.AdGroupCriterionStatus ; import com.google.ads.googleads.v22.enums.KeywordMatchTypeEnum.KeywordMatchType ; import com.google.ads.googleads.v22.errors.GoogleAdsError ; import com.google.ads.googleads.v22.errors.GoogleAdsException ; import com.google.ads.googleads.v22.errors.QuotaErrorEnum.QuotaError ; import com.google.ads.googleads.v22.resources.AdGroupCriterion ; import com.google.ads.googleads.v22.services.AdGroupCriterionOperation ; import com.google.ads.googleads.v22.services.AdGroupCriterionServiceClient ; import com.google.ads.googleads.v22.services.MutateAdGroupCriteriaRequest ; import com.google.ads.googleads.v22.services.MutateAdGroupCriteriaResponse ; import com.google.ads.googleads.v22.utils.ResourceNames ; import java.io.FileNotFoundException ; import java.io.IOException ; import java.util.ArrayList ; import java.util.List ; /** * Handles RateExceededError in an application. This code example runs 5 threads in parallel, each * thread attempting to validate 100 keywords in a single request. While spanning 5 parallel threads * is unlikely to trigger a rate exceeded error, substantially increasing the number of threads may * have that effect. Note that this example is for illustrative purposes only, and you shouldn't * intentionally try to trigger a rate exceed error in your application. */ public class HandleRateExceededError { private static class HandleRateExceededErrorParams extends CodeSampleParams { @Parameter ( names = ArgumentNames . CUSTOMER_ID , required = true ) private Long customerId ; @Parameter ( names = ArgumentNames . AD_GROUP_ID , required = true ) private Long adGroupId ; } public static void main ( String [] args ) throws InterruptedException { HandleRateExceededErrorParams params = new HandleRateExceededErrorParams (); if ( ! params . parseArguments ( args )) { // Either pass the required parameters for this example on the command line, or insert them // into the code here. See the parameter class definition above for descriptions. params . customerId = Long . parseLong ( "INSERT_CUSTOMER_ID_HERE" ); params . adGroupId = Long . parseLong ( "INSERT_AD_GROUP_ID_HERE" ); } GoogleAdsClient googleAdsClient = null ; try { googleAdsClient = GoogleAdsClient . newBuilder (). fromPropertiesFile (). build (); } catch ( FileNotFoundException fnfe ) { System . err . printf ( "Failed to load GoogleAdsClient configuration from file. Exception: %s%n" , fnfe ); System . exit ( 1 ); } catch ( IOException ioe ) { System . err . printf ( "Failed to create GoogleAdsClient. Exception: %s%n" , ioe ); System . exit ( 1 ); } try { new HandleRateExceededError () . runExample ( googleAdsClient , params . customerId , params . adGroupId ); } catch ( GoogleAdsException gae ) { // GoogleAdsException is the base class for most exceptions thrown by an API request. // Instances of this exception have a message and a GoogleAdsFailure that contains a // collection of GoogleAdsErrors that indicate the underlying causes of the // GoogleAdsException. System . err . printf ( "Request ID %s failed due to GoogleAdsException. Underlying errors:%n" , gae . getRequestId ()); int i = 0 ; for ( GoogleAdsError googleAdsError : gae . getGoogleAdsFailure (). getErrorsList ()) { System . err . printf ( " Error %d: %s%n" , i ++ , googleAdsError ); } System . exit ( 1 ); } } /** * Runs the example. * * @param googleAdsClient the Google Ads API client. * @param customerId the client customer ID. * @param adGroupId the ID of the ad group to which keywords are added. */ private void runExample ( GoogleAdsClient googleAdsClient , long customerId , long adGroupId ) throws InterruptedException { final int NUM_THREADS = 5 ; List<Thread> threads = new ArrayList <> (); // Adds 5 threads that validate keywords to a list. for ( int i = 0 ; i < NUM_THREADS ; i ++ ) { KeywordsThread keywordsThread = new KeywordsThread ( googleAdsClient , customerId , adGroupId , i ); Thread thread = new Thread ( keywordsThread ); threads . add ( thread ); } // Starts the threads. for ( Thread thread : threads ) { thread . start (); } // Ensures the calling thread waits until all threads of terminated. for ( Thread thread : threads ) { thread . join (); } } /** Thread class for validating keywords */ private static class KeywordsThread implements Runnable { // Number of keywords to be validated in each API call. private final int NUM_KEYWORDS = 100 ; // The GoogleAdsClient. private final GoogleAdsClient googleAdsClient ; // The customer ID. private final long customerId ; // The ad group ID to which keywords are added. private final long adGroupId ; // Index of this thread, for identifying and debugging. private final int threadIndex ; /** * Initializes a new instance of the KeywordThread * * @param googleAdsClient the Google Ads API client. * @param customerId the client customer ID. * @param adGroupId the ID of the ad group to which keywords are added. * @param threadIndex the index of the thread. */ public KeywordsThread ( GoogleAdsClient googleAdsClient , long customerId , long adGroupId , int threadIndex ) { this . googleAdsClient = googleAdsClient ; this . customerId = customerId ; this . adGroupId = adGroupId ; this . threadIndex = threadIndex ; } /** Main method for the thread. */ public void run () { List<AdGroupCriterionOperation> operations = new ArrayList <> (); for ( int i = 0 ; i < NUM_KEYWORDS ; i ++ ) { // Configures the keywordText text and match type settings. KeywordInfo keywordInfo = KeywordInfo . newBuilder () . setText ( "mars cruise thread " + String . valueOf ( threadIndex ) + " seed " + "" + String . valueOf ( i )) . setMatchType ( KeywordMatchType . EXACT ) . build (); String adGroupResourceName = ResourceNames . adGroup ( customerId , adGroupId ); // Constructs an ad group criterion using the keywordText configuration above. AdGroupCriterion criterion = AdGroupCriterion . newBuilder () . setAdGroup ( adGroupResourceName ) . setStatus ( AdGroupCriterionStatus . PAUSED ) . setKeyword ( keywordInfo ) . build (); // Creates the operation. AdGroupCriterionOperation operation = AdGroupCriterionOperation . newBuilder (). setCreate ( criterion ). build (); operations . add ( operation ); } // Gets the AdGroupCriterionService. This should be done within the thread, since a service // can only handle one outgoing HTTP request at a time. try ( AdGroupCriterionServiceClient adGroupCriterionServiceClient = googleAdsClient . getLatestVersion (). createAdGroupCriterionServiceClient ()) { int retryCount = 0 ; int retrySeconds = 10 ; final int NUM_RETRIES = 3 ; try { while ( retryCount < NUM_RETRIES ) { try { // Creates the validateOnly request. MutateAdGroupCriteriaRequest mutateAdGroupCriteriaRequest = MutateAdGroupCriteriaRequest . newBuilder () . setCustomerId ( Long . toString ( customerId )) . addAllOperations ( operations ) . setValidateOnly ( true ) . build (); // Makes the mutate request. The result set will be empty because validateOnly is set // to true in the MutateAdGroupCriteriaRequest. MutateAdGroupCriteriaResponse response = adGroupCriterionServiceClient . mutateAdGroupCriteria ( mutateAdGroupCriteriaRequest ); System . out . printf ( "%d operations validated.%n" , operations . size ()); break ; } catch ( GoogleAdsException gae ) { for ( GoogleAdsError googleAdsError : gae . getGoogleAdsFailure (). getErrorsList ()) { // Checks if any of the errors are QuotaError.RESOURCE_EXHAUSTED or // QuotaError.RESOURCE_TEMPORARILY_EXHAUSTED. if ( googleAdsError . getErrorCode (). getQuotaError () == QuotaError . RESOURCE_EXHAUSTED || googleAdsError . getErrorCode (). getQuotaError () == QuotaError . RESOURCE_TEMPORARILY_EXHAUSTED ) { System . err . printf ( "Received rate exceeded error, retry after %d seconds.%n" , retrySeconds ); Thread . sleep ( retrySeconds * 1000 ); retryCount ++ ; // Uses an exponential backoff policy to avoid polling too aggressively. retrySeconds *= 2 ; } } } finally { if ( retryCount == NUM_RETRIES ) { throw new Exception ( String . format ( "Could not recover after making %d attempts.%n" , retryCount )); } } } } catch ( Exception e ) { System . err . printf ( "Failed to validate keywords.%n" , e ); System . exit ( 1 ); } } } } }
C#
// Copyright 2020 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. using CommandLine ; using Google.Ads.Gax.Examples ; using Google.Ads.GoogleAds.Lib ; using Google.Ads.GoogleAds.V22.Common ; using Google.Ads.GoogleAds.V22.Errors ; using Google.Ads.GoogleAds.V22.Resources ; using Google.Ads.GoogleAds.V22.Services ; using System ; using System.Collections.Generic ; using System.Linq ; using System.Threading ; using System.Threading.Tasks ; using static Google . Ads . GoogleAds . V22 . Enums . AdGroupCriterionStatusEnum . Types ; using static Google . Ads . GoogleAds . V22 . Enums . KeywordMatchTypeEnum . Types ; using static Google . Ads . GoogleAds . V22 . Errors . QuotaErrorEnum . Types ; namespace Google.Ads.GoogleAds.Examples.V22 { /// <summary> /// This code example demonstrates how to handle RateExceededError in an application. /// This code example runs 5 threads in parallel, each thread attempting to validate /// 100 keywords in a single request. While spanning 5 parallel threads is unlikely to /// trigger a rate exceeded error, substantially increasing the number of threads may /// have that effect. Note that this example is for illustrative purposes only, and you /// shouldn't intentionally try to trigger a rate exceed error in your application. /// </summary> public class HandleRateExceededError : ExampleBase { /// <summary> /// Command line options for running the <see cref="HandleRateExceededError"/> example. /// </summary> public class Options : OptionsBase { /// <summary> /// The customer ID for which the call is made. /// </summary> [Option("customerId", Required = true, HelpText = "The customer ID for which the call is made.")] public long CustomerId { get ; set ; } /// <summary> /// ID of the ad group to which keywords are added. /// </summary> [Option("adGroupId", Required = true, HelpText = "ID of the ad group to which keywords are added.")] public long AdGroupId { get ; set ; } } /// <summary> /// Main method, to run this code example as a standalone application. /// </summary> /// <param name="args">The command line arguments.</param> public static void Main ( string [] args ) { Options options = ExampleUtilities . ParseCommandLine<Options> ( args ); HandleRateExceededError codeExample = new HandleRateExceededError (); Console . WriteLine ( codeExample . Description ); codeExample . Run ( new GoogleAdsClient (), options . CustomerId , options . AdGroupId ); } // Number of threads to use in the code example. private const int NUM_THREADS = 5 ; // Number of keywords to be validated in each API call. private const int NUM_KEYWORDS = 5000 ; /// <summary> /// Returns a description about the code example. /// </summary> public override string Description = > "This code example demonstrates how to handle RateExceededError in an application. " + "This code example runs 5 threads in parallel, each thread attempting to validate " + "100 keywords in a single request. While spanning 5 parallel threads is unlikely to " + "trigger a rate exceeded error, substantially increasing the number of threads may " + "have that effect. Note that this example is for illustrative purposes only, and you " + "shouldn't intentionally try to trigger a rate exceed error in your application." ; /// <summary> /// Runs the code example. /// </summary> /// <param name="client">The Google Ads client.</param> /// <param name="customerId">The customer ID for which the call is made.</param> /// <param name="adGroupId">ID of the ad group to which keywords are added.</param> public void Run ( GoogleAdsClient client , long customerId , long adGroupId ) { List<Task> tasks = new List<Task> (); for ( int i = 0 ; i < NUM_THREADS ; i ++ ) { Task t = CreateKeyword ( client , i , customerId , adGroupId ); tasks . Add ( t ); } Task . WaitAll ( tasks . ToArray ()); } /// <summary> /// Displays the result from the mutate operation. /// </summary> /// <param name="client">The Google Ads client.</param> /// <param name="customerId">The Google Ads customer ID for which the call is made.</param> /// <param name="adGroupId">The ad group to which keywords are added.</param> /// <param name="threadIndex">The thread ID.</param> private async Task CreateKeyword ( GoogleAdsClient client , int threadIndex , long customerId , long adGroupId ) { await Task . Run (() = > { // Get the AdGroupCriterionServiceClient. AdGroupCriterionServiceClient adGroupCriterionService = client . GetService ( Services . V22 . AdGroupCriterionService ); List<AdGroupCriterionOperation> operations = new List<AdGroupCriterionOperation> (); for ( int i = 0 ; i < NUM_KEYWORDS ; i ++ ) { AdGroupCriterion criterion = new AdGroupCriterion () { Keyword = new KeywordInfo () { Text = $"mars cruise thread {threadIndex} seed {i}" , MatchType = KeywordMatchType . Exact }, AdGroup = ResourceNames . AdGroup ( customerId , adGroupId ), Status = AdGroupCriterionStatus . Paused }; // Creates the operation. operations . Add ( new AdGroupCriterionOperation () { Create = criterion }); } int retryCount = 0 ; int retrySeconds = 30 ; const int NUM_RETRIES = 3 ; while ( retryCount < NUM_RETRIES ) { try { // Makes the validateOnly mutate request. MutateAdGroupCriteriaResponse response = adGroupCriterionService . MutateAdGroupCriteria ( new MutateAdGroupCriteriaRequest () { CustomerId = customerId . ToString (), Operations = { operations }, PartialFailure = false , ValidateOnly = true }); Console . WriteLine ( $"[{threadIndex}] Validated {operations.Count} " + $"ad group criteria:" ); break ; } catch ( GoogleAdsException e ) { // Checks if any of the errors are QuotaError.RESOURCE_EXHAUSTED or // QuotaError.RESOURCE_TEMPORARILY_EXHAUSTED. // Note: The code assumes that the developer token is approved for // Standard Access. if ( e . Failure != null ) { bool isRateExceededError = false ; e . Failure . Errors . Where ( err = > err . ErrorCode . QuotaError == QuotaError . ResourceExhausted || err . ErrorCode . QuotaError == QuotaError . ResourceTemporarilyExhausted ) . ToList () . ForEach ( delegate ( GoogleAdsError err ) { Console . Error . WriteLine ( $"[{threadIndex}] Received rate " + $"exceeded error. Message says, \"{err.Message}\"." ); isRateExceededError = true ; } ); if ( isRateExceededError ) { Console . Error . WriteLine ( $"[{threadIndex}] Will retry after {retrySeconds} seconds." ); Thread . Sleep ( retrySeconds * 1000 ); retryCount ++ ; // Uses an exponential backoff policy to avoid polling too // aggressively. retrySeconds *= 2 ; } } else { Console . WriteLine ( "Failure:" ); Console . WriteLine ( $"Message: {e.Message}" ); Console . WriteLine ( $"Failure: {e.Failure}" ); Console . WriteLine ( $"Request ID: {e.RequestId}" ); break ; } } finally { if ( retryCount == NUM_RETRIES ) { throw new Exception ( $"[{ threadIndex }] Could not recover after " + $"making {retryCount} attempts." ); } } } }); } } }
PHP
< ?php /** * Copyright 2019 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ namespace Google\Ads\GoogleAds\Examples\ErrorHandling; require __DIR__ . '/../../vendor/autoload.php'; use GetOpt\GetOpt; use Google\Ads\GoogleAds\Examples\Utils\ArgumentNames; use Google\Ads\GoogleAds\Examples\Utils\ArgumentParser; use Google\Ads\GoogleAds\Lib\OAuth2TokenBuilder; use Google\Ads\GoogleAds\Lib\V22\GoogleAdsClient; use Google\Ads\GoogleAds\Lib\V22\GoogleAdsClientBuilder; use Google\Ads\GoogleAds\Lib\V22\GoogleAdsException; use Google\Ads\GoogleAds\Util\V22\ResourceNames; use Google\Ads\GoogleAds\V22\Common\KeywordInfo; use Google\Ads\GoogleAds\V22\Enums\AdGroupCriterionStatusEnum\AdGroupCriterionStatus; use Google\Ads\GoogleAds\V22\Enums\KeywordMatchTypeEnum\KeywordMatchType; use Google\Ads\GoogleAds\V22\Errors\GoogleAdsError; use Google\Ads\GoogleAds\V22\Errors\QuotaErrorEnum\QuotaError; use Google\Ads\GoogleAds\V22\Resources\AdGroupCriterion; use Google\Ads\GoogleAds\V22\Services\AdGroupCriterionOperation; use Google\Ads\GoogleAds\V22\Services\GoogleAdsRow; use Google\Ads\GoogleAds\V22\Services\MutateAdGroupCriteriaRequest; use Google\ApiCore\ApiException; use Exception; /** * Handles RateExceededError in an application. This code example runs 5 requests sequentially, * each request attempting to validate 100 keywords. While it is unlikely that running * these requests would trigger a rate exceeded error, substantially increasing the * number of requests may have that effect. Note that this example is for illustrative * purposes only, and you shouldn't intentionally try to trigger a rate exceed error in your * application. */ class HandleRateExceededError { private const CUSTOMER_ID = 'INSERT_CUSTOMER_ID_HERE'; private const AD_GROUP_ID = 'INSERT_AD_GROUP_ID_HERE'; // Number of requests to be run. private const NUM_REQUESTS = 5; // Number of keywords to be validated in each API call. private const NUM_KEYWORDS = 100; // Number of retries to be run in case of a RateExceededError. private const NUM_RETRIES = 3; // Minimum number of seconds to wait before a retry. private const RETRY_SECONDS = 10; public static function main() { // Either pass the required parameters for this example on the command line, or insert them // into the constants above. $options = (new ArgumentParser())->parseCommandArguments([ ArgumentNames::CUSTOMER_ID => GetOpt::REQUIRED_ARGUMENT, ArgumentNames::AD_GROUP_ID => GetOpt::REQUIRED_ARGUMENT ]); // Generate a refreshable OAuth2 credential for authentication. $oAuth2Credential = (new OAuth2TokenBuilder())->fromFile()->build(); // Construct a Google Ads client configured from a properties file and the // OAuth2 credentials above. $googleAdsClient = (new GoogleAdsClientBuilder())->fromFile() ->withOAuth2Credential($oAuth2Credential) ->build(); try { self::runExample( $googleAdsClient, $options[ArgumentNames::CUSTOMER_ID] ?: self::CUSTOMER_ID, $options[ArgumentNames::AD_GROUP_ID] ?: self::AD_GROUP_ID ); } catch (GoogleAdsException $googleAdsException) { printf( "Request with ID '%s' has failed.%sGoogle Ads failure details:%s", $googleAdsException->getRequestId(), PHP_EOL, PHP_EOL ); foreach ($googleAdsException->getGoogleAdsFailure()->getErrors() as $error) { /** @var GoogleAdsError $error */ printf( "\t%s: %s%s", $error->getErrorCode()->getErrorCode(), $error->getMessage(), PHP_EOL ); } exit(1); } catch (ApiException $apiException) { printf( "ApiException was thrown with message '%s'.%s", $apiException->getMessage(), PHP_EOL ); exit(1); } } /** * Runs the example. * * @param GoogleAdsClient $googleAdsClient the Google Ads API client * @param int $customerId the customer ID * @param int $adGroupId the ad group ID to validate keywords from */ public static function runExample( GoogleAdsClient $googleAdsClient, int $customerId, int $adGroupId ) { // Sequentially sends the requests. for ($i = 0; $i < self::NUM_REQUESTS; $i++) { // Creates operations. $operations = self::createAdGroupCriterionOperations($customerId, $adGroupId, $i); try { $retryCount = 0; $retrySeconds = self::RETRY_SECONDS; while ($retryCount < self::NUM_RETRIES) { try { // Sends request. self::requestMutateAndDisplayResult( $googleAdsClient, $customerId, $operations ); break; } catch (GoogleAdsException $googleAdsException) { $hasRateExceededError = false; foreach ( $googleAdsException->getGoogleAdsFailure() ->getErrors() as $googleAdsError ) { // Checks if any of the errors are QuotaError.RESOURCE_EXHAUSTED or // QuotaError.RESOURCE_TEMPORARILY_EXHAUSTED. if ( $googleAdsError->getErrorCode()->getQuotaError() == QuotaError::RESOURCE_EXHAUSTED || $googleAdsError->getErrorCode()->getQuotaError() == QuotaError::RESOURCE_TEMPORARILY_EXHAUSTED ) { printf( 'Received rate exceeded error, retry after %d seconds.%s', $retrySeconds, PHP_EOL ); sleep($retrySeconds); $hasRateExceededError = true; $retryCount++; // Uses an exponential back-off policy. $retrySeconds *= 2; break; } } // Bubbles up when there is not RateExceededError if (!$hasRateExceededError) { throw $googleAdsException; } } finally { // Bubbles up when the number of retries has already been reached. if ($retryCount == self::NUM_RETRIES) { throw new Exception(sprintf( 'Could not recover after making %d attempts.%s', $retryCount, PHP_EOL )); } } } } catch (Exception $exception) { // Prints any unhandled exception and bubbles up. printf( 'Failed to validate keywords.%1$s%2$s%1$s', PHP_EOL, $exception->getMessage() ); throw $exception; } } } /** * Creates ad group criterion operations. * * @param int $customerId the customer ID * @param int $adGroupId the ad group ID to link the ad group criteria to * @param int $reqIndex the request index * @return array the created ad group criterion operations */ private static function createAdGroupCriterionOperations( int $customerId, int $adGroupId, int $reqIndex ) { $operations = []; for ($i = 0; $i < self::NUM_KEYWORDS; $i++) { // Creates a keyword info. $keywordInfo = new KeywordInfo([ 'text' => 'mars cruise req ' . $reqIndex . ' seed ' . $i, 'match_type' => KeywordMatchType::EXACT ]); // Constructs an ad group criterion using the keyword text info above. $adGroupCriterion = new AdGroupCriterion([ 'ad_group' => ResourceNames::forAdGroup($customerId, $adGroupId), 'status' => AdGroupCriterionStatus::ENABLED, 'keyword' => $keywordInfo ]); // Creates an ad group criterion operation. $adGroupCriterionOperation = new AdGroupCriterionOperation(); $adGroupCriterionOperation->setCreate($adGroupCriterion); $operations[] = $adGroupCriterionOperation; } return $operations; } /** * Requests a mutate of ad group criterion operations and displays the results. * * @param GoogleAdsClient $googleAdsClient the Google Ads API client * @param int $customerId the customer ID * @param array $operations the ad group criterion operations */ private static function requestMutateAndDisplayResult( GoogleAdsClient $googleAdsClient, int $customerId, array $operations ) { $adGroupCriterionServiceClient = $googleAdsClient->getAdGroupCriterionServiceClient(); // Makes a validateOnly mutate request. $response = $adGroupCriterionServiceClient->mutateAdGroupCriteria( MutateAdGroupCriteriaRequest::build($customerId, $operations) ->setPartialFailure(false) ->setValidateOnly(true) ); // Displays the results. printf( "Added %d ad group criteria:%s", $response->getResults()->count(), PHP_EOL ); foreach ($response->getResults() as $result) { /** @var GoogleAdsRow $result */ print $result->getAdGroupCriterion()->getResourceName() . PHP_EOL; } } } HandleRateExceededError::main();
Python
#!/usr/bin/env python # Copyright 2020 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Handles RateExceededError in an application. This code example runs 5 requests sequentially, each request attempting to validate 100 keywords. While it is unlikely that running these requests would trigger a rate exceeded error, substantially increasing the number of requests may have that effect. Note that this example is for illustrative purposes only, and you shouldn't intentionally try to trigger a rate exceed error in your application. """ import argparse from time import sleep from typing import List , Any from google.ads.googleads.client import GoogleAdsClient from google.ads.googleads.errors import GoogleAdsException from google.ads.googleads.v22.errors.types.quota_error import QuotaErrorEnum from google.ads.googleads.v22.services.services.ad_group_service import ( AdGroupServiceClient , ) from google.ads.googleads.v22.services.services.ad_group_criterion_service import ( AdGroupCriterionServiceClient , ) from google.ads.googleads.v22.services.types.ad_group_criterion_service import ( AdGroupCriterionOperation , MutateAdGroupCriteriaRequest , MutateAdGroupCriteriaResponse , ) # Number of requests to be run. NUM_REQUESTS : int = 5 # Number of keywords to be validated in each API call. NUM_KEYWORDS : int = 100 # Number of retries to be run in case of a RateExceededError. NUM_RETRIES : int = 3 # Minimum number of seconds to wait before a retry. RETRY_SECONDS : int = 10 def main ( client : GoogleAdsClient , customer_id : str , ad_group_id : str ) - > None : """Runs the example code, which shows how to handle rate exceeded errors. Args: client: An initialized GoogleAdsClient instance. customer_id: A valid customer account ID. ad_group_id: The ad group ID to validate keywords from. """ quota_error_enum : QuotaErrorEnum = client . get_type ( "QuotaErrorEnum" ) . QuotaError resource_exhausted : QuotaErrorEnum = quota_error_enum . RESOURCE_EXHAUSTED temp_resource_exhausted : QuotaErrorEnum = ( quota_error_enum . RESOURCE_TEMPORARILY_EXHAUSTED ) for i in range ( NUM_REQUESTS ): operations : List [ AdGroupCriterionOperation ] = ( create_ad_group_criterion_operations ( client , customer_id , ad_group_id , i ) ) try : retry_count : int = 0 retry_seconds : int = RETRY_SECONDS while retry_count < NUM_RETRIES : try : request_mutate_and_display_result ( client , customer_id , operations ) break except GoogleAdsException as ex : has_rate_exceeded_error : bool = False for googleads_error in ex . failure . errors : # Checks if any of the errors are # QuotaError.RESOURCE_EXHAUSTED or # QuotaError.RESOURCE_TEMPORARILY_EXHAUSTED. quota_error : QuotaErrorEnum = ( googleads_error . error_code . quota_error ) if ( quota_error == resource_exhausted or quota_error == temp_resource_exhausted ): print ( "Received rate exceeded error, retry after" f " { retry_seconds } seconds." ) sleep ( retry_seconds ) has_rate_exceeded_error = True retry_count += 1 # Here exponential backoff is employed to ensure # the account doesn't get rate limited by making # too many requests too quickly. This increases the # time to wait between requests by a factor of 2. retry_seconds *= 2 break # Bubbles up when there is not a RateExceededError if not has_rate_exceeded_error : raise ex finally : if retry_count == NUM_RETRIES : raise Exception ( "Could not recover after making " f " { retry_count } attempts." ) except Exception as ex : # Prints any unhandled exception and bubbles up. print ( f "Failed to validate keywords: { ex } " ) raise ex def create_ad_group_criterion_operations ( client : GoogleAdsClient , customer_id : str , ad_group_id : str , request_index : int , ) - > List [ AdGroupCriterionOperation ]: """Creates ad group criterion operations. The number of operations created depends on the number of keywords this example should remove. That value is configurable via the NUM_KEYWORDS variable. Args: client: An initialized GoogleAdsClient instance. customer_id: A valid customer account ID. ad_group_id: An ID for an AdGroup. request_index: The number from a sequence of requests in which these operations will be sent. Returns: A list of AdGroupCriterionOperation instances. """ ad_group_service : AdGroupServiceClient = client . get_service ( "AdGroupService" ) status : Any = client . enums . AdGroupCriterionStatusEnum . ENABLED match_type : Any = client . enums . KeywordMatchTypeEnum . EXACT operations : List [ AdGroupCriterionOperation ] = [] for i in range ( NUM_KEYWORDS ): ad_group_criterion_operation : AdGroupCriterionOperation = ( client . get_type ( "AdGroupCriterionOperation" ) ) ad_group_criterion : AdGroupCriterion = ( ad_group_criterion_operation . create ) ad_group_criterion . ad_group = ad_group_service . ad_group_path ( customer_id , ad_group_id ) ad_group_criterion . status = status ad_group_criterion . keyword . text = ( f "mars cruise req { request_index } seed { i } " ) ad_group_criterion . keyword . match_type = match_type operations . append ( ad_group_criterion_operation ) return operations def request_mutate_and_display_result ( client : GoogleAdsClient , customer_id : str , operations : List [ AdGroupCriterionOperation ], ) - > None : """Mutates a set of ad group criteria as a dry-run and displays the results. The request is sent with validate_only set to true, so no actual mutations will be made in the API. Args: client: An initialized GoogleAdsClient instance. customer_id: A valid customer account ID. operations: a list of AdGroupCriterionOperation instances. """ ad_group_criterion_service : AdGroupCriterionServiceClient = ( client . get_service ( "AdGroupCriterionService" ) ) request : MutateAdGroupCriteriaRequest = client . get_type ( "MutateAdGroupCriteriaRequest" ) request . customer_id = customer_id request . operations = operations request . validate_only = True response : MutateAdGroupCriteriaResponse = ( ad_group_criterion_service . mutate_ad_group_criteria ( request = request ) ) print ( f "Added { len ( response . results ) } ad group criteria:" ) for ad_group_criterion in response . results : print ( f "Resource name: ' { ad_group_criterion . resource_name } '" ) if __name__ == "__main__" : parser : argparse . ArgumentParser = argparse . ArgumentParser ( description = "Handles RateExceededError in an application.." ) # The following argument(s) should be provided to run the example. parser . add_argument ( "-c" , "--customer_id" , type = str , required = True , help = "The Google Ads customer ID." , ) parser . add_argument ( "-a" , "--ad_group_id" , type = str , required = True , help = "The ID of an ad group belonging to the given customer." , ) args = parser . parse_args () # GoogleAdsClient will read the google-ads.yaml configuration file in the # home directory if none is specified. googleads_client : GoogleAdsClient = GoogleAdsClient . load_from_storage ( version = "v22" ) main ( googleads_client , args . customer_id , args . ad_group_id )
Ruby
# Encoding: utf-8 # # Copyright 2020 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Handles RateExceededError in an application. This code example runs 5 requests # sequentially, each request attempting to validate 100 keywords. While it is # unlikely that running these requests would trigger a rate exceeded error, # substantially increasing the number of requests may have that effect. Note # that this example is for illustrative purposes only, and you shouldn't # intentionally try to trigger a rate exceed error in your application. require 'optparse' require 'google/ads/google_ads' require 'date' def handle_rate_exceeded_error ( customer_id , ad_group_id ) # GoogleAdsClient will read a config file from # ENV['HOME']/google_ads_config.rb when called without parameters client = Google :: Ads :: GoogleAds :: GoogleAdsClient . new # Sequentially sends the requests. NUM_REQUESTS . times do | i | # Creates operations. operations = create_ad_group_criterion_operations ( client , customer_id , ad_group_id , i ) begin retry_count = 0 retry_seconds = RETRY_SECONDS while retry_count < NUM_RETRIES begin # Sends request. request_mutate_and_display_result ( client , customer_id , operations ) break rescue Google :: Ads :: GoogleAds :: Errors :: GoogleAdsError = > e has_rate_exceeded_error = false e . failure . errors . each do | error | # Checks if any of the errors are QuotaError.RESOURCE_EXHAUSTED or # QuotaError.RESOURCE_TEMPORARILY_EXHAUSTED. if error . error_code . quota_error == :RESOURCE_EXHAUSTED \ || error . error_code . quota_error == :RESOURCE_TEMPORARILY_EXHAUSTED puts "Received rate exceeded error, retry after " \ " #{ retry_seconds } seconds." sleep retry_seconds has_rate_exceeded_error = true retry_count += 1 # Uses an exponential back-off policy. retry_seconds *= 2 break end end # Bubbles up when there is not RateExceededError. if ! has_rate_exceeded_error raise end ensure # Bubbles up when the number of retries has already been reached. if retry_count == NUM_RETRIES raise "Could not recover after making #{ retry_count } attempts." end end end rescue StandardError = > e # Prints any unhandled exception and bubbles up. puts "Failed to validate keywords. #{ e . message } " raise end end end def create_ad_group_criterion_operations ( client , customer_id , ad_group_id , req_index ) operations = [] NUM_KEYWORDS . times do | i | # Creates an ad group criterion operation. operations << client . operation . create_resource . ad_group_criterion do | agc | agc . ad_group = client . path . ad_group ( customer_id , ad_group_id ) agc . status = :ENABLED agc . keyword = client . resource . keyword_info do | ki | ki . text = "mars cruise req #{ req_index } seed #{ i } " ki . match_type = :EXACT end end end operations end def request_mutate_and_display_result ( client , customer_id , operations ) # Makes a validate_only mutate request. response = client . service . ad_group_criterion . mutate_ad_group_criteria ( customer_id : customer_id , operations : operations , partial_failure : false , validate_only : true , ) # Displays the results. puts "Added #{ response . results . size } ad group criteria:" response . results . each do | result | puts " \t #{ result . resource_name } " end end if __FILE__ == $PROGRAM_NAME # Number of requests to be run. NUM_REQUESTS = 5 # Number of keywords to be validated in each API call. NUM_KEYWORDS = 100 # Number of retries to be run in case of a RateExceededError. NUM_RETRIES = 3 # Minimum number of seconds to wait before a retry. RETRY_SECONDS = 10 options = {} # The following parameter(s) should be provided to run the example. You can # either specify these by changing the INSERT_XXX_ID_HERE values below, or on # the command line. # # Parameters passed on the command line will override any parameters set in # code. # # Running the example with -h will print the command line usage. options [ :customer_id ] = 'INSERT_CUSTOMER_ID_HERE' options [ :ad_group_id ] = 'INSERT_AD_GROUP_ID_HERE' OptionParser . new do | opts | opts . banner = sprintf ( 'Usage: ruby %s [options]' , File . basename ( __FILE__ )) opts . separator '' opts . separator 'Options:' opts . on ( '-C' , '--customer-id CUSTOMER-ID' , String , 'Customer ID' ) do | v | options [ :customer_id ] = v end opts . on ( '-A' , '--ad-group-id AD-GROUP-ID' , String , 'Ad Group ID' ) do | v | options [ :ad_group_id ] = v end opts . separator '' opts . separator 'Help:' opts . on_tail ( '-h' , '--help' , 'Show this message' ) do puts opts exit end end . parse! begin handle_rate_exceeded_error ( options . fetch ( :customer_id ) . tr ( "-" , "" ), options . fetch ( :ad_group_id ), ) rescue Google :: Ads :: GoogleAds :: Errors :: GoogleAdsError = > e e . failure . errors . each do | error | STDERR . printf ( "Error with message: %s \n " , error . message ) if error . location error . location . field_path_elements . each do | field_path_element | STDERR . printf ( " \t On field: %s \n " , field_path_element . field_name ) end end error . error_code . to_h . each do | k , v | next if v == :UNSPECIFIED STDERR . printf ( " \t Type: %s \n\t Code: %s \n " , k , v ) end end raise end end
Perl
#!/usr/bin/perl -w # # Copyright 2019, Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Handles RateExceededError in an application. This code example runs 5 requests # sequentially, each request attempting to validate 100 keywords. While it is # unlikely that running these requests would trigger a rate exceeded error, # substantially increasing the number of requests may have that effect. Note that # this example is for illustrative purposes only, and you shouldn't intentionally # try to trigger a rate exceed error in your application. use strict ; use warnings ; use utf8 ; use FindBin qw($Bin) ; use lib "$Bin/../../lib" ; use Google::Ads::GoogleAds::Client ; use Google::Ads::GoogleAds::Utils::GoogleAdsHelper ; use Google::Ads::GoogleAds::V22::Resources::AdGroupCriterion ; use Google::Ads::GoogleAds::V22::Common::KeywordInfo ; use Google::Ads::GoogleAds::V22::Enums::KeywordMatchTypeEnum qw(EXACT) ; use Google::Ads::GoogleAds::V22::Enums::AdGroupCriterionStatusEnum qw(ENABLED) ; use Google::Ads::GoogleAds::V22::Services::AdGroupCriterionService::AdGroupCriterionOperation ; use Google::Ads::GoogleAds::V22::Utils::ResourceNames ; use Getopt::Long qw(:config auto_help) ; use Pod::Usage ; use Cwd qw(abs_path) ; use Time::HiRes qw(sleep) ; # Number of requests to be run. use constant NUM_REQUESTS = > 5 ; # Number of keywords to be validated in each API call. use constant NUM_KEYWORDS = > 100 ; # Number of retries to be run in case of a RateExceededError. use constant NUM_RETRIES = > 3 ; # Minimum number of seconds to wait before a retry. use constant RETRY_SECONDS = > 10 ; # The following parameter(s) should be provided to run the example. You can # either specify these by changing the INSERT_XXX_ID_HERE values below, or on # the command line. # # Parameters passed on the command line will override any parameters set in # code. # # Running the example with -h will print the command line usage. my $customer_id = "INSERT_CUSTOMER_ID_HERE" ; my $ad_group_id = "INSERT_AD_GROUP_ID_HERE" ; sub handle_rate_exceeded_error { my ( $api_client , $customer_id , $ad_group_id ) = @_ ; # Sequentially send the requests. for ( my $i = 0 ; $i < NUM_REQUESTS ; $i ++ ) { # Create operations. my $operations = create_ad_group_criterion_operations ( $customer_id , $ad_group_id , $i ); eval { my $retry_count = 0 ; my $retry_seconds = RETRY_SECONDS ; while ( $retry_count < NUM_RETRIES ) { # Send request. my $response = request_mutate_and_display_result ( $api_client , $customer_id , $operations ); if ( $response - > isa ( "Google::Ads::GoogleAds::GoogleAdsException" )) { my $has_rate_exceeded_error = 0 ; foreach my $error ( @ { $response - > get_google_ads_failure () - > { errors }}) { # Check if any of the errors are QuotaError.RESOURCE_EXHAUSTED or # QuotaError.RESOURCE_TEMPORARILY_EXHAUSTED. my $quota_error = $error - > { errorCode }{ quotaError }; if ( $quota_error && grep /^$quota_error/ , ( "RESOURCE_EXHAUSTED" , "RESOURCE_TEMPORARILY_EXHAUSTED" )) { printf "Received rate exceeded error, retry after %d seconds.\n" , $retry_seconds ; sleep ( $retry_seconds ); $has_rate_exceeded_error = 1 ; $retry_count ++ ; # Use an exponential back-off policy. $retry_seconds *= 2 ; last ; } } # Bubble up when there is not RateExceededError. if ( not $has_rate_exceeded_error ) { die $response - > get_message (); } } else { last ; } # Bubble up when the number of retries has already been reached. if ( $retry_count == NUM_RETRIES ) { die "Could not recover after making $retry_count attempts.\n" ,; } } }; if ( $@ ) { # Catch and print any unhandled exception. printf "Failed to validate keywords.\n%s" , $@ ; return 0 ; } } return 1 ; } # Creates ad group criterion operations. sub create_ad_group_criterion_operations { my ( $customer_id , $ad_group_id , $request_index ) = @_ ; my $operations = [] ; for ( my $i = 0 ; $i < NUM_KEYWORDS ; $i ++ ) { # Create a keyword info. my $keyword_info = Google::Ads::GoogleAds::V22::Common:: KeywordInfo - > new ({ text = > "mars cruise req " . $request_index . " seed " . $i , matchType = > EXACT }); # Construct an ad group criterion using the keyword text info above. my $ad_group_criterion = Google::Ads::GoogleAds::V22::Resources:: AdGroupCriterion - > new ({ adGroup = > Google::Ads::GoogleAds::V22::Utils::ResourceNames:: ad_group ( $customer_id , $ad_group_id ), status = > ENABLED , keyword = > $keyword_info }); # Create an ad group criterion operation. my $ad_group_criterion_operation = Google::Ads::GoogleAds::V22::Services::AdGroupCriterionService:: AdGroupCriterionOperation - > new ({ create = > $ad_group_criterion }); push @$operations , $ad_group_criterion_operation ; } return $operations ; } # Requests a mutate of ad group criterion operations and displays the results. sub request_mutate_and_display_result { my ( $api_client , $customer_id , $operations ) = @_ ; # Make a validateOnly mutate request. my $ad_group_criteria_response = $api_client - > AdGroupCriterionService () - > mutate ({ customerId = > $customer_id , operations = > $operations , partialFailure = > "false" , validateOnly = > "true" }); # Display the results. if ( not $ad_group_criteria_response - > isa ( "Google::Ads::GoogleAds::GoogleAdsException" )) { my $ad_group_criterion_results = $ad_group_criteria_response - > { results }; printf "Added %d ad group criteria:\n" , scalar @$ad_group_criterion_results ; foreach my $ad_group_criterion_result ( @$ad_group_criterion_results ) { print $ad_group_criterion_result - > { resourceName }, "\n" ; } } return $ad_group_criteria_response ; } # Don't run the example if the file is being included. if ( abs_path ( $0 ) ne abs_path ( __FILE__ )) { return 1 ; } # Get Google Ads Client, credentials will be read from ~/googleads.properties. my $api_client = Google::Ads::GoogleAds:: Client - > new (); # By default examples are set to die on any server returned fault. $api_client - > set_die_on_faults ( 0 ); # Parameters passed on the command line will override any parameters set in code. GetOptions ( "customer_id=s" = > \ $customer_id , "ad_group_id=i" = > \ $ad_group_id ); # Print the help message if the parameters are not initialized in the code nor # in the command line. pod2usage ( 2 ) if not check_params ( $customer_id , $ad_group_id ); # Call the example. handle_rate_exceeded_error ( $api_client , $customer_id =~ s/-//g r , $ad_group_id ); =pod =head1 NAME handle_rate_exceeded_error =head1 DESCRIPTION Handles RateExceededError in an application. This code example runs 5 requests sequentially, each request attempting to validate 100 keywords. While it is unlikely that running these requests would trigger a rate exceeded error, substantially increasing the number of requests may have that effect. Note that this example is for illustrative purposes only, and you shouldn't intentionally try to trigger a rate exceed error in your application. =head1 SYNOPSIS handle_rate_exceeded_error.pl [options] -help Show the help message. -customer_id The Google Ads customer ID. -ad_group_id The ad group ID. =cut

