Created
February 12, 2025 21:57
-
-
Save patreeceeo/26845f4caa8d275bf4893e58dd77d27a to your computer and use it in GitHub Desktop.
Create Google Ads campaign with Maximize Conversion Value bid strategy?
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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