Bulk remove ads

162 views
Skip to first unread message

EladB

unread,
Sep 2, 2021, 9:48:38 PM9/2/21
to AdWords API and Google Ads API Forum
Hi

I saw this example on how to remove a single ad.


I've tried to send a list of operation for bulk removing ads, but it didn't work (it's hard to reproduce as I don't have many live ads).

What's the max bulk size for removal? 

Thanks!


Google Ads API Forum Advisor

unread,
Sep 7, 2021, 2:29:10 AM9/7/21
to adwor...@googlegroups.com
Hi,

You can make use of a test account, and create (non serving) ads if you wish to test the bulk removal of ads.

For the bulk removal of ads, I would recommend that you refer to our batch processing guide for more information. The limitations are then discussed here.

For the non BatchJobService approach, you can refer to the below guides for more information on the API's limits : For mutate requests, up to 5,000 operations can be included in a single request.

I hope this helps.

Best regards,

Google Logo
Peter Laurence Napa Oliquino
Google Ads API Team
 


ref:_00D1U1174p._5004Q2MlCCh:ref

EladB

unread,
Sep 7, 2021, 7:46:15 PM9/7/21
to AdWords API and Google Ads API Forum
Thanks, please allow me to dive deeper to my question:

Google Ads API: batch-processing vs bulk mutates


I want to remove a few ads in one server request.
What is the difference between:

 - batch-processing (only async?) 
 - bulk mutates (only sync, shorter code?)



I have tried both ways and got errors:


1) batch-processing

I've tried to follow this [post][1] about `batch-processing` to create an async batch job for removing multiple ads. It was sent to the server, but I didn't see the sent ad ids were deleted.

Do I miss anything?


    class ServiceWrapper:
        """Wraps GoogleAdsService API request"""
    
        # public properties ...
    
        def __init__(self, client, customer_id):
            self._client = client
            self._ga_service = client.get_service("GoogleAdsService")
            self._ad_group_ad_service = client.get_service("AdGroupAdService")
            self._batch_job_service = client.get_service("BatchJobService")
            self._customer_id = customer_id
    
            self._batch_job_operation = self._create_batch_job_operation(client)
            self._batch_job_resource_name = self._create_batch_job(self._batch_job_service, customer_id,
                                                                   self._batch_job_operation)
    
        def _create_batch_job_operation(self, client):
            """Created a BatchJobOperation and sets an empty BatchJob instance to
            the "create" property in order to tell the Google Ads API that we're
            creating a new BatchJob.
            Args:
                client: an initialized GoogleAdsClient instance.
            Returns: a BatchJobOperation with a BatchJob instance set in the "create"
                property.
            """
            batch_job_operation = client.get_type("BatchJobOperation")
            batch_job = client.get_type("BatchJob")
            client.copy_from(batch_job_operation.create, batch_job)
            return batch_job_operation
    
        def _create_batch_job(self, batch_job_service, customer_id, batch_job_operation):
            """Creates a batch job for the specified customer ID.
            Args:
                batch_job_service: an instance of the BatchJobService message class.
                customer_id: a str of a customer ID.
                batch_job_operation: a BatchJobOperation instance set to "create"
            Returns: a str of a resource name for a batch job.
            """
            try:
                response = batch_job_service.mutate_batch_job(
                    customer_id=customer_id, operation=batch_job_operation
                )
                resource_name = response.result.resource_name
                print(f'Created a batch job with resource name "{resource_name}"')
                return resource_name
            except GoogleAdsException as exception:
                handle_googleads_exception(exception)
    
        def add_all_batch_job_operations(self, batch_job_service, operations, resource_name):
            """Adds all mutate operations to the batch job.
            As this is the first time for this batch job, we pass null as a sequence
            token. The response will contain the next sequence token that we can use
            to upload more operations in the future.
            Args:
                batch_job_service: an instance of the BatchJobService message class.
                operations: a list of a mutate operations.
                resource_name: a str of a resource name for a batch job.
            """
            try:
                response = batch_job_service.add_batch_job_operations(
                    resource_name=resource_name,
                    sequence_token=None,
                    mutate_operations=operations,
                )
    
                print(
                    f"{response.total_operations} mutate operations have been "
                    "added so far."
                )
    
                # You can use this next sequence token for calling
                # add_batch_job_operations() next time.
                print(
                    "Next sequence token for adding next operations is "
                    f"{response.next_sequence_token}"
                )
            except GoogleAdsException as exception:
                handle_googleads_exception(exception)
    
    
    def remove_disapproved_ads_for_account(account):
        """Remove all disapproved ads for a given customer id"""
        ad_removal_operations = []
            for row in rows:
                        ad_removal_operations.append(
                            build_removal_operation(customer_id, ad_json["ad_group_id"],     
        if len(ad_removal_operations) > 0:
            remove_ads(ad_removal_operations)
            #serviceWrapper.mutate(customer_id, [mutate_operation1, mutate_operation2])
    
    
    def build_removal_operation(customer_id, ad_group_id, ad_id):
        """Removes the specified ad"""
        resource_name = serviceWrapper.ad_group_ad_service.ad_group_ad_path(
            customer_id, ad_group_id, ad_id
        )
        ad_group_ad_operation = serviceWrapper.client.get_type("AdGroupAdOperation")
        ad_group_ad_operation.remove = resource_name
        return ad_group_ad_operation
    
    async def remove_ads(removal_operations):
        """Removes the specified ad"""
        serviceWrapper.add_all_batch_job_operations(serviceWrapper.batch_job_service, removal_operations,
                                                    serviceWrapper.batch_job_resource_name)
        operations_response = _run_batch_job(serviceWrapper.batch_job_service, serviceWrapper.batch_job_resource_name)
    
        # Create an asyncio.Event instance to control execution during the
        # asyncronous steps in _poll_batch_job. Note that this is not important
        # for polling asyncronously, it simply helps with execution control so we
        # can run _fetch_and_print_results after the asyncronous operations have
        # completed.
        _done_event = asyncio.Event()
        _poll_batch_job(operations_response, _done_event)
        # Execution will stop here and wait for the asyncronous steps in
        # _poll_batch_job to complete before proceeding.
        await _done_event.wait()
        _fetch_and_print_results(serviceWrapper.client, serviceWrapper.batch_job_service,
                                 serviceWrapper.batch_job_resource_name)
    
    
    def _run_batch_job(batch_job_service, resource_name):
        """Runs the batch job for executing all uploaded mutate operations.
        Args:
            batch_job_service: an instance of the BatchJobService message class.
            resource_name: a str of a resource name for a batch job.
        Returns: a google.api_core.operation.Operation instance.
        """
        try:
            response = batch_job_service.run_batch_job(resource_name=resource_name)
            print(
                f'Batch job with resource name "{resource_name}" has been '
                "executed."
            )
            return response
        except GoogleAdsException as exception:
            handle_googleads_exception(exception)
    
    
    def _poll_batch_job(operations_response, event):
        """Polls the server until the batch job execution finishes.
        Sets the initial poll delay time and the total time to wait before time-out.
        Args:
            operations_response: a google.api_core.operation.Operation instance.
            event: an instance of asyncio.Event to invoke once the operations have
                completed, alerting the awaiting calling code that it can proceed.
        """
        loop = asyncio.get_event_loop()
    
        def _done_callback(future):
            # The operations_response object will call callbacks from a daemon
            # thread so we must use a threadsafe method of setting the event here
            # otherwise it will not trigger the awaiting code.
            loop.call_soon_threadsafe(event.set)
    
        # operations_response represents a Long-Running Operation or LRO. The class
        # provides an interface for polling the API to check when the operation is
        # complete. Below we use the asynchronous interface, but there's also a
        # synchronous interface that uses the Operation.result method.
        operations_response.add_done_callback(_done_callback)
    
    
    def _fetch_and_print_results(client, batch_job_service, resource_name):
        """Prints all the results from running the batch job.
        Args:
            client: an initialized GoogleAdsClient instance.
            batch_job_service: an instance of the BatchJobService message class.
            resource_name: a str of a resource name for a batch job.
        """
        print(
            f'Batch job with resource name "{resource_name}" has finished. '
            "Now, printing its results..."
        )
    
        list_results_request = client.get_type("ListBatchJobResultsRequest")
        list_results_request.resource_name = resource_name
        list_results_request.page_size = BULK_REMOVE_PAGE_SIZE
        # Gets all the results from running batch job and prints their information.
        batch_job_results = batch_job_service.list_batch_job_results(
            request=list_results_request
        )
    
        for batch_job_result in batch_job_results:
            status = batch_job_result.status.message
            status = status if status else "N/A"
            result = batch_job_result.mutate_operation_response
            result = result or "N/A"
            print(
                f"Batch job #{batch_job_result.operation_index} "
                f'has a status "{status}" and response type "{result}"'
            )


2) Bulk Mutates

If I choose to follow this [post][2] about `Bulk Mutates`, and create a sync batch, I get an undefined symbol `:Mutate` how can I fix this? Or make this code work?



    class ServiceWrapper:
        """Wraps GoogleAdsService API request"""
    
        # public properties ...
    
        def __init__(self, client, customer_id):
            self._client = client
            self._ga_service = client.get_service("GoogleAdsService")
            self._ad_group_ad_service = client.get_service("AdGroupAdService")
            self._batch_job_service = client.get_service("BatchJobService")
            self._customer_id = customer_id
    
            self._batch_job_operation = self._create_batch_job_operation(client)
            self._batch_job_resource_name = self._create_batch_job(self._batch_job_service, customer_id,
                                                                   self._batch_job_operation)
    
           
    
     def build_removal_operation_sync(customer_id, ad_group_id, ad_id):
         mutate_operation1 = serviceWrapper.client.operation(:Mutate)
    
         """Removes the specified ad"""
         resource_name = serviceWrapper.ad_group_ad_service.ad_group_ad_path(
             customer_id, ad_group_id, ad_id
         )
         ad_group_ad_operation = serviceWrapper.client.get_type("AdGroupAdOperation")
         ad_group_ad_operation.remove = resource_name
    
         mutate_operation1.ad_group_ad_operation = campaign_operation
    
    


Google Ads API Forum Advisor

unread,
Sep 8, 2021, 2:16:49 AM9/8/21
to adwor...@googlegroups.com
Hi Elad,

Thank you for your follow up.

1) batch-processing - I've tried to follow this [post][1] about `batch-processing` to create an async batch job for removing multiple ads. It was sent to the server, but I didn't see the sent ad ids were deleted.

You can poll your batch jobs and then list the results for monitoring purposes. This should allow you to see whether your operations (containing the ad IDs you passed) were successful or resulted into an error.

I would also recommend that you enable logging so that the request and response logs could be visible on your end. Logging can be enabled by navigating to the Client libraries > Your client library (ex. Java) > Logging documentation, which you can access from this link.


2) Bulk Mutates If I choose to follow this [post][2] about `Bulk Mutates`, and create a sync batch, I get an undefined symbol `:Mutate` how can I fix this? Or make this code work?

The issue (undefined symbol `:Mutate) you are encountering could be a client library issue on how you implemented the code. I would recommend that you reach out to client library owner via the Issues tab of their respective Github sites which you can access from here (links under the Source column), for further guidance on how to implement the mutate in your code.

Elad Ben-David

unread,
Sep 8, 2021, 11:12:15 AM9/8/21
to ads...@forumsupport.google, adwor...@googlegroups.com
Thank you so much for your help.

Please allow me to hune on my questions:

1) What are the ways to send a bulk mutate request:
Batch processing (RPC only? async only?)
Bulk mutates (REST only? sync only?)
Other?


2) Can you please help me verify my code uses batch-processing correctly (design / flow wise) ?
3) I opened an issue there yesterday, I hope I will get an answer. 
But generally speaking ":Mutate" syntax should not require any library installation? Can you please help me verify my code uses Bulk Mutates correctly?

Unfortunately we debugged the code over the LCS client side, so I cannot see other request ids nor the logs (we configured them to verbose mode).
Will try to gather them again tomorrow.

Thanks!

--
--
=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~
Also find us on our blog:
https://googleadsdeveloper.blogspot.com/
=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~
 
You received this message because you are subscribed to the Google
Groups "AdWords API and Google Ads API Forum" group.
To post to this group, send email to adwor...@googlegroups.com
To unsubscribe from this group, send email to
adwords-api...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/adwords-api?hl=en
---
You received this message because you are subscribed to a topic in the Google Groups "AdWords API and Google Ads API Forum" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/adwords-api/U0Avc7dQZIw/unsubscribe.
To unsubscribe from this group and all its topics, send an email to adwords-api...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/adwords-api/iW32F000000000000000000000000000000000000000000000QZ3QRN00eoAu81u2RautuNT4sv3GEg%40sfdc.net.

Google Ads API Forum Advisor

unread,
Sep 10, 2021, 5:17:19 AM9/10/21
to el...@google.com, adwor...@googlegroups.com
Hi Elad,

You may refer below for my responses :


1) What are the ways to send a bulk mutate request:
Batch processing (RPC only? async only?)
Bulk mutates (REST only? sync only?)
Other?


Both the bulk mutates and batch processing can be performed using a client library or via REST. Also, batch processing is asynchronous while bulk mutates is for synchronous operations. Please go over the documentation in the links for more details.


2) Can you please help me verify my code uses batch-processing correctly (design / flow wise) ?

3) I opened an issue there yesterday, I hope I will get an answer. 
But generally speaking ":Mutate" syntax should not require any library installation? Can you please help me verify my code uses Bulk Mutates correctly?


For these two items, you may continue discussion of coding and client library related concerns with the client library owners.
Reply all
Reply to author
Forward
0 new messages