Google offers a variety of products and audiences you can use to build
campaigns, but you may not know the exact formulation when creating a plan. To
help with this, ReachPlanService
provides
discovery methods you can use to generate a
curve.
Audience targeting
The first component of a media plan is audience targeting, which includes:
- Locations
- Use the
ListPlannableLocations
method to find supported location IDs. - Age Ranges
- Reference the
ReachPlanAgeRange
enum. - Genders
- Reference the
GenderInfo
criterion. - Devices
- Reference the
DeviceInfo
criterion.
Product mix
In addition to the targeting information, you must specify a product mix in order to generate a curve. There are two ways to do this.
Suggested mix
If you are not familiar with Google's video ad formats, then you can use the
GenerateProductMixIdeas
method to get a recommended split for a given budget
in your currency
and set of preferences including:
- Whether or not you would like a guaranteed price on inventory.
- If you would like video ad formats that include sound, are skippable or only show on top content.
- A
ReachPlanAdLength
.
Java
private void forecastSuggestedMix( ReachPlanServiceClient reachPlanServiceClient, long customerId, String locationId, String currencyCode, long budgetMicros) { // Note: If preferences are too restrictive, then the response will be empty. Preferences preferences = Preferences.newBuilder() .setHasGuaranteedPrice(true) .setStartsWithSound(true) .setIsSkippable(false) .setTopContentOnly(true) .setAdLength(ReachPlanAdLength.FIFTEEN_OR_TWENTY_SECONDS) .build(); GenerateProductMixIdeasRequest mixRequest = GenerateProductMixIdeasRequest.newBuilder() .setBudgetMicros(budgetMicros) .setCurrencyCode(currencyCode) .setCustomerId(Long.toString(customerId)) .setPlannableLocationId(locationId) .setPreferences(preferences) .build(); GenerateProductMixIdeasResponse mixResponse = reachPlanServiceClient.generateProductMixIdeas(mixRequest); List<PlannedProduct> productMix = new ArrayList<>(); for (ProductAllocation product : mixResponse.getProductAllocationList()) { productMix.add( PlannedProduct.newBuilder() .setPlannableProductCode(product.getPlannableProductCode()) .setBudgetMicros(product.getBudgetMicros()) .build()); } GenerateReachForecastRequest curveRequest = buildReachRequest(customerId, productMix, locationId, currencyCode); getReachCurve(reachPlanServiceClient, curveRequest); }
C#
public void ForecastSuggestedMix(ReachPlanServiceClient reachPlanService, string customerId, string locationId, string currencyCode, long budgetMicros) { // Note: If preferences are too restrictive, then the response will be empty. Preferences preferences = new Preferences { HasGuaranteedPrice = true, StartsWithSound = true, IsSkippable = false, TopContentOnly = true, AdLength = ReachPlanAdLength.FifteenOrTwentySeconds }; GenerateProductMixIdeasRequest mixRequest = new GenerateProductMixIdeasRequest { BudgetMicros = Convert.ToInt64((double) budgetMicros), CurrencyCode = currencyCode, CustomerId = customerId, PlannableLocationId = locationId, Preferences = preferences }; GenerateProductMixIdeasResponse mixResponse = reachPlanService.GenerateProductMixIdeas(mixRequest); List<PlannedProduct> productMix = new List<PlannedProduct>(); foreach (ProductAllocation product in mixResponse.ProductAllocation) { productMix.Add(new PlannedProduct { PlannableProductCode = product.PlannableProductCode, BudgetMicros = product.BudgetMicros }); } GenerateReachForecastRequest curveRequest = BuildReachRequest(customerId, productMix, locationId, currencyCode); GetReachCurve(reachPlanService, curveRequest); }
PHP
private static function forecastSuggestedMix(GoogleAdsClient $googleAdsClient, int $customerId) { $preferences = new Preferences([ 'has_guaranteed_price' => true, 'starts_with_sound' => true, 'is_skippable' => false, 'top_content_only' => true, 'ad_length' => ReachPlanAdLength::FIFTEEN_OR_TWENTY_SECONDS ]); $mixResponse = $googleAdsClient->getReachPlanServiceClient()->generateProductMixIdeas( $customerId, self::LOCATION_ID, self::CURRENCY_CODE, self::BUDGET_MICROS, ['preferences' => $preferences] ); $productMix = []; foreach ($mixResponse->getProductAllocation() as $product) { /** @var ProductAllocation $product */ $productMix[] = new PlannedProduct([ 'plannable_product_code' => $product->getPlannableProductCode(), 'budget_micros' => $product->getBudgetMicros() ]); } self::getReachCurve( $googleAdsClient, $customerId, $productMix, self::LOCATION_ID, self::CURRENCY_CODE ); }
Python
def _forecast_suggested_mix( client, customer_id, location_id, currency_code, budget ): """Pulls a forecast for a product mix based on your set of preferences. Args: client: an initialized GoogleAdsClient instance. customer_id: The customer ID for the reach forecast. product_mix: The product mix for the reach forecast. location_id: The location ID to plan for. currency_code: Three-character ISO 4217 currency code. budget: Budget to allocate to the plan. """ preferences = client.get_type("Preferences") preferences.has_guaranteed_price = True preferences.starts_with_sound = True preferences.is_skippable = False preferences.top_content_only = True preferences.ad_length = ( client.enums.ReachPlanAdLengthEnum.FIFTEEN_OR_TWENTY_SECONDS ) reach_plan_service = client.get_service("ReachPlanService") request = client.get_type("GenerateProductMixIdeasRequest") request.customer_id = customer_id request.plannable_location_id = location_id request.preferences = preferences request.currency_code = currency_code request.budget_micros = int(budget * ONE_MILLION) mix_response = reach_plan_service.generate_product_mix_ideas( request=request ) product_mix = [] for product in mix_response.product_allocation: planned_product = client.get_type("PlannedProduct") planned_product.plannable_product_code = product.plannable_product_code planned_product.budget_micros = product.budget_micros product_mix.append(planned_product) _request_reach_curve( client, customer_id, product_mix, location_id, currency_code )
Ruby
def forecast_suggested_mix(client, reach_plan_service, customer_id) preferences = client.resource.preferences do |p| p.has_guaranteed_price = true p.starts_with_sound = true p.is_skippable = false p.top_content_only = true p.ad_length = :FIFTEEN_OR_TWENTY_SECONDS end mix_response = reach_plan_service.generate_product_mix_ideas( customer_id: customer_id, plannable_location_id: LOCATION_ID, currency_code: CURRENCY_CODE, budget_micros: BUDGET_MICROS, preferences: preferences, ) product_mix = [] mix_response.product_allocation.each do |product| product_mix << client.resource.planned_product do |pp| pp.plannable_product_code = product.plannable_product_code pp.budget_micros = product.budget_micros end end get_reach_curve( client, reach_plan_service, customer_id, product_mix, LOCATION_ID, CURRENCY_CODE, ) end
Perl
sub forecast_suggested_mix { my ( $reach_plan_service, $customer_id, $location_id, $currency_code, $budget_micros ) = @_; # Note: If preferences are too restrictive, then the response will be empty. my $preferences = Google::Ads::GoogleAds::V11::Services::ReachPlanService::Preferences->new({ hasGuaranteedPrice => "true", startsWithSound => "true", isSkippable => "false", topContentOnly => "true", adLength => FIFTEEN_OR_TWENTY_SECONDS }); my $mix_response = $reach_plan_service->generate_product_mix_ideas({ customerId => $customer_id, plannableLocationId => $location_id, preferences => $preferences, currencyCode => $currency_code, budgetMicros => $budget_micros, }); my $product_mix = []; foreach my $product (@{$mix_response->{productAllocation}}) { push @$product_mix, Google::Ads::GoogleAds::V11::Services::ReachPlanService::PlannedProduct-> new({ plannableProductCode => $product->{plannableProductCode}, budgetMicros => $product->{budgetMicros}}); } my $reach_request = build_reach_request($customer_id, $product_mix, $location_id, $currency_code); pull_reach_curve($reach_plan_service, $reach_request); }
Manual mix
If you are already familiar with Google's video ad formats, the
ListPlannableProducts
method on ReachPlanService
will show the
latest available formats for a given location ID, represented by the
plannable_product_code
and plannable_product_name
fields of ProductMetadata
. Keep in mind
that these values change periodically, so it is best to provide your users with
live results rather than persisting responses offline.
To learn more about Google video ad formats, visit this Google Ads Help
Center page.
Java
private void showPlannableProducts( ReachPlanServiceClient reachPlanServiceClient, String locationId) { ListPlannableProductsRequest request = ListPlannableProductsRequest.newBuilder().setPlannableLocationId(locationId).build(); ListPlannableProductsResponse response = reachPlanServiceClient.listPlannableProducts(request); System.out.printf("Plannable Products for location %s:%n", locationId); for (ProductMetadata product : response.getProductMetadataList()) { System.out.printf("%s:%n", product.getPlannableProductCode()); System.out.println("Age Ranges:"); for (ReachPlanAgeRange ageRange : product.getPlannableTargeting().getAgeRangesList()) { System.out.printf("\t- %s%n", ageRange); } System.out.println("Genders:"); for (GenderInfo gender : product.getPlannableTargeting().getGendersList()) { System.out.printf("\t- %s%n", gender.getType()); } System.out.println("Devices:"); for (DeviceInfo device : product.getPlannableTargeting().getDevicesList()) { System.out.printf("\t- %s%n", device.getType()); } } }
C#
public void ShowPlannableProducts( ReachPlanServiceClient reachPlanService, string locationId) { ListPlannableProductsRequest request = new ListPlannableProductsRequest { PlannableLocationId = locationId }; ListPlannableProductsResponse response = reachPlanService.ListPlannableProducts( request); Console.WriteLine($"Plannable Products for location {locationId}:"); foreach (ProductMetadata product in response.ProductMetadata) { Console.WriteLine($"{product.PlannableProductCode}:"); Console.WriteLine("Age Ranges:"); foreach (ReachPlanAgeRange ageRange in product.PlannableTargeting.AgeRanges) { Console.WriteLine($"\t- {ageRange}"); } Console.WriteLine("Genders:"); foreach (GenderInfo gender in product.PlannableTargeting.Genders) { Console.WriteLine($"\t- {gender.Type}"); } Console.WriteLine("Devices:"); foreach (DeviceInfo device in product.PlannableTargeting.Devices) { Console.WriteLine($"\t- {device.Type}"); } } }
PHP
private static function showPlannableProducts(GoogleAdsClient $googleAdsClient) { $response = $googleAdsClient->getReachPlanServiceClient()->listPlannableProducts( self::LOCATION_ID ); print 'Plannable Products for Location ID ' . self::LOCATION_ID . ':' . PHP_EOL; foreach ($response->getProductMetadata() as $product) { /** @var ProductMetadata $product */ print $product->getPlannableProductCode() . ':' . PHP_EOL; print 'Age Ranges:' . PHP_EOL; foreach ($product->getPlannableTargeting()->getAgeRanges() as $ageRange) { /** @var ReachPlanAgeRange $ageRange */ printf("\t- %s%s", ReachPlanAgeRange::name($ageRange), PHP_EOL); } print 'Genders:' . PHP_EOL; foreach ($product->getPlannableTargeting()->getGenders() as $gender) { /** @var GenderInfo $gender */ printf("\t- %s%s", GenderType::name($gender->getType()), PHP_EOL); } print 'Devices:' . PHP_EOL; foreach ($product->getPlannableTargeting()->getDevices() as $device) { /** @var DeviceInfo $device */ printf("\t- %s%s", Device::name($device->getType()), PHP_EOL); } } }
Python
def _show_plannable_products(client, location_id): """Lists plannable products for a given location. Args: client: an initialized GoogleAdsClient instance. location_id: The location ID to plan for. """ reach_plan_service = client.get_service("ReachPlanService") response = reach_plan_service.list_plannable_products( plannable_location_id=location_id ) print(f"Plannable Products for Location ID {location_id}") for product_metadata in response.product_metadata: print( f"{product_metadata.plannable_product_code} : " f"{product_metadata.plannable_product_name}" ) print("Age Ranges:") for age_range in product_metadata.plannable_targeting.age_ranges: print(f"\t- {age_range.name}") print("Genders:") for gender in product_metadata.plannable_targeting.genders: print(f"\t- {gender.type_.name}") print("Devices:") for device in product_metadata.plannable_targeting.devices: print(f"\t- {device.type_.name}")
Ruby
def show_plannable_products(reach_plan_service) response = reach_plan_service.list_plannable_products( plannable_location_id: LOCATION_ID, ) puts "Plannable Products for Location ID #{LOCATION_ID}:" response.product_metadata.each do |product| puts "#{product.plannable_product_code}:" puts "Age Ranges:" product.plannable_targeting.age_ranges.each do |age_range| puts "\t- #{age_range}" end puts "Genders:" product.plannable_targeting.genders.each do |gender| puts "\t- #{gender.type}" end puts "Devices:" product.plannable_targeting.devices.each do |device| puts "\t- #{device.type}" end end end
Perl
sub show_plannable_products { my ($reach_plan_service, $location_id) = @_; my $response = $reach_plan_service->list_plannable_products({ plannableLocationId => $location_id }); printf "Plannable Products for location %d:\n", $location_id; foreach my $product (@{$response->{productMetadata}}) { printf "%s : '%s'\n", $product->{plannableProductCode}, $product->{plannableProductName}; print "Age Ranges:\n"; foreach my $age_range (@{$product->{plannableTargeting}{ageRanges}}) { printf "\t- %s\n", $age_range; } print "Genders:\n"; foreach my $gender (@{$product->{plannableTargeting}{genders}}) { printf "\t- %s\n", $gender->{type}; } print "Devices:\n"; foreach my $device (@{$product->{plannableTargeting}{devices}}) { printf "\t- %s\n", $device->{type}; } } }
To generate a curve you would simply assign a budget to each product.
Java
private void forecastManualMix( ReachPlanServiceClient reachPlanServiceClient, long customerId, String locationId, String currencyCode, long budgetMicros) { List<PlannedProduct> productMix = new ArrayList<>(); // Set up a ratio to split the budget between two products. double trueviewAllocation = 0.15; double bumperAllocation = 1 - trueviewAllocation; // See listPlannableProducts on ReachPlanService to retrieve a list // of valid PlannableProductCode's for a given location: // https://developers.google.com/google-ads/api/reference/rpc/latest/ReachPlanService productMix.add( PlannedProduct.newBuilder() .setPlannableProductCode("TRUEVIEW_IN_STREAM") .setBudgetMicros((long) (budgetMicros * bumperAllocation)) .build()); productMix.add( PlannedProduct.newBuilder() .setPlannableProductCode("BUMPER") .setBudgetMicros((long) (budgetMicros * bumperAllocation)) .build()); GenerateReachForecastRequest request = buildReachRequest(customerId, productMix, locationId, currencyCode); getReachCurve(reachPlanServiceClient, request); }
C#
public void ForecastManualMix(ReachPlanServiceClient reachPlanService, string customerId, string locationId, string currencyCode, long budgetMicros) { List<PlannedProduct> productMix = new List<PlannedProduct>(); // Set up a ratio to split the budget between two products. double trueviewAllocation = 0.15; double bumperAllocation = 1 - trueviewAllocation; // See listPlannableProducts on ReachPlanService to retrieve a list // of valid PlannableProductCode's for a given location: // https://developers.google.com/google-ads/api/reference/rpc/latest/ReachPlanService productMix.Add(new PlannedProduct { PlannableProductCode = "TRUEVIEW_IN_STREAM", BudgetMicros = Convert.ToInt64(budgetMicros * trueviewAllocation) }); productMix.Add(new PlannedProduct { PlannableProductCode = "BUMPER", BudgetMicros = Convert.ToInt64(budgetMicros * bumperAllocation) }); GenerateReachForecastRequest request = BuildReachRequest(customerId, productMix, locationId, currencyCode); GetReachCurve(reachPlanService, request); }
PHP
private static function forecastManualMix(GoogleAdsClient $googleAdsClient, int $customerId) { // Set up a ratio to split the budget between two products. $trueviewAllocation = floatval(0.15); $bumperAllocation = floatval(1 - $trueviewAllocation); // See listPlannableProducts on ReachPlanService to retrieve a list // of valid PlannableProductCode's for a given location: // https://developers.google.com/google-ads/api/reference/rpc/latest/ReachPlanService $productMix = [ new PlannedProduct([ 'plannable_product_code' => 'TRUEVIEW_IN_STREAM', 'budget_micros' => self::BUDGET_MICROS * $trueviewAllocation ]), new PlannedProduct([ 'plannable_product_code' => 'BUMPER', 'budget_micros' => self::BUDGET_MICROS * $bumperAllocation ]) ]; self::getReachCurve( $googleAdsClient, $customerId, $productMix, self::LOCATION_ID, self::CURRENCY_CODE ); }
Python
def _forecast_manual_mix( client, customer_id, location_id, currency_code, budget ): """Pulls a forecast for product mix created manually. Args: client: an initialized GoogleAdsClient instance. customer_id: The customer ID for the reach forecast. product_mix: The product mix for the reach forecast. location_id: The location ID to plan for. currency_code: Three-character ISO 4217 currency code. budget: Budget to allocate to the plan. """ product_mix = [] trueview_allocation = 0.15 bumper_allocation = 1 - trueview_allocation product_splits = [ ("TRUEVIEW_IN_STREAM", trueview_allocation), ("BUMPER", bumper_allocation), ] for product, split in product_splits: planned_product = client.get_type("PlannedProduct") planned_product.plannable_product_code = product planned_product.budget_micros = math.trunc(budget * ONE_MILLION * split) product_mix.append(planned_product) _request_reach_curve( client, customer_id, product_mix, location_id, currency_code )
Ruby
def forecast_manual_mix(client, reach_plan_service, customer_id) # Set up a ratio to split the budget between two products. trueview_allocation = 0.15 bumper_allocation = 1 - trueview_allocation # See listPlannableProducts on ReachPlanService to retrieve a list # of valid PlannableProductCode's for a given location: # https://developers.google.com/google-ads/api/reference/rpc/latest/ReachPlanService product_mix = [] product_mix << client.resource.planned_product do |p| p.plannable_product_code = 'TRUEVIEW_IN_STREAM' p.budget_micros = BUDGET_MICROS * trueview_allocation end product_mix << client.resource.planned_product do |p| p.plannable_product_code = 'BUMPER' p.budget_micros = BUDGET_MICROS * bumper_allocation end get_reach_curve( client, reach_plan_service, customer_id, product_mix, LOCATION_ID, CURRENCY_CODE, ) end
Perl
sub forecast_manual_mix { my ( $reach_plan_service, $customer_id, $location_id, $currency_code, $budget_micros ) = @_; my $product_mix = []; # Set up a ratio to split the budget between two products. my $trueview_allocation = 0.15; my $bumper_allocation = 1 - $trueview_allocation; # See list_plannable_products on ReachPlanService to retrieve a list of valid # plannable product codes for a given location: # https://developers.google.com/google-ads/api/reference/rpc/latest/ReachPlanService push @$product_mix, Google::Ads::GoogleAds::V11::Services::ReachPlanService::PlannedProduct-> new({ plannableProductCode => "TRUEVIEW_IN_STREAM", budgetMicros => int($budget_micros * $trueview_allocation)}); push @$product_mix, Google::Ads::GoogleAds::V11::Services::ReachPlanService::PlannedProduct-> new({ plannableProductCode => "BUMPER", budgetMicros => int($budget_micros * $bumper_allocation)}); my $reach_request = build_reach_request($customer_id, $product_mix, $location_id, $currency_code); pull_reach_curve($reach_plan_service, $reach_request); }
Reach forecast request fields
You must set the customer_id
, campaign_duration
, and planned_products
fields for each GenerateReachForecastRequest
.
The following optional fields are also available:
Field | Description |
---|---|
currency_code |
Three-character ISO 4217 currency code. |
cookie_frequency_cap_setting |
The maximum number of times an ad can be shown to the same user during a specified time interval. This cap can be defined as a limit per one day, one week, or one month. |
min_effective_frequency |
Minimum number of times a person was exposed to the ad for the reported reach metrics. |
targeting |
The targeting to be applied to all products selected in the product mix, including location, age range, genders, devices, and network. |