Poll report status
Stay organized with collections
Save and categorize content based on your preferences.
Display & Video 360 reports don't generate instantaneously. Display & Video 360 might
take multiple minutes to over an hour to create a report file.
To determine if your query finished running, regularly retrieve the report using
queries.reports.get
, then check if the resource's
metadata.status.state
field has been updated to DONE
or
FAILED
. This process is known as "polling".
An inefficient polling implementation checking on a long-running report consumes
a lot of API request quota. To limit retries and conserve quota,
use exponential backoff.
Here's how to poll a report using exponential backoff:
Java
//This example the following import.
import com.google.api.client.util.ExponentialBackOff;
// The ID of the parent query.
Long queryId = query-id;
// The ID of the running report.
Long reportId = report-id;
// Minimum amount of time between polling requests. Defaults to 5 seconds.
final int MIN_RETRY_INTERVAL_IN_MILLIS = 5_000;
// Maximum amount of time between polling requests. Defaults to 5 minutes.
final int MAX_RETRY_INTERVAL_IN_MILLIS = 5 * 60_000;
// Maximum amount of time to spend polling. Defaults to 5 hours.
final int MAX_RETRY_ELAPSED_TIME_IN_MILLIS = 5 * 60 * 60_000;
// Configure reports.get request.
Reports.Get reportGetRequest =
service.queries().reports().get(queryId, reportId);
// Get initial report object.
Report report = reportGetRequest.execute();
// Configure exponential backoff for checking the status of our report.
ExponentialBackOff backOff =
new ExponentialBackOff.Builder()
.setInitialIntervalMillis(MIN_RETRY_INTERVAL_IN_MILLIS)
.setMaxIntervalMillis(MAX_RETRY_INTERVAL_IN_MILLIS)
.setMaxElapsedTimeMillis(MAX_RETRY_ELAPSED_TIME_IN_MILLIS)
.build();
// Poll report while it is running.
while (!report.getMetadata().getStatus().getState().equals("DONE")
&& !report.getMetadata().getStatus().getState().equals("FAILED")) {
long backoffMillis = backOff.nextBackOffMillis();
if (backoffMillis == ExponentialBackOff.STOP) {
break;
}
System.out.printf(
"Report %s still running, sleeping for %s seconds.%n",
reportId,
backoffMillis / 1000);
Thread.sleep(backoffMillis);
// Get current status of operation.
report = reportGetRequest.execute();
}
// Print the result of the report generation.
if (report.getMetadata().getStatus().getState().equals("DONE")) {
System.out.printf(
"Report %s was successfully generated.%n",
report.getKey().getReportId());
} else if (report.getMetadata().getStatus().getState().equals("FAILED")) {
System.out.printf(
"Report %s finished in error.%n",
report.getKey().getReportId());
} else {
System.out.println("Report generation deadline exceeded.");
}
Python
# The ID of the parent query.
query_id = query-id
# The ID of the report.
report_id = report-id
# The following values control retry behavior while
# the report is processing.
# 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 queries.reports.get request.
get_request = service.queries().reports().get(
queryId=query_id,reportId=report_id)
sleep = 0
# Poll report while it is running.
start_time = time.time()
while True:
# Get current status of the report
report = get_request.execute()
# Print status if report is finished or deadline is exceeded.
if report["metadata"]["status"]["state"] == "DONE":
print(f'Report {report["key"]["reportId"]} was successfully generated.')
break
elif report["metadata"]["status"]["state"] == "FAILED":
print(f'Report {report["key"]["reportId"]} finished in error.')
break
elif time.time() - start_time > max_retry_elapsed_time:
print("Report generation deadline exceeded.")
break
sleep = next_sleep_interval(sleep)
print(
f'Report {report["key"]["reportId"]} 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))
PHP
// The ID of the parent query.
$queryId = query-id;
// The ID of the running report.
$reportId = report-id;
// Minimum amount of time between polling requests. Defaults to 5 seconds.
$minRetryInterval = 5;
// Maximum amount of time between polling requests. Defaults to 5 minutes.
$maxRetryInterval = 300;
// Maximum amount of time to spend polling. Defaults to 5 hours.
$maxRetryElapsedTime = 18000;
$sleepInterval = 0;
$startTime = time();
// Get initial report object.
$report = $this->service->queries_reports->get($queryId, $reportId);
// Regularly poll report status using exponential backoff.
while (
$report->getMetadata()->getStatus()->getState() !== "DONE"
&& $report->getMetadata()->getStatus()->getState() !== "FAILED"
) {
// If the operation has exceeded the set deadline, throw an exception.
if (time() - $startTime > $maxRetryElapsedTime) {
printf('SDF download task processing deadline exceeded\n');
break;
}
// Generate the next sleep interval using exponential backoff logic.
$sleepInterval = min(
$maxRetryInterval,
rand(
max($minRetryInterval, $sleepInterval),
max($minRetryInterval, $sleepInterval * 3)
)
);
// Sleep before retrieving the report again.
printf(
'Report is %d still running, sleeping for %d seconds<br>',
$reportId,
$sleepInterval
);
sleep($sleepInterval);
// Retrieve the report.
$report = $this->service->queries_reports->get($queryId, $reportId);
}
// Print the result of the report generation.
if($report->getMetadata()->getStatus()->getState() == "DONE") {
printf('Report %d was successfully generated.<br>', $reportId);
} else if($report->getMetadata()->getStatus()->getState() == "FAILED") {
printf('Report %d finished in error.<br>', $reportId);
} else {
print('Report generation deadline exceeded.<br>');
}
Except as otherwise noted, the content of this page is licensed under the Creative Commons Attribution 4.0 License, and code samples are licensed under the Apache 2.0 License. For details, see the Google Developers Site Policies. Java is a registered trademark of Oracle and/or its affiliates.
Last updated 2025-08-28 UTC.
[null,null,["Last updated 2025-08-28 UTC."],[[["\u003cp\u003eDisplay & Video 360 reports are not generated instantly and can take several minutes to over an hour to complete.\u003c/p\u003e\n"],["\u003cp\u003eYou need to periodically check the report status using the \u003ccode\u003equeries.reports.get\u003c/code\u003e method and the \u003ccode\u003emetadata.status.state\u003c/code\u003e field to see if it's 'DONE' or 'FAILED'.\u003c/p\u003e\n"],["\u003cp\u003eTo avoid excessive API requests and conserve quota, implement exponential backoff when polling for report status.\u003c/p\u003e\n"],["\u003cp\u003eThe provided code samples (Java, Python, PHP) demonstrate how to poll report status with exponential backoff.\u003c/p\u003e\n"]]],[],null,["# Poll report status\n\nDisplay \\& Video 360 reports don't generate instantaneously. Display \\& Video 360 might\ntake multiple minutes to over an hour to create a report file.\n\nTo determine if your query finished running, regularly retrieve the report using\n[`queries.reports.get`](/bid-manager/reference/rest/current/queries.reports/get), then check if the resource's\n[`metadata.status.state`](/bid-manager/reference/rest/current/queries.reports#ReportStatus.FIELDS.state) field has been updated to `DONE` or\n`FAILED`. This process is known as \"polling\".\n\nAn inefficient polling implementation checking on a long-running report consumes\na lot of [API request quota](/bid-manager/quotas). To limit retries and conserve quota,\nuse [exponential backoff](/bid-manager/guides/general/best-practices#exponential_backoff).\n\nHere's how to poll a report using exponential backoff: \n\n### Java\n\n```java\n//This example the following import.\nimport com.google.api.client.util.ExponentialBackOff;\n\n// The ID of the parent query.\nLong queryId = query-id;\n\n// The ID of the running report.\nLong reportId = report-id;\n\n// Minimum amount of time between polling requests. Defaults to 5 seconds.\nfinal int MIN_RETRY_INTERVAL_IN_MILLIS = 5_000;\n\n// Maximum amount of time between polling requests. Defaults to 5 minutes.\nfinal int MAX_RETRY_INTERVAL_IN_MILLIS = 5 * 60_000;\n\n// Maximum amount of time to spend polling. Defaults to 5 hours.\nfinal int MAX_RETRY_ELAPSED_TIME_IN_MILLIS = 5 * 60 * 60_000;\n\n// Configure reports.get request.\nReports.Get reportGetRequest =\n service.queries().reports().get(queryId, reportId);\n\n// Get initial report object.\nReport report = reportGetRequest.execute();\n\n// Configure exponential backoff for checking the status of our report.\nExponentialBackOff backOff =\n new ExponentialBackOff.Builder()\n .setInitialIntervalMillis(MIN_RETRY_INTERVAL_IN_MILLIS)\n .setMaxIntervalMillis(MAX_RETRY_INTERVAL_IN_MILLIS)\n .setMaxElapsedTimeMillis(MAX_RETRY_ELAPSED_TIME_IN_MILLIS)\n .build();\n\n// Poll report while it is running.\nwhile (!report.getMetadata().getStatus().getState().equals(\"DONE\")\n && !report.getMetadata().getStatus().getState().equals(\"FAILED\")) {\n long backoffMillis = backOff.nextBackOffMillis();\n if (backoffMillis == ExponentialBackOff.STOP) {\n break;\n }\n System.out.printf(\n \"Report %s still running, sleeping for %s seconds.%n\",\n reportId,\n backoffMillis / 1000);\n Thread.sleep(backoffMillis);\n\n // Get current status of operation.\n report = reportGetRequest.execute();\n}\n\n// Print the result of the report generation.\nif (report.getMetadata().getStatus().getState().equals(\"DONE\")) {\n System.out.printf(\n \"Report %s was successfully generated.%n\",\n report.getKey().getReportId());\n} else if (report.getMetadata().getStatus().getState().equals(\"FAILED\")) {\n System.out.printf(\n \"Report %s finished in error.%n\",\n report.getKey().getReportId());\n} else {\n System.out.println(\"Report generation deadline exceeded.\");\n}\n```\n\n### Python\n\n```python\n# The ID of the parent query.\nquery_id = query-id\n\n# The ID of the report.\nreport_id = report-id\n\n# The following values control retry behavior while\n# the report is processing.\n# Minimum amount of time between polling requests. Defaults to 5 seconds.\nmin_retry_interval = 5\n# Maximum amount of time between polling requests. Defaults to 5 minutes.\nmax_retry_interval = 5 * 60\n# Maximum amount of time to spend polling. Defaults to 5 hours.\nmax_retry_elapsed_time = 5 * 60 * 60\n\n# Configure the queries.reports.get request.\nget_request = service.queries().reports().get(\n queryId=query_id,reportId=report_id)\n\nsleep = 0\n\n# Poll report while it is running.\nstart_time = time.time()\nwhile True:\n # Get current status of the report\n report = get_request.execute()\n\n # Print status if report is finished or deadline is exceeded.\n if report[\"metadata\"][\"status\"][\"state\"] == \"DONE\":\n print(f'Report {report[\"key\"][\"reportId\"]} was successfully generated.')\n break\n elif report[\"metadata\"][\"status\"][\"state\"] == \"FAILED\":\n print(f'Report {report[\"key\"][\"reportId\"]} finished in error.')\n break\n elif time.time() - start_time \u003e max_retry_elapsed_time:\n print(\"Report generation deadline exceeded.\")\n break\n\n sleep = next_sleep_interval(sleep)\n print(\n f'Report {report[\"key\"][\"reportId\"]} still running, sleeping for '\n f'{sleep} seconds.')\n time.sleep(sleep)\n\ndef next_sleep_interval(previous_sleep_interval):\n \"\"\"Calculates the next sleep interval based on the previous.\"\"\"\n min_interval = previous_sleep_interval or min_retry_interval\n max_interval = previous_sleep_interval * 3 or min_retry_interval\n return min(\n max_retry_interval, random.randint(min_interval, max_interval))\n```\n\n### PHP\n\n```php\n// The ID of the parent query.\n$queryId = \u003cvar translate=\"no\"\u003equery-id\u003c/var\u003e;\n\n// The ID of the running report.\n$reportId = \u003cvar translate=\"no\"\u003ereport-id\u003c/var\u003e;\n\n// Minimum amount of time between polling requests. Defaults to 5 seconds.\n$minRetryInterval = 5;\n// Maximum amount of time between polling requests. Defaults to 5 minutes.\n$maxRetryInterval = 300;\n// Maximum amount of time to spend polling. Defaults to 5 hours.\n$maxRetryElapsedTime = 18000;\n\n$sleepInterval = 0;\n$startTime = time();\n\n// Get initial report object.\n$report = $this-\u003eservice-\u003equeries_reports-\u003eget($queryId, $reportId);\n\n// Regularly poll report status using exponential backoff.\nwhile (\n $report-\u003egetMetadata()-\u003egetStatus()-\u003egetState() !== \"DONE\"\n && $report-\u003egetMetadata()-\u003egetStatus()-\u003egetState() !== \"FAILED\"\n) {\n\n // If the operation has exceeded the set deadline, throw an exception.\n if (time() - $startTime \u003e $maxRetryElapsedTime) {\n printf('SDF download task processing deadline exceeded\\n');\n break;\n }\n\n // Generate the next sleep interval using exponential backoff logic.\n $sleepInterval = min(\n $maxRetryInterval,\n rand(\n max($minRetryInterval, $sleepInterval),\n max($minRetryInterval, $sleepInterval * 3)\n )\n );\n\n // Sleep before retrieving the report again.\n printf(\n 'Report is %d still running, sleeping for %d seconds\u003cbr\u003e',\n $reportId,\n $sleepInterval\n );\n sleep($sleepInterval);\n\n // Retrieve the report.\n $report = $this-\u003eservice-\u003equeries_reports-\u003eget($queryId, $reportId);\n}\n\n// Print the result of the report generation.\nif($report-\u003egetMetadata()-\u003egetStatus()-\u003egetState() == \"DONE\") {\n printf('Report %d was successfully generated.\u003cbr\u003e', $reportId);\n} else if($report-\u003egetMetadata()-\u003egetStatus()-\u003egetState() == \"FAILED\") {\n printf('Report %d finished in error.\u003cbr\u003e', $reportId);\n} else {\n print('Report generation deadline exceeded.\u003cbr\u003e');\n}\n```"]]