Target specific countries and languages
You want a campaign to show only in the United States and the United Kingdom, to English speakers. In the Google Ads UI that’s a few dropdowns. In bidsmith it’s two lines on the campaign — and a reviewer can read exactly what changed in the pull request.
The change
Add locations and languages to the google_ads_campaign block:
resource "google_ads_campaign" "search_brand" { name = "Search_Brand" advertising_channel_type = "SEARCH" campaign_budget = google_ads_campaign_budget.brand.id
languages = ["en"] locations = ["US", "GB"]}locationsuses two-letter ISO country codes —"US","GB","CA","DE","SG", and so on.languagesuses short language codes —"en","de","fr","pl","zh-CN".
Widening the campaign to Canada later is the kind of one-word diff that reviews itself:
locations = ["US", "GB", "CA"]Only people actually there
By default, locations = ["US"] does not mean “people in the United
States”. Google reads it as people in the US plus people anywhere in
the world showing interest in the US — searching for US destinations,
watching US content. For a brand campaign that extra reach is often
welcome. For anything where the market is the experiment, it’s a
confound: run a US, a German, and a French version of the same test and
all three can quietly serve to the same third-country audience, so the
per-market numbers stop being comparable.
Say which you mean:
resource "google_ads_campaign" "search_brand" { # ... locations = ["US", "GB"]
geo_target_type_setting { positive_geo_target_type = "PRESENCE" negative_geo_target_type = "PRESENCE" }}PRESENCE— people in (or regularly in) those countries.PRESENCE_OR_INTEREST— those people plus anyone interested in them. Google’s default.
The negative side answers the same question for locations you exclude.
Leave the block out and bidsmith leaves the setting to the account;
write it and a flip in the Google Ads UI shows up in the next plan as
drift. This is the same setting the UI calls
advanced location options,
buried a level down in the locations panel.
Plan and apply
bidsmith plan .Expected: one + create row per new country/language criterion, a
~ update on the campaign if you changed how those locations are read,
and nothing else churns. Commit, PR, merge, apply.
Cities, regions, and uncommon languages
The shipped tables cover every country and the major languages. For a
city or region, drop the raw geoTargetConstants/NNNN id into the same
list — look it up in Google’s
geo target table:
# United States, plus the New York City metro area locations = ["US", "geoTargetConstants/1023191"]The same goes for a language that isn’t in the shipped set — use its
languageConstants/NNNN id.
Converting an existing campaign
If your campaign already targets locations and languages through
explicit google_ads_campaign_criterion resources, you can collapse
them onto the campaign and delete the standalone blocks:
# Before — one resource per targetresource "google_ads_campaign_criterion" "geo_us" { campaign = google_ads_campaign.search_brand.id location { geo_target_constant = "geoTargetConstants/2840" }}
# After — inline on the campaignresource "google_ads_campaign" "search_brand" { # ... locations = ["US"]}bidsmith identifies live criteria by their resolved target, not by an
address, so this conversion plans as no change at all — no campaign
recreation, no lost history. Running bidsmith refresh on an existing
account writes the inline form for you automatically.
Demand Gen campaigns pick a level first
Demand Gen is the one channel where Google asks where the targeting lives before it accepts any: on the campaign, or on its ad groups — never both. Every new Demand Gen campaign starts in ad-group mode unless it opts out at creation, and the choice can’t be changed afterwards.
To keep the two-line form above, add the opt-out:
resource "google_ads_campaign" "demand_gen_fr" { name = "DemandGen_FR" advertising_channel_type = "DEMAND_GEN" campaign_budget = google_ads_campaign_budget.dg.id target_spend {}
languages = ["fr"] locations = ["FR"]
demand_gen_campaign_settings { upgraded_targeting = false }}Or leave the campaign in ad-group mode and declare the targeting per ad group instead — useful when one campaign runs a French and a German ad group side by side:
resource "google_ads_ad_group_criterion" "dg_fr_language" { ad_group = google_ads_ad_group.dg_fr.id
language { language_constant = "languageConstants/1002" }}
resource "google_ads_ad_group_criterion" "dg_fr_location" { ad_group = google_ads_ad_group.dg_fr.id
location { geo_target_constant = "geoTargetConstants/2250" }}Mixing the two levels — languages on a Demand Gen campaign without
the opt-out, or ad-group criteria under a campaign that declared it —
is a validate error, because Google would reject the apply with an
error message that names no cause. For a campaign that already exists,
match the level it was created with: bidsmith refresh writes the
opt-out block for campaigns that have it, and plan warns when a file
declares the other level.
See also
google_ads_campaign— thelanguages/locationsreference.google_ads_campaign_criterion— negatives, proximity, and the explicit form.- Turn many cloned campaigns into one template — the next step once targeting is the only thing that varies.