Specifying a Media Plan

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 desired 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.

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)
{
    $trueValue = new BoolValue(['value' => true]);
    $falseValue = new BoolValue(['value' => false]);

    $preferences = new Preferences([
        'has_guaranteed_price' => $trueValue,
        'starts_with_sound' => $trueValue,
        'is_skippable' => $falseValue,
        'top_content_only' => $trueValue,
        'ad_length' => ReachPlanAdLength::FIFTEEN_OR_TWENTY_SECONDS
    ]);

    $mixResponse = $googleAdsClient->getReachPlanServiceClient()->generateProductMixIdeas(
        $customerId,
        new StringValue(['value' => self::LOCATION_ID]),
        new StringValue(['value' => self::CURRENCY_CODE]),
        new Int64Value(['value' => 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

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::V5::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::V5::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. Please keep in mind that these values change periodically, so it is best to provide your users with live results rather than persisting responses offline.

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(
        new StringValue(['value' => self::LOCATION_ID])
    );

    print 'Plannable Products for Location ID ' . self::LOCATION_ID . ':' . PHP_EOL;
    foreach ($response->getProductMetadata() as $product) {
        /** @var ProductMetadata $product */
        print $product->getPlannableProductCodeUnwrapped() . ':' . 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: A google.ads.google_ads.client.GoogleAdsClient instance.
      location_id: Location ID to plan for. You can get a valid loction ID from
        https://developers.google.com/adwords/api/docs/appendix/geotargeting or
        by calling ListPlannableLocations on the ReachPlanService.
    """
    reach_plan_service = client.get_service("ReachPlanService", version="v5")
    response = reach_plan_service.list_plannable_products(
        plannable_location_id=_string_value(client, location_id)
    )
    print("Plannable Products for Location ID {}".format(location_id))
    print(response)

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\n", $product->{plannableProductCode};
    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 desired budget to each product.

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' => new StringValue(['value' => 'TRUEVIEW_IN_STREAM']),
            'budget_micros' => new Int64Value([
                'value' => self::BUDGET_MICROS * $trueviewAllocation
            ])
        ]),
        new PlannedProduct([
            'plannable_product_code' => new StringValue(['value' => 'BUMPER']),
            'budget_micros' => new Int64Value([
                'value' => 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

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/google.ads.googleads.v5.services#reachplanservice
  push @$product_mix,
    Google::Ads::GoogleAds::V5::Services::ReachPlanService::PlannedProduct->new(
    {
      plannableProductCode => "TRUEVIEW_IN_STREAM",
      budgetMicros         => int($budget_micros * $trueview_allocation)});
  push @$product_mix,
    Google::Ads::GoogleAds::V5::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 Desired 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.