# Poll task status

Uploaded Structured Data Files don't instantaneously create or update resources.
Display \& Video 360 might take anywhere from a few seconds to hours to process your
SDFs and make the resulting updates. Multiple files can be uploaded concurrently
as long as they don't contain any overlapping resources.

> [!CAUTION]
> **Caution:** A single Display \& Video 360 user can only process 20 SDF upload tasks at a time.

To determine if your task is finished, regularly retrieve the operation using
[`sdfuploadtasks.operations.get`](https://developers.google.com/display-video/api/sdf-upload/rest/v4/sdfuploadtasks.operations/get) and check if the
[`done`](https://developers.google.com/display-video/api/sdf-upload/rest/v4/sdfuploadtasks.operations#Operation.FIELDS.done) field is `True` and the task is finished. This process is
known as "polling."

If the task is finished, either the [`response`](https://developers.google.com/display-video/api/sdf-upload/rest/v4/sdfuploadtasks.operations#Operation.FIELDS.response) or [`error`](https://developers.google.com/display-video/api/sdf-upload/rest/v4/sdfuploadtasks.operations#Operation.FIELDS.error)
field is set. If the [`error`](https://developers.google.com/display-video/api/sdf-upload/rest/v4/sdfuploadtasks.operations#Operation.FIELDS.error) field is populated, the task failed and
details about the failure are available in the resulting [`Status`](https://developers.google.com/display-video/api/sdf-upload/rest/v4/sdfdownloadtasks.operations#Operation.Status)
object. If the [`response`](https://developers.google.com/display-video/api/sdf-upload/rest/v4/sdfuploadtasks.operations#Operation.FIELDS.response) field is populated, an
[`SdfUploadTask`](https://developers.google.com/display-video/api/sdf-upload/rest/v4/SdfUploadTask) will be assigned to the field. This means that
Display \& Video 360 successfully processed the uploaded SDF. [Download the result
files](https://developers.google.com/display-video/api/sdf-upload/upload-guide/download-results) from the location provided in
`SdfUploadTask.resourceName` field.

An inefficient polling implementation that checks on a long-running report
consumes a lot of your [API request quota](https://developers.google.com/display-video/api/limits). To limit retries and
conserve quota, use [exponential backoff](https://developers.google.com/display-video/api/guides/best-practices/quota#implement_exponential_backoff_for_long-running_operations).

Here's how to poll a SDF upload operation using exponential backoff:

```
# Import the necessary libraries
import random
import time

# Provide the name of the sdfuploadtask operation.
operation_name = operation-name

# Set the following values that control retry behavior while the operation
# is running.
# Minimum amount of time between polling requests. Defaults to 5 seconds.
min_retry_interval = 5
# Maximum amount of time between polling requests. Defaults to 5 minutes.
max_retry_interval = 5 * 60
# Maximum amount of time to spend polling. Defaults to 5 hours.
max_retry_elapsed_time = 5 * 60 * 60

# Configure the sdfuploadtasks.operations.get request.
get_request = service.sdfuploadtasks().operations().get(operation_name)

sleep = 0

start_time = time.time()
while True:
  # Get current status of the report.
  operation = get_request.execute()

  if "done" in operation:
    if "error" in operation:
      print(
        f'The operation finished in error with code '
        f'{operation["error"]["code"]}: {operation["error"]["message"]}')
    else:
      print(
        f'The operation completed successfully. The result files can be '
        f'downloaded at {operation["response"]["resourceName"]}.')
    break
  elif time.time() - start_time > max_retry_elapsed_time:
    print("Polling deadline exceeded.")
    break

  sleep = next_sleep_interval(sleep)
  print(
    f'Operation {operation_name} is still running, sleeping for '
    f'{sleep} seconds.')
  time.sleep(sleep)

def next_sleep_interval(previous_sleep_interval):
  """Calculates the next sleep interval based on the previous."""
  min_interval = previous_sleep_interval or min_retry_interval
  max_interval = previous_sleep_interval * 3 or min_retry_interval
  return min(max_retry_interval, random.randint(min_interval, max_interval))
```