Skip to content

Instantly share code, notes, and snippets.

@patreeceeo
Created February 12, 2025 21:57
Show Gist options
  • Save patreeceeo/26845f4caa8d275bf4893e58dd77d27a to your computer and use it in GitHub Desktop.
Save patreeceeo/26845f4caa8d275bf4893e58dd77d27a to your computer and use it in GitHub Desktop.
Create Google Ads campaign with Maximize Conversion Value bid strategy?
#!/usr/bin/env python
"""
This example shows how to create a complete Responsive Search ad.
Includes creation of: budget, campaign, ad group, ad group ad,
keywords, and geo targeting.
More details on Responsive Search ads can be found here:
https://support.google.com/google-ads/answer/7684791
"""
import argparse
import sys
import uuid
import typing
from google.ads.googleads.client import GoogleAdsClient
from google.ads.googleads.errors import GoogleAdsException
from google.ads.googleads.v18.services.types.campaign_service import CampaignOperation
from google.ads.googleads.v18.services.types.campaign_budget_service import CampaignBudgetOperation
from google.ads.googleads.v18.services.types.ad_group_service import AdGroupOperation
from google.ads.googleads.v18.services.types.ad_group_ad_service import AdGroupAdOperation
from google.ads.googleads.v18.services.types.campaign_criterion_service import CampaignCriterionOperation
from google.ads.googleads.v18.services.types.ad_group_criterion_service import AdGroupCriterionOperation
from google.ads.googleads.v18.services.services.campaign_budget_service.client import CampaignBudgetServiceClient
from google.ads.googleads.v18.services.services.campaign_service.client import CampaignServiceClient
from google.ads.googleads.v18.services.services.ad_group_service.client import AdGroupServiceClient
from google.ads.googleads.v18.services.services.ad_group_ad_service.client import AdGroupAdServiceClient
from google.ads.googleads.v18.services.services.ad_group_criterion_service.client import AdGroupCriterionServiceClient
from google.ads.googleads.v18.services.services.geo_target_constant_service.client import GeoTargetConstantServiceClient
from google.ads.googleads.v18.services.services.campaign_criterion_service.client import CampaignCriterionServiceClient
from google.ads.googleads.v18.enums.types.bidding_strategy_type import BiddingStrategyTypeEnum
from google.ads.googleads.v18.common.types.ad_asset import AdTextAsset
from google.ads.googleads.v18.services.types.geo_target_constant_service import SuggestGeoTargetConstantsRequest
# Keywords from user.
KEYWORD_TEXT_EXACT = "example of exact match"
KEYWORD_TEXT_PHRASE = "example of phrase match"
KEYWORD_TEXT_BROAD = "example of broad match"
# Geo targeting from user.
GEO_LOCATION_1 = "Buenos aires"
GEO_LOCATION_2 = "San Isidro"
GEO_LOCATION_3 = "Mar del Plata"
# LOCALE and COUNTRY_CODE are used for geo targeting.
# LOCALE is using ISO 639-1 format. If an invalid LOCALE is given,
# 'es' is used by default.
LOCALE = "es"
# A list of country codes can be referenced here:
# https://developers.google.com/google-ads/api/reference/data/geotargets
COUNTRY_CODE = "AR"
T = typing.TypeVar("T")
def get_type(client, cls: typing.Type[T]) -> T:
return client.get_type(cls.__name__)
def get_service(client, cls: typing.Type[T]) -> T:
"""Basically same as get_type, except it slices "Client" off of the end of the class name to get the service name."""
return client.get_service(cls.__name__[:-6])
def main(client, customer_id):
"""
The main method that creates all necessary entities for the example.
Args:
client: an initialized GoogleAdsClient instance.
customer_id: a client customer ID.
"""
# Create a budget, which can be shared by multiple campaigns.
campaign_budget = create_campaign_budget(client, customer_id)
campaign_resource_name = create_campaign(
client, customer_id, campaign_budget
)
ad_group_resource_name = create_ad_group(
client, customer_id, campaign_resource_name
)
create_ad_group_ad(
client, customer_id, ad_group_resource_name
)
add_keywords(client, customer_id, ad_group_resource_name)
add_geo_targeting(client, customer_id, campaign_resource_name)
def create_ad_text_asset(client, text, pinned_field=None):
"""Create an AdTextAsset.
Args:
client: an initialized GoogleAdsClient instance.
text: text for headlines and descriptions.
pinned_field: to pin a text asset so it always shows in the ad.
Returns:
An AdTextAsset.
"""
ad_text_asset = get_type(client, AdTextAsset)
ad_text_asset.text = text
if pinned_field:
ad_text_asset.pinned_field = pinned_field
return ad_text_asset
def create_campaign_budget(client, customer_id):
"""Creates campaign budget resource.
Args:
client: an initialized GoogleAdsClient instance.
customer_id: a client customer ID.
Returns:
Campaign budget resource name.
"""
# Create a budget, which can be shared by multiple campaigns.
campaign_budget_service = get_service(client, CampaignBudgetServiceClient)
campaign_budget_operation = get_type(client, CampaignBudgetOperation)
campaign_budget = campaign_budget_operation.create
campaign_budget.name = f"Campaign budget {uuid.uuid4()}"
campaign_budget.delivery_method = (
client.enums.BudgetDeliveryMethodEnum.STANDARD
)
campaign_budget.amount_micros = 500000
# Add budget.
campaign_budget_response = campaign_budget_service.mutate_campaign_budgets(
customer_id=customer_id, operations=[campaign_budget_operation]
)
return campaign_budget_response.results[0].resource_name
def create_campaign(client, customer_id, campaign_budget):
"""Creates campaign resource.
Args:
client: an initialized GoogleAdsClient instance.
customer_id: a client customer ID.
campaign_budget: a budget resource name.
Returns:
Campaign resource name.
"""
campaign_service = get_service(client, CampaignServiceClient)
campaign_operation = get_type(client, CampaignOperation)
campaign = campaign_operation.create
campaign.name = f"Testing RSA via API {uuid.uuid4()}"
campaign.advertising_channel_type = (
client.enums.AdvertisingChannelTypeEnum.SEARCH
)
# Recommendation: Set the campaign to PAUSED when creating it to prevent
# the ads from immediately serving. Set to ENABLED once you've added
# targeting and the ads are ready to serve.
campaign.status = client.enums.CampaignStatusEnum.PAUSED
# Set the bidding strategy and budget.
# The bidding strategy for Maximize Clicks is TargetSpend.
# The target_spend_micros is deprecated so don't put any value.
# See other bidding strategies you can select in the link below.
# https://developers.google.com/google-ads/api/reference/rpc/latest/Campaign#campaign_bidding_strategy
# campaign.target_spend.target_spend_micros = 0
campaign.bidding_strategy_type = BiddingStrategyTypeEnum.BiddingStrategyType.MAXIMIZE_CONVERSION_VALUE
# campaign.campaign_bidding_strategy = BiddingStrategyTypeEnum.BiddingStrategyType.MAXIMIZE_CONVERSION_VALUE
# campaign.maximize_conversion_value.target_roas = 1.0
campaign.campaign_budget = campaign_budget
# Set the campaign network options.
campaign.network_settings.target_google_search = True
campaign.network_settings.target_search_network = True
campaign.network_settings.target_partner_search_network = False
# Enable Display Expansion on Search campaigns. For more details see:
# https://support.google.com/google-ads/answer/7193800
campaign.network_settings.target_content_network = True
# # Optional: Set the start date.
# start_time = datetime.date.today() + datetime.timedelta(days=1)
# campaign.start_date = datetime.date.strftime(start_time, _DATE_FORMAT)
# # Optional: Set the end date.
# end_time = start_time + datetime.timedelta(weeks=4)
# campaign.end_date = datetime.date.strftime(end_time, _DATE_FORMAT)
# Add the campaign.
campaign_response = campaign_service.mutate_campaigns(
customer_id=customer_id, operations=[campaign_operation]
)
resource_name = campaign_response.results[0].resource_name
print(f"Created campaign {resource_name}.")
return resource_name
def create_ad_group(client, customer_id, campaign_resource_name):
"""Creates ad group.
Args:
client: an initialized GoogleAdsClient instance.
customer_id: a client customer ID.
campaign_resource_name: a campaign resource name.
Returns:
Ad group ID.
"""
ad_group_service = get_service(client, AdGroupServiceClient)
ad_group_operation = get_type(client, AdGroupOperation)
ad_group = ad_group_operation.create
ad_group.name = f"Testing RSA via API {uuid.uuid4()}"
ad_group.status = client.enums.AdGroupStatusEnum.ENABLED
ad_group.campaign = campaign_resource_name
ad_group.type_ = client.enums.AdGroupTypeEnum.SEARCH_STANDARD
# If you want to set up a max CPC bid uncomment line below.
# ad_group.cpc_bid_micros = 10000000
# Add the ad group.
ad_group_response = ad_group_service.mutate_ad_groups(
customer_id=customer_id, operations=[ad_group_operation]
)
ad_group_resource_name = ad_group_response.results[0].resource_name
print(f"Created ad group {ad_group_resource_name}.")
return ad_group_resource_name
def create_ad_group_ad(
client, customer_id, ad_group_resource_name
):
"""Creates ad group ad.
Args:
client: an initialized GoogleAdsClient instance.
customer_id: a client customer ID.
ad_group_resource_name: an ad group resource name.
Returns:
Ad group ad resource name.
"""
ad_group_ad_service = get_service(client, AdGroupAdServiceClient)
ad_group_ad_operation = get_type(client, AdGroupAdOperation)
ad_group_ad = ad_group_ad_operation.create
ad_group_ad.status = client.enums.AdGroupAdStatusEnum.ENABLED
ad_group_ad.ad_group = ad_group_resource_name
# Set responsive search ad info.
# https://developers.google.com/google-ads/api/reference/rpc/latest/ResponsiveSearchAdInfo
# The list of possible final URLs after all cross-domain redirects for the ad.
ad_group_ad.ad.final_urls.append("https://www.example.com/")
# Set a pinning to always choose this asset for HEADLINE_1. Pinning is
# optional; if no pinning is set, then headlines and descriptions will be
# rotated and the ones that perform best will be used more often.
# Headline 1
served_asset_enum = client.enums.ServedAssetFieldTypeEnum.HEADLINE_1
pinned_headline = create_ad_text_asset(
client, "Headline 1 testing", served_asset_enum
)
# Headline 2 and 3
ad_group_ad.ad.responsive_search_ad.headlines.extend(
[
pinned_headline,
create_ad_text_asset(client, "Headline 2 testing"),
create_ad_text_asset(client, "Headline 3 testing"),
]
)
# Description 1 and 2
description_1 = create_ad_text_asset(client, "Desc 1 testing")
description_2 = None
description_2 = create_ad_text_asset(client, "Desc 2 testing")
ad_group_ad.ad.responsive_search_ad.descriptions.extend(
[description_1, description_2]
)
# Paths
# First and second part of text that can be appended to the URL in the ad.
# If you use the examples below, the ad will show
# https://www.example.com/all-inclusive/deals
ad_group_ad.ad.responsive_search_ad.path1 = "all-inclusive"
ad_group_ad.ad.responsive_search_ad.path2 = "deals"
# Send a request to the server to add a responsive search ad.
ad_group_ad_response = ad_group_ad_service.mutate_ad_group_ads(
customer_id=customer_id, operations=[ad_group_ad_operation]
)
for result in ad_group_ad_response.results:
print(
f"Created responsive search ad with resource name "
f'"{result.resource_name}".'
)
def add_keywords(client, customer_id, ad_group_resource_name):
"""Creates keywords.
Creates 3 keyword match types: EXACT, PHRASE, and BROAD.
EXACT: ads may show on searches that ARE the same meaning as your keyword.
PHRASE: ads may show on searches that INCLUDE the meaning of your keyword.
BROAD: ads may show on searches that RELATE to your keyword.
For smart bidding, BROAD is the recommended one.
Args:
client: an initialized GoogleAdsClient instance.
customer_id: a client customer ID.
ad_group_resource_name: an ad group resource name.
"""
ad_group_criterion_service = get_service(client, AdGroupCriterionServiceClient)
operations = []
# Create keyword 1.
ad_group_criterion_operation = get_type(client, AdGroupCriterionOperation)
ad_group_criterion = ad_group_criterion_operation.create
ad_group_criterion.ad_group = ad_group_resource_name
ad_group_criterion.status = client.enums.AdGroupCriterionStatusEnum.ENABLED
ad_group_criterion.keyword.text = KEYWORD_TEXT_EXACT
ad_group_criterion.keyword.match_type = (
client.enums.KeywordMatchTypeEnum.EXACT
)
# Uncomment the below line if you want to change this keyword to a negative target.
# ad_group_criterion.negative = True
# Optional repeated field
# ad_group_criterion.final_urls.append('https://www.example.com')
# Add operation
operations.append(ad_group_criterion_operation)
# Create keyword 2.
ad_group_criterion_operation = get_type(client, AdGroupCriterionOperation)
ad_group_criterion = ad_group_criterion_operation.create
ad_group_criterion.ad_group = ad_group_resource_name
ad_group_criterion.status = client.enums.AdGroupCriterionStatusEnum.ENABLED
ad_group_criterion.keyword.text = KEYWORD_TEXT_PHRASE
ad_group_criterion.keyword.match_type = (
client.enums.KeywordMatchTypeEnum.PHRASE
)
# Uncomment the below line if you want to change this keyword to a negative target.
# ad_group_criterion.negative = True
# Optional repeated field
# ad_group_criterion.final_urls.append('https://www.example.com')
# Add operation
operations.append(ad_group_criterion_operation)
# Create keyword 3.
ad_group_criterion_operation = get_type(client, AdGroupCriterionOperation)
ad_group_criterion = ad_group_criterion_operation.create
ad_group_criterion.ad_group = ad_group_resource_name
ad_group_criterion.status = client.enums.AdGroupCriterionStatusEnum.ENABLED
ad_group_criterion.keyword.text = KEYWORD_TEXT_BROAD
ad_group_criterion.keyword.match_type = (
client.enums.KeywordMatchTypeEnum.BROAD
)
# Uncomment the below line if you want to change this keyword to a negative target.
# ad_group_criterion.negative = True
# Optional repeated field
# ad_group_criterion.final_urls.append('https://www.example.com')
# Add operation
operations.append(ad_group_criterion_operation)
# Add keywords
ad_group_criterion_response = (
ad_group_criterion_service.mutate_ad_group_criteria(
customer_id=customer_id,
operations=operations,
)
)
for result in ad_group_criterion_response.results:
print("Created keyword " f"{result.resource_name}.")
def add_geo_targeting(client, customer_id, campaign_resource_name):
"""Creates geo targets.
Args:
client: an initialized GoogleAdsClient instance.
customer_id: a client customer ID.
campaign_resource_name: an campaign resource name.
Returns:
Geo targets.
"""
geo_target_constant_service = get_service(client, GeoTargetConstantServiceClient)
# Search by location names from
# GeoTargetConstantService.suggest_geo_target_constants() and directly
# apply GeoTargetConstant.resource_name.
gtc_request = get_type(client, SuggestGeoTargetConstantsRequest)
gtc_request.locale = LOCALE
gtc_request.country_code = COUNTRY_CODE
# The location names to get suggested geo target constants.
gtc_request.location_names.names.extend(
[GEO_LOCATION_1, GEO_LOCATION_2, GEO_LOCATION_3]
)
results = geo_target_constant_service.suggest_geo_target_constants(
gtc_request
)
operations = []
for suggestion in results.geo_target_constant_suggestions:
print(
"geo_target_constant: "
f"{suggestion.geo_target_constant.resource_name} "
f"is found in LOCALE ({suggestion.locale}) "
f"with reach ({suggestion.reach}) "
f"from search term ({suggestion.search_term})."
)
# Create the campaign criterion for location targeting.
campaign_criterion_operation = get_type(
client,
CampaignCriterionOperation
)
campaign_criterion = campaign_criterion_operation.create
campaign_criterion.campaign = campaign_resource_name
campaign_criterion.location.geo_target_constant = (
suggestion.geo_target_constant.resource_name
)
operations.append(campaign_criterion_operation)
campaign_criterion_service = get_service(client, CampaignCriterionServiceClient)
campaign_criterion_response = (
campaign_criterion_service.mutate_campaign_criteria(
customer_id=customer_id, operations=[*operations]
)
)
for result in campaign_criterion_response.results:
print(f'Added campaign criterion "{result.resource_name}".')
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description=("Creates a Responsive Search Ad for specified customer.")
)
# The following argument(s) should be provided to run the example.
parser.add_argument(
"-c",
"--customer_id",
type=str,
required=True,
help="The Google Ads customer ID.",
)
args = parser.parse_args()
# GoogleAdsClient will read the google-ads.yaml configuration file in the
# home directory if none is specified.
googleads_client = GoogleAdsClient.load_from_storage(version="v18")
try:
main(
googleads_client,
args.customer_id,
)
except GoogleAdsException as ex:
print(
f'Request with ID "{ex.request_id}" failed with status '
f'"{ex.error.code().name}" and includes the following errors:'
)
for error in ex.failure.errors:
print(f'Error with message "{error.message}".')
if error.location:
for field_path_element in error.location.field_path_elements:
print(f"\t\tOn field: {field_path_element.field_name}")
sys.exit(1)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment