The steps for using batch processing are as follows:

## Create a new batch job

To create a [`BatchJob`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJob.md.txt) resource, call
[`MutateBatchJob`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJobService/MutateBatchJob.md.txt). When you construct the
[`BatchJob`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJob.md.txt) object, you can optionally set
[`metadata.execution_limit_seconds`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJob.BatchJobMetadata.md.txt#execution_limit_seconds) to a positive integer to
configure an approximate upper bound, in seconds, on how long the job can run
before being automatically canceled (see
[Additional usage guidance](https://developers.google.com/google-ads/api/docs/batch-processing/best-practices.md.txt#additional-usage-guidance)).


### Java

```java
private String createBatchJob(BatchJobServiceClient batchJobServiceClient, long customerId) {
  BatchJobOperation operation =
      BatchJobOperation.newBuilder().setCreate(BatchJob.newBuilder().build()).build();
  String batchJobResourceName =
      batchJobServiceClient
          .mutateBatchJob(Long.toString(customerId), operation)
          .getResult()
          .getResourceName();
  System.out.printf("Created a mutate job with resource name: '%s'.%n", batchJobResourceName);

  return batchJobResourceName;
}
      
```

### C#

```c#
private static string CreateBatchJob(BatchJobServiceClient batchJobService,
    long customerId)
{
    BatchJobOperation operation = new BatchJobOperation()
    {
        Create = new BatchJob()
        {
        }
    };
    string batchJobResourceName =
        batchJobService.MutateBatchJob(customerId.ToString(), operation)
        .Result.ResourceName;
    Console.WriteLine($"Created a batch job with resource name: " +
        $"'{batchJobResourceName}'.");

    return batchJobResourceName;
}
      
```

### PHP

```php
private static function createBatchJob(
    BatchJobServiceClient $batchJobServiceClient,
    int $customerId
): string {
    // Creates a batch job operation to create a new batch job.
    $batchJobOperation = new BatchJobOperation();
    $batchJobOperation->setCreate(new BatchJob());

    // Issues a request to the API and get the batch job's resource name.
    $batchJobResourceName = $batchJobServiceClient->mutateBatchJob(
        MutateBatchJobRequest::build($customerId, $batchJobOperation)
    )->getResult()->getResourceName();
    printf(
        "Created a batch job with resource name: '%s'.%s",
        $batchJobResourceName,
        PHP_EOL
    );
    return $batchJobResourceName;
}
      
```

### Python

```python
def create_batch_job(
    batch_job_service: BatchJobServiceClient,
    customer_id: str,
    batch_job_operation: BatchJobOperation,
) -> str:
    """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: MutateBatchJobResponse = batch_job_service.mutate_batch_job(
            customer_id=customer_id, operation=batch_job_operation
        )
        resource_name: str = 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)
        # This line will likely not be reached due to sys.exit(1) in handle_googleads_exception
        # but to satisfy the type checker, we add a return statement.
        return ""  # Or raise an exception
      
```

### Ruby

```ruby
def create_batch_job(client, batch_job_service, customer_id)
  # Creates a batch job operation to create a new batch job.
  operation = client.operation.create_resource.batch_job

  # Issues a request to the API and get the batch job's resource name.
  response = batch_job_service.mutate_batch_job(
    customer_id: customer_id,
    operation: operation
  )

  batch_job_resource_name = response.result.resource_name
  puts "Created a batch job with resource name: '#{batch_job_resource_name}'"

  batch_job_resource_name
end
      
```

### Perl

```perl
sub create_batch_job {
  my ($batch_job_service, $customer_id) = @_;

  # Create a batch job operation.
  my $batch_job_operation =
    Google::Ads::GoogleAds::V26::Services::BatchJobService::BatchJobOperation->
    new({create => Google::Ads::GoogleAds::V26::Resources::BatchJob->new({})});

  my $batch_job_resource_name = $batch_job_service->mutate({
      customerId => $customer_id,
      operation  => $batch_job_operation
    })->{result}{resourceName};

  printf
    "Created a batch job with resource name: '%s'.\n",
    $batch_job_resource_name;

  return $batch_job_resource_name;
}
      
```

### curl

```console
# Creates a batch job.
#
# Variables:
#   API_VERSION,
#   CUSTOMER_ID,
#   MANAGER_CUSTOMER_ID,
#   OAUTH2_ACCESS_TOKEN:
#     See https://developers.google.com/google-ads/api/rest/auth#request_headers
#     for details.

curl -f --request POST \
"https://googleads.googleapis.com/v${API_VERSION}/customers/${CUSTOMER_ID}/batchJobs:mutate" \
--header "Content-Type: application/json" \
--header "login-customer-id: ${MANAGER_CUSTOMER_ID}" \
--header "Authorization: Bearer ${OAUTH2_ACCESS_TOKEN}" \
--data @- <<EOF
{
  "operation": {
    "create": {}
  }
}
EOF
      
```

<br />

At this point in the process, the [`status`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJob.md.txt#status) of the job is
[`PENDING`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJobStatusEnum.BatchJobStatus.md.txt#pending).

## Add mutate operations to the batch job

Add one or more [`MutateOperation`](https://developers.google.com/google-ads/api/reference/rpc/v26/MutateOperation.md.txt) objects to the batch job
created in the previous step by calling
[`AddBatchJobOperations`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJobService/AddBatchJobOperations.md.txt). The
[response](https://developers.google.com/google-ads/api/reference/rpc/v26/AddBatchJobOperationsResponse.md.txt) then contains the following:

- [`total_operations`](https://developers.google.com/google-ads/api/reference/rpc/v26/AddBatchJobOperationsResponse.md.txt#total_operations): Total number of operations added so far for this job.
- [`next_sequence_token`](https://developers.google.com/google-ads/api/reference/rpc/v26/AddBatchJobOperationsResponse.md.txt#next_sequence_token): Sequence token to pass in the [`sequence_token`](https://developers.google.com/google-ads/api/reference/rpc/v26/AddBatchJobOperationsRequest.md.txt#sequence_token) field when you call this method again to add more operations.

The first [`AddBatchJobOperations`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJobService/AddBatchJobOperations.md.txt) request for a
batch job must omit [`sequence_token`](https://developers.google.com/google-ads/api/reference/rpc/v26/AddBatchJobOperationsRequest.md.txt#sequence_token). When you call
[`AddBatchJobOperations`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJobService/AddBatchJobOperations.md.txt) again to add more
operations, specify the previously obtained sequence token in the
[`sequence_token`](https://developers.google.com/google-ads/api/reference/rpc/v26/AddBatchJobOperationsRequest.md.txt#sequence_token) field of the
[request](https://developers.google.com/google-ads/api/reference/rpc/v26/AddBatchJobOperationsRequest.md.txt). Calling the method with an empty
[`mutate_operations`](https://developers.google.com/google-ads/api/reference/rpc/v26/AddBatchJobOperationsRequest.md.txt#mutate_operations) list returns a
[`BatchJobError.EMPTY_OPERATIONS`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJobErrorEnum.BatchJobError.md.txt#empty_operations) error, calling it with any
sequence token other than the previously obtained one returns a
[`BatchJobError.INVALID_SEQUENCE_TOKEN`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJobErrorEnum.BatchJobError.md.txt#invalid_sequence_token) error, and
attempting to add operations after the job starts running returns a
[`BatchJobError.CANNOT_MODIFY_JOB_AFTER_JOB_STARTS_RUNNING`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJobErrorEnum.BatchJobError.md.txt#cannot_modify_job_after_job_starts_running)
error.

While the batch job's [`status`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJob.md.txt#status) is
[`PENDING`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJobStatusEnum.BatchJobStatus.md.txt#pending), `sequence_token` is also available as
[`next_add_sequence_token`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJob.md.txt#next_add_sequence_token) on the
[`BatchJob`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJob.md.txt) resource, which you can [retrieve](https://developers.google.com/google-ads/api/docs/concepts/retrieving-objects.md.txt)
later.

If you're creating dependent objects such as a complete campaign consisting of a
new campaign and corresponding ad groups, ads, and keywords, you can use
[temporary IDs](https://developers.google.com/google-ads/api/docs/batch-processing/temporary-ids.md.txt) to specify a resource name.


### Java

```java
private void addAllBatchJobOperations(
    BatchJobServiceClient batchJobServiceClient, long customerId, String batchJobResourceName) {
  AddBatchJobOperationsResponse response =
      batchJobServiceClient.addBatchJobOperations(
          AddBatchJobOperationsRequest.newBuilder()
              .setResourceName(batchJobResourceName)
              .addAllMutateOperations(buildAllOperations(customerId))
              .build());
  System.out.printf(
      "%d mutate operations have been added so far.%n", response.getTotalOperations());

  // You can use this next sequence token for calling addBatchJobOperations() next time.
  System.out.printf(
      "Next sequence token for adding next operations is '%s'.%n",
      response.getNextSequenceToken());
}
      
```

### C#

```c#
private static void AddAllBatchJobOperations(BatchJobServiceClient batchJobService,
    long customerId, string batchJobResourceName)
{
    AddBatchJobOperationsResponse response =
        batchJobService.AddBatchJobOperations(
            new AddBatchJobOperationsRequest()
            {
                ResourceName = batchJobResourceName,
                MutateOperations = { BuildAllOperations(customerId) }
            });
    Console.WriteLine($"{response.TotalOperations} mutate operations have been added" +
        $" so far.");

    // You can use this next sequence token for calling AddBatchJobOperations() next time.
    Console.WriteLine($"Next sequence token for adding next operations is " +
        $"'{response.NextSequenceToken}'.");
}
      
```

### PHP

```php
private static function addAllBatchJobOperations(
    BatchJobServiceClient $batchJobServiceClient,
    int $customerId,
    string $batchJobResourceName
): void {
    $response = $batchJobServiceClient->addBatchJobOperations(
        AddBatchJobOperationsRequest::build(
            $batchJobResourceName,
            '',
            self::buildAllOperations($customerId)
        )
    );
    printf(
        "%d mutate operations have been added so far.%s",
        $response->getTotalOperations(),
        PHP_EOL
    );
    // You can use this next sequence token for calling addBatchJobOperations() next time.
    printf(
        "Next sequence token for adding next operations is '%s'.%s",
        $response->getNextSequenceToken(),
        PHP_EOL
    );
}
      
```

### Python

```python
def add_all_batch_job_operations(
    batch_job_service: BatchJobServiceClient,
    operations: List[MutateOperation],
    resource_name: str,
) -> None:
    """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: AddBatchJobOperationsResponse = (
            batch_job_service.add_batch_job_operations(
                resource_name=resource_name,
                sequence_token=None,  # type: ignore
                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)
      
```

### Ruby

```ruby
def add_all_batch_job_operations(
  client,
  batch_job_service,
  customer_id,
  batch_job_resource_name)
  response = batch_job_service.add_batch_job_operations(
    resource_name: batch_job_resource_name,
    mutate_operations: build_all_operations(client, customer_id),
  )
  puts "#{response.total_operations} mutate operations have been added so far."

  # You can use this next sequence token for calling
  # add_all_batch_job_operations() next time
  puts "Next sequence token for adding next operations is " \
    "'#{response.next_sequence_token}'"
end
      
```

### Perl

```perl
sub add_all_batch_job_operations {
  my ($batch_job_service, $customer_id, $batch_job_resource_name) = @_;

  my $add_batch_job_operations_response = $batch_job_service->add_operations({
      resourceName     => $batch_job_resource_name,
      sequenceToken    => undef,
      mutateOperations => build_all_operations($customer_id)});

  printf
    "%d batch operations have been added so far.\n",
    $add_batch_job_operations_response->{totalOperations};

  # You can use this next sequence token for calling add_operations() next time.
  printf
    "Next sequence token for adding next operations is '%s'.\n",
    $add_batch_job_operations_response->{nextSequenceToken};
}
      
```

### curl

```console
# Adds operations to a batch job.
#
# Variables:
#   API_VERSION,
#   CUSTOMER_ID,
#   MANAGER_CUSTOMER_ID,
#   OAUTH2_ACCESS_TOKEN:
#     See https://developers.google.com/google-ads/api/rest/auth#request_headers
#     for details.
#   BATCH_JOB_RESOURCE_NAME:
#     The resource name of the batch job to which the operations should be added
#     as returned by the previous step.

curl -f --request POST \
"https://googleads.googleapis.com/v${API_VERSION}/${BATCH_JOB_RESOURCE_NAME}:addOperations" \
--header "Content-Type: application/json" \
--header "login-customer-id: ${MANAGER_CUSTOMER_ID}" \
--header "Authorization: Bearer ${OAUTH2_ACCESS_TOKEN}" \
--data @- <<EOF
{
  "mutateOperations": [
    {
      "campaignBudgetOperation": {
        "create": {
          "resourceName": "customers/${CUSTOMER_ID}/campaignBudgets/-1",
          "name": "batch job budget #${RANDOM}",
          "deliveryMethod": "STANDARD",
          "amountMicros": 5000000
        }
      }
    },
    {
      "campaignOperation": {
        "create": {
          "advertisingChannelType": "SEARCH",
          "status": "PAUSED",
          "name": "batch job campaign #${RANDOM}",
          "campaignBudget": "customers/${CUSTOMER_ID}/campaignBudgets/-1",
          "resourceName": "customers/${CUSTOMER_ID}/campaigns/-2",
          "manualCpc": {
          }
        }
      },
    }
  ]
}
EOF
      
```

<br />

Click to see the content of the build operations function in GitHub for your
client library:

### Java

`
buildAllOperations()
`

### C#

`
BuildAllOperations()
`

### PHP

`
buildAllOperations()
`

### Python

`
build_all_operations()
`

### Ruby

`
build_all_operations()
`

### Perl

`
build_all_operations
`

> [!TIP]
> **Tip:** The order of your operations can have a significant impact on your job's performance. Check out the tip on operation order in [Best practices](https://developers.google.com/google-ads/api/docs/batch-processing/best-practices.md.txt).

## Run the batch job

After you add all your operations, you can request the Google Ads API to run the batch
job by calling [`RunBatchJob`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJobService/RunBatchJob.md.txt) on the uploaded operations.
Calling [`RunBatchJob`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJobService/RunBatchJob.md.txt) after the job has already started running
returns a
[`BatchJobError.CANNOT_MODIFY_JOB_AFTER_JOB_STARTS_RUNNING`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJobErrorEnum.BatchJobError.md.txt#cannot_modify_job_after_job_starts_running)
error.


### Java

```java
private OperationFuture runBatchJob(
    BatchJobServiceClient batchJobServiceClient, String batchJobResourceName) {
  OperationFuture operationResponse =
      batchJobServiceClient.runBatchJobAsync(batchJobResourceName);

  // BEWARE! The above call returns an OperationFuture. The execution of that future depends on
  // the thread pool which is owned by batchJobServiceClient. If you use this future, you *must*
  // keep the service client in scope too.
  // See https://developers.google.com/google-ads/api/docs/client-libs/java/lro for more detail.

  System.out.printf(
      "Mutate job with resource name '%s' has been executed.%n", batchJobResourceName);

  return operationResponse;
}
      
```

### C#

```c#
private Operation<Empty, BatchJobMetadata> RunBatchJob(
    BatchJobServiceClient batchJobService, string batchJobResourceName)
{
    Operation<Empty, BatchJobMetadata> operationResponse =
        batchJobService.RunBatchJob(batchJobResourceName);
    Console.WriteLine($"Batch job with resource name '{batchJobResourceName}' has been " +
        $"executed.");

    return operationResponse;
}
      
```

### PHP

```php
private static function runBatchJob(
    BatchJobServiceClient $batchJobServiceClient,
    string $batchJobResourceName
): OperationResponse {
    $operationResponse =
        $batchJobServiceClient->runBatchJob(RunBatchJobRequest::build($batchJobResourceName));
    printf(
        "Batch job with resource name '%s' has been executed.%s",
        $batchJobResourceName,
        PHP_EOL
    );
    return $operationResponse;
}
      
```

### Python

```python
def run_batch_job(
    batch_job_service: BatchJobServiceClient, resource_name: str
) -> Operation:
    """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: Operation = 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)
        # This line will likely not be reached due to sys.exit(1) in handle_googleads_exception
        # but to satisfy the type checker, we add a return statement.
        # In a real application, you might want to return a dummy Operation or raise an error.
        return Operation(
            op_type_name="type.googleapis.com/google.protobuf.Empty",
            complete=True,
            done_callbacks=[],
            metadata_type=None,
            result_type=None,
        )  # type: ignore
      
```

### Ruby

```ruby
def run_batch_job(batch_job_service, batch_job_resource_name)
  operation_response = batch_job_service.run_batch_job(
    resource_name: batch_job_resource_name,
  )
  puts "Batch job with resource name '#{batch_job_resource_name}' " \
    "has been executed."
  operation_response
end
      
```

### Perl

```perl
sub run_batch_job {
  my ($batch_job_service, $batch_job_resource_name) = @_;

  my $batch_job_lro =
    $batch_job_service->run({resourceName => $batch_job_resource_name});

  printf
    "Batch job with resource name '%s' has been executed.\n",
    $batch_job_resource_name;

  return $batch_job_lro;
}
      
```

### curl

```console
# Runs a batch job.
#
# Variables:
#   API_VERSION,
#   CUSTOMER_ID,
#   MANAGER_CUSTOMER_ID,
#   OAUTH2_ACCESS_TOKEN:
#     See https://developers.google.com/google-ads/api/rest/auth#request_headers
#     for details.
#   BATCH_JOB_RESOURCE_NAME:
#     The resource name of the batch job to run as returned by the previous step.

curl -f --request POST \
"https://googleads.googleapis.com/v${API_VERSION}/${BATCH_JOB_RESOURCE_NAME}:run" \
--header "Content-Type: application/json" \
--header "login-customer-id: ${MANAGER_CUSTOMER_ID}" \
--header "Authorization: Bearer ${OAUTH2_ACCESS_TOKEN}" \
--data @- <<EOF
{}
EOF

      
```

<br />

The returned response is a [long-running `Operation`](https://developers.google.com/google-ads/api/reference/rpc/google.longrunning.md.txt#google.longrunning.Operation) object that
contains the metadata of your batch job and information about the job status.

## Poll the status of the batch job until it's done

Poll the batch job status with the
[`Operations.GetOperation`](https://developers.google.com/google-ads/api/reference/rpc/google.longrunning.md.txt#google.longrunning.Operations.GetOperation) method on the
[`google.longrunning.Operations`](https://developers.google.com/google-ads/api/reference/rpc/google.longrunning.md.txt#google.longrunning.Operations) service (separate from
[`BatchJobService`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJobService.md.txt)) until the
[`Operation.done`](https://developers.google.com/google-ads/api/reference/rpc/google.longrunning.md.txt#google.longrunning.Operation.FIELDS.bool.google.longrunning.Operation.done) field is `true`. In the Google Ads API,
[`Operations.GetOperation`](https://developers.google.com/google-ads/api/reference/rpc/google.longrunning.md.txt#google.longrunning.Operations.GetOperation) is the only supported method on
[`google.longrunning.Operations`](https://developers.google.com/google-ads/api/reference/rpc/google.longrunning.md.txt#google.longrunning.Operations) (`WaitOperation`,
`ListOperations`, `CancelOperation`, and `DeleteOperation` return
`UNIMPLEMENTED`):

- **Client libraries** : Call the polling or wait helper on the long-running operation wrapper returned by [`RunBatchJob`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJobService/RunBatchJob.md.txt) (or pass the operation name to `OperationService::poll_until_done` in the REST-based Perl client library), which polls [`Operations.GetOperation`](https://developers.google.com/google-ads/api/reference/rpc/google.longrunning.md.txt#google.longrunning.Operations.GetOperation) until the operation completes (or until the configured poll timeout expires; in Perl, verify that `done` is `true` on the returned hash before listing results). In Java, keep `BatchJobServiceClient` open until [`OperationFuture`](https://developers.google.com/google-ads/api/docs/client-libs/java/lro.md.txt) finishes.
- **REST** : Send an HTTP `GET` request to `https://googleads.googleapis.com/v26/OPERATION_NAME` (where `OPERATION_NAME` is the [`Operation.name`](https://developers.google.com/google-ads/api/reference/rpc/google.longrunning.md.txt#google.longrunning.Operation.FIELDS.string.google.longrunning.Operation.name) resource name, formatted as `customers/CUSTOMER_ID/operations/OPERATION_ID`) with exponential backoff until the [`Operation.done`](https://developers.google.com/google-ads/api/reference/rpc/google.longrunning.md.txt#google.longrunning.Operation.FIELDS.bool.google.longrunning.Operation.done) field is `true`.


### Java

```java
private void pollBatchJob(OperationFuture operationResponse) {
  try {
    operationResponse.get(MAX_TOTAL_POLL_INTERVAL_SECONDS, TimeUnit.SECONDS);
  } catch (InterruptedException | ExecutionException | TimeoutException e) {
    System.err.printf("Failed polling the mutate job. Exception: %s%n", e);
    System.exit(1);
  }
}
      
```

### C#

```c#
private static void PollBatchJob(Operation<Empty, BatchJobMetadata> operationResponse)
{
    PollSettings pollSettings = new PollSettings(
        Expiration.FromTimeout(TimeSpan.FromSeconds(MAX_TOTAL_POLL_INTERVAL_SECONDS)),
        TimeSpan.FromSeconds(1));
    operationResponse.PollUntilCompleted(pollSettings);
}
      
```

### PHP

```php
private static function pollBatchJob(OperationResponse $operationResponse): void
{
    $operationResponse->pollUntilComplete(
        [
        'initialPollDelayMillis' => self::POLL_FREQUENCY_SECONDS * 1000,
        'totalPollTimeoutMillis' => self::MAX_TOTAL_POLL_INTERVAL_SECONDS * 1000
        ]
    );
}
      
```

### Python

```python
def poll_batch_job(
    operations_response: Operation, event: asyncio.Event
) -> None:
    """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.AbstractEventLoop = asyncio.get_event_loop()

    def done_callback(future: Coroutine[Any, Any, Any]) -> None:
        # 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.
    # See: https://googleapis.dev/python/google-api-core/latest/operation.html
    operations_response.add_done_callback(done_callback)  # type: ignore
      
```

### Ruby

```ruby
def poll_batch_job(operation_response)
  operation_response.wait_until_done!
end
      
```

### Perl

```perl
sub poll_batch_job {
  my ($operation_service, $batch_job_lro) = @_;

  $operation_service->poll_until_done({
    name                 => $batch_job_lro->{name},
    pollFrequencySeconds => POLL_FREQUENCY_SECONDS,
    pollTimeoutSeconds   => POLL_TIMEOUT_SECONDS
  });
}
      
```

### curl

```console
# Gets the status of a batch job.
#
# Variables:
#   API_VERSION,
#   CUSTOMER_ID,
#   MANAGER_CUSTOMER_ID,
#   OAUTH2_ACCESS_TOKEN:
#     See https://developers.google.com/google-ads/api/rest/auth#request_headers
#     for details.
#   BATCH_JOB_OPERATION_NAME:
#     The operation name of the running batch job as returned by the previous
#     step.

curl -f --request GET \
"https://googleads.googleapis.com/v${API_VERSION}/${BATCH_JOB_OPERATION_NAME}" \
--header "Content-Type: application/json" \
--header "login-customer-id: ${MANAGER_CUSTOMER_ID}" \
--header "Authorization: Bearer ${OAUTH2_ACCESS_TOKEN}" \

      
```

<br />

Alternatively, query the [`batch_job`](https://developers.google.com/google-ads/api/fields/v26/batch_job.md.txt) resource with the
[`GoogleAdsService.Search`](https://developers.google.com/google-ads/api/reference/rpc/v26/GoogleAdsService/Search.md.txt) or
[`GoogleAdsService.SearchStream`](https://developers.google.com/google-ads/api/reference/rpc/v26/GoogleAdsService/SearchStream.md.txt) method, selecting
[`batch_job.status`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJob.md.txt#status) and specific
[`BatchJobMetadata`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJob.BatchJobMetadata.md.txt) subfields (such as
[`batch_job.metadata.estimated_completion_ratio`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJob.BatchJobMetadata.md.txt#estimated_completion_ratio) and
[`batch_job.metadata.executed_operation_count`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJob.BatchJobMetadata.md.txt#executed_operation_count)) until
[`batch_job.status`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJob.md.txt#status) is [`DONE`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJobStatusEnum.BatchJobStatus.md.txt#done). Each
[`Operations.GetOperation`](https://developers.google.com/google-ads/api/reference/rpc/google.longrunning.md.txt#google.longrunning.Operations.GetOperation) or non-paginated GAQL request
counts as one operation toward your [daily API quota](https://developers.google.com/google-ads/api/docs/best-practices/quotas.md.txt); when you monitor
multiple concurrent batch jobs, a single GAQL query (`WHERE batch_job.id IN
(...)` or `WHERE batch_job.status = 'RUNNING'`) checks all of them in one
request instead of polling each long-running operation individually.

When [`Operation.done`](https://developers.google.com/google-ads/api/reference/rpc/google.longrunning.md.txt#google.longrunning.Operation.FIELDS.bool.google.longrunning.Operation.done) is `true`, the long-running
operation returns an empty response without per-operation errors---call
[`ListBatchJobResults`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJobService/ListBatchJobResults.md.txt) to inspect individual operation
outcomes.

### Retrieve an existing long-running operation

If your application polls batch jobs from a separate process or needs to resume
polling after a restart, you can retrieve the long-running operation name using
[`GoogleAdsService.Search`](https://developers.google.com/google-ads/api/reference/rpc/v26/GoogleAdsService/Search.md.txt) or
[`GoogleAdsService.SearchStream`](https://developers.google.com/google-ads/api/reference/rpc/v26/GoogleAdsService/SearchStream.md.txt):

1. Query the [`batch_job`](https://developers.google.com/google-ads/api/fields/v26/batch_job.md.txt) resource to read its
   [`long_running_operation`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJob.md.txt#long_running_operation) field:

   ```sql
   SELECT
     batch_job.id,
     batch_job.status,
     batch_job.long_running_operation
   FROM batch_job
   WHERE batch_job.id = BATCH_JOB_ID
   ```

   Replace `BATCH_JOB_ID` with the numeric ID of the batch job.
2. Verify that [`batch_job.status`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJob.md.txt#status) is
   [`RUNNING`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJobStatusEnum.BatchJobStatus.md.txt#running) or [`DONE`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJobStatusEnum.BatchJobStatus.md.txt#done)
   ([`long_running_operation`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJob.md.txt#long_running_operation) is unset while the job is
   [`PENDING`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJobStatusEnum.BatchJobStatus.md.txt#pending) because no long-running operation
   exists until [`RunBatchJob`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJobService/RunBatchJob.md.txt) is called), and read the
   [`long_running_operation`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJob.md.txt#long_running_operation) string from the returned
   [`BatchJob`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJob.md.txt) object.

3. Poll or inspect the operation by using that operation name string:

   - **Client libraries** : Obtain the operations client from your `BatchJobService` client (`getOperationsClient()` in Java and PHP, `RunBatchJobOperationsClient` in C#, `transport.operations_client` in Python, `operations_client` in Ruby, or `$api_client->OperationService()` in Perl) so that Google Ads authentication and `login-customer-id` headers are configured automatically, instead of instantiating a standalone `OperationsClient`. Call [`Operations.GetOperation`](https://developers.google.com/google-ads/api/reference/rpc/google.longrunning.md.txt#google.longrunning.Operations.GetOperation) (or `OperationService::get` in Perl) for a single status check, or use the operation name to resume a long-running operation wrapper and poll until completion (`runBatchJobOperationCallable().resumeFutureCall` in Java, `PollOnceRunBatchJob` in C#, `resumeOperation` in PHP, `google.api_core.operation.from_gapic` on the `Operation` returned by `get_operation` in Python, `wait_until_done!` on the `Gapic::Operation` returned by `get_operation` in Ruby, or `name` in `OperationService::poll_until_done` in Perl).
   - **REST** : Send an HTTP `GET` request to `https://googleads.googleapis.com/v26/OPERATION_NAME`.

   Because `OPERATION_NAME` encodes the API major version in which the
   [`BatchJob`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJob.md.txt) was created,
   [`Operations.GetOperation`](https://developers.google.com/google-ads/api/reference/rpc/google.longrunning.md.txt#google.longrunning.Operations.GetOperation) (even through the active
   `v26` REST endpoint) fails with a
   [`RequestError.UNSUPPORTED_VERSION`](https://developers.google.com/google-ads/api/reference/rpc/v26/RequestErrorEnum.RequestError.md.txt#unsupported_version) error if that
   version has since sunset. Query [`batch_job.status`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJob.md.txt#status)
   directly with `GoogleAdsService` instead.

### Understand operation metadata

When you poll the long-running operation or query the
[`batch_job`](https://developers.google.com/google-ads/api/fields/v26/batch_job.md.txt) resource,
[`BatchJobMetadata`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJob.BatchJobMetadata.md.txt) provides details about the batch job's
progress:

- [`creation_date_time`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJob.BatchJobMetadata.md.txt#creation_date_time): The timestamp when the batch job was created.
- [`start_date_time`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJob.BatchJobMetadata.md.txt#start_date_time): The timestamp when the batch job started running (populated when you query `batch_job.metadata.start_date_time` with `GoogleAdsService`; omitted from `Operation.metadata` in [`RunBatchJob`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJobService/RunBatchJob.md.txt) and [`Operations.GetOperation`](https://developers.google.com/google-ads/api/reference/rpc/google.longrunning.md.txt#google.longrunning.Operations.GetOperation)).
- [`completion_date_time`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJob.BatchJobMetadata.md.txt#completion_date_time): The timestamp when the batch job finished (only present if the job is done).
- [`estimated_completion_ratio`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJob.BatchJobMetadata.md.txt#estimated_completion_ratio): An estimate of the fraction of the job (from 0.0 to 1.0) that has been completed.
- [`operation_count`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJob.BatchJobMetadata.md.txt#operation_count): The total number of operations in the batch job.
- [`executed_operation_count`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJob.BatchJobMetadata.md.txt#executed_operation_count): The number of operations that have been executed so far.
- [`execution_limit_seconds`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJob.BatchJobMetadata.md.txt#execution_limit_seconds): The approximate upper bound, in seconds, for how long the batch job can run before being canceled (populated when you query `batch_job.metadata.execution_limit_seconds` with `GoogleAdsService` if a positive limit was set at job creation; omitted from `Operation.metadata` in [`RunBatchJob`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJobService/RunBatchJob.md.txt) and [`Operations.GetOperation`](https://developers.google.com/google-ads/api/reference/rpc/google.longrunning.md.txt#google.longrunning.Operations.GetOperation)).

These fields, especially [`estimated_completion_ratio`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJob.BatchJobMetadata.md.txt#estimated_completion_ratio)
and the operation counts, can help you gauge the job's progress, estimate
remaining time, and adjust your polling frequency. For example, you might poll
less frequently when the [`estimated_completion_ratio`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJob.BatchJobMetadata.md.txt#estimated_completion_ratio)
is low, and more frequently as it approaches 1.0.

## List all batch job results

When the batch job finishes, call
[`ListBatchJobResults`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJobService/ListBatchJobResults.md.txt) to retrieve the status and
response for each operation in the job:


### Java

```java
private void fetchAndPrintResults(
    BatchJobServiceClient batchJobServiceClient, String batchJobResourceName) {
  System.out.printf(
      "Mutate job with resource name '%s' has finished. Now, printing its results...%n",
      batchJobResourceName);
  // Gets all the results from running mutate job and prints their information.
  ListBatchJobResultsPagedResponse batchJobResults =
      batchJobServiceClient.listBatchJobResults(
          ListBatchJobResultsRequest.newBuilder()
              .setResourceName(batchJobResourceName)
              .setPageSize(PAGE_SIZE)
              .build());
  for (BatchJobResult batchJobResult : batchJobResults.iterateAll()) {
    System.out.printf(
        "Mutate job #%d has a status '%s' and response of type '%s'.%n",
        batchJobResult.getOperationIndex(),
        batchJobResult.getStatus().getMessage().isEmpty()
            ? "N/A"
            : batchJobResult.getStatus().getMessage(),
        batchJobResult
                .getMutateOperationResponse()
                .getResponseCase()
                .equals(ResponseCase.RESPONSE_NOT_SET)
            ? "N/A"
            : batchJobResult.getMutateOperationResponse().getResponseCase());
  }
}
      
```

### C#

```c#
private static void FetchAndPrintResults(BatchJobServiceClient batchJobService,
    string batchJobResourceName)
{
    Console.WriteLine($"batch job with resource name '{batchJobResourceName}' has " +
        $"finished. Now, printing its results...");

    ListBatchJobResultsRequest request = new ListBatchJobResultsRequest()
    {
        ResourceName = batchJobResourceName,
        PageSize = PAGE_SIZE,
    };
    ListBatchJobResultsResponse resp = new ListBatchJobResultsResponse();
    // Gets all the results from running batch job and prints their information.
    foreach (BatchJobResult batchJobResult in
        batchJobService.ListBatchJobResults(request))
    {
        if (!batchJobResult.IsFailed)
        {
            Console.WriteLine($"batch job result #{batchJobResult.OperationIndex} is " +
                $"successful and response is of type " +
                $"'{batchJobResult.MutateOperationResponse.ResponseCase}'.");
        }
        else
        {
            Console.WriteLine($"batch job result #{batchJobResult.OperationIndex} " +
                $"failed with error message {batchJobResult.Status.Message}.");

            foreach (GoogleAdsError error in batchJobResult.Failure.Errors)
            {
                Console.WriteLine($"Error found: {error}.");
            }
        }
    }
}
      
```

### PHP

```php
    private static function fetchAndPrintResults(
        BatchJobServiceClient $batchJobServiceClient,
        string $batchJobResourceName
    ): void {
        printf(
            "Batch job with resource name '%s' has finished. Now, printing its results...%s",
            $batchJobResourceName,
            PHP_EOL
        );
        // Gets all the results from running batch job and print their information.
        $batchJobResults = $batchJobServiceClient->listBatchJobResults(
            ListBatchJobResultsRequest::build($batchJobResourceName)->setPageSize(self::PAGE_SIZE)
        );
        foreach ($batchJobResults->iterateAllElements() as $batchJobResult) {
            /**
 * @var BatchJobResult $batchJobResult
*/
            printf(
                "Batch job #%d has a status '%s' and response of type '%s'.%s",
                $batchJobResult->getOperationIndex(),
                $batchJobResult->getStatus()
                    ? $batchJobResult->getStatus()->getMessage() : 'N/A',
                $batchJobResult->getMutateOperationResponse()
                    ? $batchJobResult->getMutateOperationResponse()->getResponse()
                    : 'N/A',
                PHP_EOL
            );
        }
    }
      
```

### Python

```python
def fetch_and_print_results(
    client: GoogleAdsClient,
    batch_job_service: BatchJobServiceClient,
    resource_name: str,
) -> None:
    """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: ListBatchJobResultsRequest = client.get_type(
        "ListBatchJobResultsRequest"
    )
    list_results_request.resource_name = resource_name
    list_results_request.page_size = 1000
    # Gets all the results from running batch job and prints their information.
    batch_job_results: ListBatchJobResultsResponse = (
        batch_job_service.list_batch_job_results(request=list_results_request)
    )

    for batch_job_result in batch_job_results:
        status: str = batch_job_result.status.message
        status = status if status else "N/A"
        result: Any = 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}"'
        )
      
```

### Ruby

```ruby
def fetch_and_print_results(batch_job_service, batch_job_resource_name)
  puts "Batch job with resource name '#{batch_job_resource_name}' has " \
    "finished. Now, printing its results..." \

  # Gets all the results from running batch job and print their information.
  batch_job_results = batch_job_service.list_batch_job_results(
    resource_name: batch_job_resource_name,
    page_size: PAGE_SIZE,
  )
  batch_job_results.each do |result|
    puts "Batch job ##{result.operation_index} has a status " \
      "#{result.status ? result.status.message : 'N/A'} and response of type " \
      "#{result.mutate_operation_response ? result.mutate_operation_response.response : 'N/A'}"
  end
end
      
```

### Perl

```perl
sub fetch_and_print_results {
  my ($batch_job_service, $batch_job_resource_name) = @_;

  printf "Batch job with resource name '%s' has finished. " .
    "Now, printing its results...\n", $batch_job_resource_name;

  # Get all the results from running batch job and print their information.
  my $list_batch_job_results_response = $batch_job_service->list_results({
    resourceName => $batch_job_resource_name,
    pageSize     => PAGE_SIZE
  });

  foreach my $batch_job_result (@{$list_batch_job_results_response->{results}})
  {
    printf
      "Batch job #%d has a status '%s' and response of type '%s'.\n",
      $batch_job_result->{operationIndex},
      $batch_job_result->{status} ? $batch_job_result->{status}{message}
      : "N/A",
      $batch_job_result->{mutateOperationResponse}
      ? [keys %{$batch_job_result->{mutateOperationResponse}}]->[0]
      : "N/A";
  }
}
      
```

### curl

```console
# Gets the results of a batch job.
#
# Variables:
#   API_VERSION,
#   CUSTOMER_ID,
#   MANAGER_CUSTOMER_ID,
#   OAUTH2_ACCESS_TOKEN:
#     See https://developers.google.com/google-ads/api/rest/auth#request_headers
#     for details.
#   BATCH_JOB_RESOURCE_NAME:
#     The operation name of the running batch job as returned by the previous
#     step.
curl -f --request GET \
"https://googleads.googleapis.com/v${API_VERSION}/${BATCH_JOB_RESOURCE_NAME}:listResults?pageSize=1000" \
--header "Content-Type: application/json" \
--header "login-customer-id: ${MANAGER_CUSTOMER_ID}" \
--header "Authorization: Bearer ${OAUTH2_ACCESS_TOKEN}"

      
```

<br />

In [`ListBatchJobResultsRequest`](https://developers.google.com/google-ads/api/reference/rpc/v26/ListBatchJobResultsRequest.md.txt), the
[`page_size`](https://developers.google.com/google-ads/api/reference/rpc/v26/ListBatchJobResultsRequest.md.txt#page_size) field defaults to the maximum value of `1000` if
omitted or set to `0`. Setting [`page_size`](https://developers.google.com/google-ads/api/reference/rpc/v26/ListBatchJobResultsRequest.md.txt#page_size) to a value greater than
`1000` or less than `0` returns a
[`BatchJobError.INVALID_PAGE_SIZE`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJobErrorEnum.BatchJobError.md.txt#invalid_page_size) error.

Each [`BatchJobResult`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJobResult.md.txt) identifies the corresponding operation
using [`operation_index`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJobResult.md.txt#operation_index), the 0-based index of the
uploaded [`MutateOperation`](https://developers.google.com/google-ads/api/reference/rpc/v26/MutateOperation.md.txt) in the batch job.

- **Successful operation** : [`mutate_operation_response`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJobResult.md.txt#mutate_operation_response) is populated (its [`response`](https://developers.google.com/google-ads/api/reference/rpc/v26/MutateOperationResponse.md.txt#response) contains the mutated resource's `resource_name`, plus all mutable fields if [`response_content_type`](https://developers.google.com/google-ads/api/reference/rpc/v26/ListBatchJobResultsRequest.md.txt#response_content_type) is set to [`MUTABLE_RESOURCE`](https://developers.google.com/google-ads/api/reference/rpc/v26/ResponseContentTypeEnum.ResponseContentType.md.txt#mutable_resource)), and [`status`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJobResult.md.txt#status) is unset.
- **Failed (or unexecuted) operation** : [`mutate_operation_response`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJobResult.md.txt#mutate_operation_response) is unset, and [`status`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJobResult.md.txt#status) contains the error details for the operation.

> [!IMPORTANT]
> **Key Point:** If you call [`ListBatchJobResults`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJobService/ListBatchJobResults.md.txt) before you call [`RunBatchJob`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJobService/RunBatchJob.md.txt) or before the batch job finishes, the API returns a [`BatchJobError.RESULTS_NOT_READY`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJobErrorEnum.BatchJobError.md.txt#results_not_ready) error. If unexpected server checkpoint errors prevent the batch job results from being listed, the API returns a [`BatchJobError.CANNOT_LIST_RESULTS`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJobErrorEnum.BatchJobError.md.txt#cannot_list_results) error.

## Cancel or remove a batch job

How you stop or discard a batch job depends on whether
[`RunBatchJob`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJobService/RunBatchJob.md.txt) has been called:

- **Before you call [`RunBatchJob`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJobService/RunBatchJob.md.txt)** : While the job's [`status`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJob.md.txt#status) is [`PENDING`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJobStatusEnum.BatchJobStatus.md.txt#pending), you can discard the batch job by calling [`MutateBatchJob`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJobService/MutateBatchJob.md.txt) with [`BatchJobOperation.remove`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJobOperation.md.txt#remove). Calling `remove` after the job starts running returns a [`BatchJobError.CAN_ONLY_REMOVE_PENDING_JOB`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJobErrorEnum.BatchJobError.md.txt#can_only_remove_pending_job) error.
- **After you call [`RunBatchJob`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJobService/RunBatchJob.md.txt)** : You cannot manually cancel a running batch job ([`Operations.CancelOperation`](https://developers.google.com/google-ads/api/reference/rpc/google.longrunning.md.txt#google.longrunning.Operations.CancelOperation) is not implemented in the Google Ads API and returns `UNIMPLEMENTED`). To bound execution time so a job cancels automatically if it runs too long, set [`metadata.execution_limit_seconds`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJob.BatchJobMetadata.md.txt#execution_limit_seconds) to a positive integer (an approximate upper bound) when you create the job in [`MutateBatchJob`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJobService/MutateBatchJob.md.txt). Because standard operations in a batch job execute with [partial failure](https://developers.google.com/google-ads/api/docs/best-practices/partial-failures.md.txt) enabled (operations in [atomic sub-batches](https://developers.google.com/google-ads/api/docs/batch-processing/best-practices.md.txt#atomicity-in-batch-splitting) are rolled back together), automatic cancellation does not roll back operations that have already completed. Once a canceled (or aborted) job's [`status`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJob.md.txt#status) transitions to [`DONE`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJobStatusEnum.BatchJobStatus.md.txt#done), calling [`ListBatchJobResults`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJobService/ListBatchJobResults.md.txt) returns [`BatchJobResult`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJobResult.md.txt) entries for the executed prefix ([`operation_index`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJobResult.md.txt#operation_index) `<` [`executed_operation_count`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJob.BatchJobMetadata.md.txt#executed_operation_count)) with their individual execution outcomes, followed by [`BatchJobResult`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJobResult.md.txt) entries for all remaining unexecuted operations ([`operation_index`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJobResult.md.txt#operation_index) from [`executed_operation_count`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJob.BatchJobMetadata.md.txt#executed_operation_count) through [`operation_count`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJob.BatchJobMetadata.md.txt#operation_count) `- 1`) with [`mutate_operation_response`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJobResult.md.txt#mutate_operation_response) unset and [`status`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJobResult.md.txt#status) set to `ABORTED` with [`InternalError.INTERNAL_ERROR`](https://developers.google.com/google-ads/api/reference/rpc/v26/InternalErrorEnum.InternalError.md.txt#internal_error) when canceled due to [`execution_limit_seconds`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJob.BatchJobMetadata.md.txt#execution_limit_seconds) (or to the job-level error status if the job aborted due to a fatal execution error).

## Error handling

[`BatchJobService`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJobService.md.txt) executes standard operations with
[partial failure](https://developers.google.com/google-ads/api/docs/best-practices/partial-failures.md.txt) enabled (successful operations commit even
if other operations in the same job fail, except within
[atomic sub-batches](https://developers.google.com/google-ads/api/docs/batch-processing/best-practices.md.txt#atomicity-in-batch-splitting)) and automatically retries
operations that fail due to transient errors. However, not all failure scenarios
can be avoided. Operations that failed due to validation errors can be fixed and
resubmitted in a new batch job. Likewise, when a batch job is
[canceled or aborts](https://developers.google.com/google-ads/api/docs/batch-processing/flow.md.txt#canceljob) before it completes all operations, you can
resubmit the unexecuted operations ([`operation_index`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJobResult.md.txt#operation_index)
`>=` [`executed_operation_count`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJob.BatchJobMetadata.md.txt#executed_operation_count)) along with any failed
operations from the executed prefix in a new batch job.