There are three methods for retrieving entities and reporting data with the Google Ads API.
GoogleAdsService.SearchStreamGoogleAdsService.Search- GET requests
This guide primarily focuses on streaming data from
GoogleAdsService. Here are high-level distinctions
for the three data retrieval methods:
| GoogleAdsService.SearchStream | GoogleAdsService.Search | GET requests | |
|---|---|---|---|
| Suitable for production code | Yes | Yes | No (for debugging only) |
| Service | GoogleAdsService |
GoogleAdsService |
Resource specific services (for example, CampaignService) |
| Scenario | Fetching objects and reports | Fetching objects and reports | Fetching objects |
| Response | Stream of GoogleAdsRow objects |
Pages of GoogleAdsRow objects |
One object (for example, Campaign) |
| Response's fields | Only those specified in the query | Only those specified in the query | All fields populated |
| Daily limits | Daily limits based on access levels | Daily limits based on access levels | 1,000 requests per day |
SearchStream versus Search
While Search can send multiple paginated
requests to download the entire report,
SearchStream sends a single request
and initiates a persistent connection with the Google Ads API regardless of report size.
For SearchStream, data packets start to download immediately with the entire
result cached in a data buffer. Your code can start reading the buffered data
without having to wait for the entire stream to finish.
By eliminating the round-trip network time required to request each individual
page of a Search response, depending on your app, SearchStream can offer
improved performance over paging, especially for bigger reports.
Example
Take a report that consists of 100,000 rows for example. The following
table breaks down the accounting differences between the two methods.
| SearchStream | Search | |
|---|---|---|
| Page size | Not Applicable | 10,000 rows per page |
| Number of API requests | 1 request | 10 requests |
| Number of API responses | 1 continuous stream | 10 responses |
Performance factors
In general, we recommend SearchStream over Search for the following reasons.
For single page reports (under 10,000 rows): No significant performance differences between the two methods.
For multiple page reports:
SearchStreamis typically faster since multiple roundtrips are avoided and reading/writing from disk cache is less of a factor.
Rate limits
Daily limits for both methods adhere to the standard limits and access levels of your developer token. A single query or report is counted as one operation regardless of the result being paged or streamed.
Code examples
Java
private void runExample(GoogleAdsClient googleAdsClient, long customerId) {
try (GoogleAdsServiceClient googleAdsServiceClient =
googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) {
String searchQuery =
"SELECT campaign.id, "
+ "campaign.name, "
+ "ad_group.id, "
+ "ad_group.name, "
+ "ad_group_criterion.criterion_id, "
+ "ad_group_criterion.keyword.text, "
+ "ad_group_criterion.keyword.match_type, "
+ "metrics.impressions, "
+ "metrics.clicks, "
+ "metrics.cost_micros "
+ "FROM keyword_view "
+ "WHERE segments.date DURING LAST_7_DAYS "
+ "AND campaign.advertising_channel_type = 'SEARCH' "
+ "AND ad_group.status = 'ENABLED' "
+ "AND ad_group_criterion.status IN ('ENABLED', 'PAUSED') "
// Limits to the 50 keywords with the most impressions in the date range.
+ "ORDER BY metrics.impressions DESC "
+ "LIMIT 50";
// Constructs the SearchGoogleAdsStreamRequest.
SearchGoogleAdsStreamRequest request =
SearchGoogleAdsStreamRequest.newBuilder()
.setCustomerId(Long.toString(customerId))
.setQuery(searchQuery)
.build();
// Creates and issues a search Google Ads stream request that will retrieve all of the
// requested field values for the keyword.
ServerStream<SearchGoogleAdsStreamResponse> stream =
googleAdsServiceClient.searchStreamCallable().call(request);
// Iterates through the results in the stream response and prints all of the requested
// field values for the keyword in each row.
for (SearchGoogleAdsStreamResponse response : stream) {
for (GoogleAdsRow googleAdsRow : response.getResultsList()) {
Campaign campaign = googleAdsRow.getCampaign();
AdGroup adGroup = googleAdsRow.getAdGroup();
AdGroupCriterion adGroupCriterion = googleAdsRow.getAdGroupCriterion();
Metrics metrics = googleAdsRow.getMetrics();
System.out.printf(
"Keyword text '%s' with "
+ "match type '%s' "
+ "and ID %d "
+ "in ad group '%s' "
+ "with ID %d "
+ "in campaign '%s' "
+ "with ID %d "
+ "had %d impression(s), "
+ "%d click(s), "
+ "and %d cost (in micros) "
+ "during the last 7 days.%n",
adGroupCriterion.getKeyword().getText(),
adGroupCriterion.getKeyword().getMatchType(),
adGroupCriterion.getCriterionId(),
adGroup.getName(),
adGroup.getId(),
campaign.getName(),
campaign.getId(),
metrics.getImpressions(),
metrics.getClicks(),
metrics.getCostMicros());
}
}
}
}
C#
public void Run(GoogleAdsClient client, long customerId)
{
// Get the GoogleAdsService.
GoogleAdsServiceClient googleAdsService = client.GetService(
Services.V11.GoogleAdsService);
// Create the query.
string query =
@"SELECT
campaign.id,
campaign.name,
ad_group.id,
ad_group.name,
ad_group_criterion.criterion_id,
ad_group_criterion.keyword.text,
ad_group_criterion.keyword.match_type,
metrics.impressions,
metrics.clicks,
metrics.cost_micros
FROM keyword_view
WHERE segments.date DURING LAST_7_DAYS
AND campaign.advertising_channel_type = 'SEARCH'
AND ad_group.status = 'ENABLED'
AND ad_group_criterion.status IN ('ENABLED','PAUSED')
ORDER BY metrics.impressions DESC
LIMIT 50";
try
{
// Issue a search request.
googleAdsService.SearchStream(customerId.ToString(), query,
delegate (SearchGoogleAdsStreamResponse resp)
{
// Display the results.
foreach (GoogleAdsRow criterionRow in resp.Results)
{
Console.WriteLine(
"Keyword with text " +
$"'{criterionRow.AdGroupCriterion.Keyword.Text}', match type " +
$"'{criterionRow.AdGroupCriterion.Keyword.MatchType}' and ID " +
$"{criterionRow.AdGroupCriterion.CriterionId} in ad group " +
$"'{criterionRow.AdGroup.Name}' with ID " +
$"{criterionRow.AdGroup.Id} in campaign " +
$"'{criterionRow.Campaign.Name}' with ID " +
$"{criterionRow.Campaign.Id} had " +
$"{criterionRow.Metrics.Impressions.ToString()} impressions, " +
$"{criterionRow.Metrics.Clicks} clicks, and " +
$"{criterionRow.Metrics.CostMicros} cost (in micros) during the " +
"last 7 days.");
}
}
);
}
catch (GoogleAdsException e)
{
Console.WriteLine("Failure:");
Console.WriteLine($"Message: {e.Message}");
Console.WriteLine($"Failure: {e.Failure}");
Console.WriteLine($"Request ID: {e.RequestId}");
throw;
}
}
PHP
public static function runExample(GoogleAdsClient $googleAdsClient, int $customerId)
{
$googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient();
// Creates a query that retrieves all keyword statistics.
$query =
"SELECT campaign.id, "
. "campaign.name, "
. "ad_group.id, "
. "ad_group.name, "
. "ad_group_criterion.criterion_id, "
. "ad_group_criterion.keyword.text, "
. "ad_group_criterion.keyword.match_type, "
. "metrics.impressions, "
. "metrics.clicks, "
. "metrics.cost_micros "
. "FROM keyword_view "
. "WHERE segments.date DURING LAST_7_DAYS "
. "AND campaign.advertising_channel_type = 'SEARCH' "
. "AND ad_group.status = 'ENABLED' "
. "AND ad_group_criterion.status IN ('ENABLED', 'PAUSED') "
// Limits to the 50 keywords with the most impressions in the date range.
. "ORDER BY metrics.impressions DESC "
. "LIMIT 50";
// Issues a search stream request.
/** @var GoogleAdsServerStreamDecorator $stream */
$stream =
$googleAdsServiceClient->searchStream($customerId, $query);
// Iterates over all rows in all messages and prints the requested field values for
// the keyword in each row.
foreach ($stream->iterateAllElements() as $googleAdsRow) {
/** @var GoogleAdsRow $googleAdsRow */
$campaign = $googleAdsRow->getCampaign();
$adGroup = $googleAdsRow->getAdGroup();
$adGroupCriterion = $googleAdsRow->getAdGroupCriterion();
$metrics = $googleAdsRow->getMetrics();
printf(
"Keyword text '%s' with "
. "match type %s "
. "and ID %d "
. "in ad group '%s' "
. "with ID %d "
. "in campaign '%s' "
. "with ID %d "
. "had %d impression(s), "
. "%d click(s), "
. "and %d cost (in micros) "
. "during the last 7 days.%s",
$adGroupCriterion->getKeyword()->getText(),
KeywordMatchType::name($adGroupCriterion->getKeyword()->getMatchType()),
$adGroupCriterion->getCriterionId(),
$adGroup->getName(),
$adGroup->getId(),
$campaign->getName(),
$campaign->getId(),
$metrics->getImpressions(),
$metrics->getClicks(),
$metrics->getCostMicros(),
PHP_EOL
);
}
}
Python
def main(client, customer_id):
ga_service = client.get_service("GoogleAdsService")
query = """
SELECT
campaign.id,
campaign.name,
ad_group.id,
ad_group.name,
ad_group_criterion.criterion_id,
ad_group_criterion.keyword.text,
ad_group_criterion.keyword.match_type,
metrics.impressions,
metrics.clicks,
metrics.cost_micros
FROM keyword_view WHERE segments.date DURING LAST_7_DAYS
AND campaign.advertising_channel_type = 'SEARCH'
AND ad_group.status = 'ENABLED'
AND ad_group_criterion.status IN ('ENABLED', 'PAUSED')
ORDER BY metrics.impressions DESC
LIMIT 50"""
# Issues a search request using streaming.
search_request = client.get_type("SearchGoogleAdsStreamRequest")
search_request.customer_id = customer_id
search_request.query = query
stream = ga_service.search_stream(search_request)
for batch in stream:
for row in batch.results:
campaign = row.campaign
ad_group = row.ad_group
criterion = row.ad_group_criterion
metrics = row.metrics
print(
f'Keyword text "{criterion.keyword.text}" with '
f'match type "{criterion.keyword.match_type.name}" '
f"and ID {criterion.criterion_id} in "
f'ad group "{ad_group.name}" '
f'with ID "{ad_group.id}" '
f'in campaign "{campaign.name}" '
f"with ID {campaign.id} "
f"had {metrics.impressions} impression(s), "
f"{metrics.clicks} click(s), and "
f"{metrics.cost_micros} cost (in micros) during "
"the last 7 days."
)
Ruby
def get_keyword_stats(customer_id)
# GoogleAdsClient will read a config file from
# ENV['HOME']/google_ads_config.rb when called without parameters
client = Google::Ads::GoogleAds::GoogleAdsClient.new
ga_service = client.service.google_ads
# Limits to the 50 keywords with the most impressions in the date range.
# If you wish to exclude entries with zero impressions, include a
# predicate in the WHERE statement like 'metrics.impressions > 0'
query = <<~QUERY
SELECT campaign.id,
campaign.name,
ad_group.id,
ad_group.name,
ad_group_criterion.criterion_id,
ad_group_criterion.keyword.text,
ad_group_criterion.keyword.match_type,
metrics.impressions,
metrics.clicks,
metrics.cost_micros
FROM keyword_view
WHERE segments.date DURING LAST_7_DAYS
AND campaign.advertising_channel_type = 'SEARCH'
AND ad_group.status = 'ENABLED'
AND ad_group_criterion.status IN ('ENABLED', 'PAUSED')
ORDER BY metrics.impressions DESC
LIMIT 50
QUERY
responses = ga_service.search_stream(customer_id: customer_id, query: query)
responses.each do |response|
response.results.each do |row|
campaign = row.campaign
ad_group = row.ad_group
criterion = row.ad_group_criterion
metrics = row.metrics
puts "Keyword text '#{criterion.keyword.text}' with match type "\
"'#{criterion.keyword.match_type}' and ID #{criterion.criterion_id} in "\
"ad group '#{ad_group.name}' with ID #{ad_group.id} in campaign "\
"'#{campaign.name}' with ID #{campaign.id} had #{metrics.impressions} "\
"impression(s), #{metrics.clicks} click(s), and #{metrics.cost_micros} "\
"cost (in micros) during the last 7 days."
end
end
end
Perl
sub get_keyword_stats {
my ($api_client, $customer_id) = @_;
# Limit to the 50 keywords with the most impressions in the date range.
# If you wish to exclude entries with zero impressions, include a
# predicate in the WHERE statement like 'metrics.impressions > 0'.
my $search_query =
"SELECT campaign.id, campaign.name, ad_group.id, ad_group.name, " .
"ad_group_criterion.criterion_id, ad_group_criterion.keyword.text, " .
"ad_group_criterion.keyword.match_type, " .
"metrics.impressions, metrics.clicks, metrics.cost_micros " .
"FROM keyword_view WHERE segments.date DURING LAST_7_DAYS " .
"AND campaign.advertising_channel_type = 'SEARCH' " .
"AND ad_group.status = 'ENABLED' " .
"AND ad_group_criterion.status IN ('ENABLED', 'PAUSED') " .
"ORDER BY metrics.impressions DESC LIMIT 50";
# Create a search Google Ads stream request that will retrieve all keyword
# statistics.
my $search_stream_request =
Google::Ads::GoogleAds::V11::Services::GoogleAdsService::SearchGoogleAdsStreamRequest
->new({
customerId => $customer_id,
query => $search_query,
});
# Get the GoogleAdsService.
my $google_ads_service = $api_client->GoogleAdsService();
my $search_stream_handler =
Google::Ads::GoogleAds::Utils::SearchStreamHandler->new({
service => $google_ads_service,
request => $search_stream_request
});
# Issue a search request and process the stream response to print the requested
# field values for the keyword in each row.
$search_stream_handler->process_contents(
sub {
my $google_ads_row = shift;
my $campaign = $google_ads_row->{campaign};
my $ad_group = $google_ads_row->{adGroup};
my $ad_group_criterion = $google_ads_row->{adGroupCriterion};
my $metrics = $google_ads_row->{metrics};
printf "Keyword text '%s' with match type '%s' and ID %d in ad group" .
" '%s' with ID %d in campaign '%s' with ID %d had %d impression(s), " .
"%d click(s), and %d cost (in micros) during the last 7 days.\n",
$ad_group_criterion->{keyword}{text},
$ad_group_criterion->{keyword}{matchType},
$ad_group_criterion->{criterionId},
$ad_group->{name},
$ad_group->{id},
$campaign->{name},
$campaign->{id},
$metrics->{impressions},
$metrics->{clicks},
$metrics->{costMicros};
});
return 1;
}

