Data Measured

artifact #A-012 · sql · current

Language criterion audit (GAQL), with the label rule

The query that pulls every campaign-level language criterion with its campaign channel type, so each row can be labelled inert, partial or live before Google Ads stops using the setting on Search in late September 2026. The criterion keeps returning after the change, so this snapshot is the only surviving record of what the account asked for.

verified_on
2026-09-10
checked against
Google Ads API — campaign_criterion field reference (v25)Google Ads API — ContextErrorGoogle Ads Developer Blog — language targeting changes starting September 2026 (13 August 2026)Google Ads Help — About language targeting
next re-check
2026-12-01
status
current
time
20 minutes
skill
you can run a GAQL query

What's inside

  • campaign_criterion.language.language_constant with language_constant.code and .name
  • campaign.advertising_channel_type and .advertising_channel_sub_type, the field that decides the label
  • The label rule: SEARCH is inert, PERFORMANCE_MAX is partial, DISPLAY / VIDEO / DEMAND_GEN are unchanged
  • The reporting rule: stop sourcing a language dimension from this table for Search rows

Install

  1. Run in the Google Ads API (v25 or later) or in Google Ads Scripts with AdsApp.search.
  2. Store the result as a dated snapshot before late September 2026; the criterion will still return afterwards, unchanged and inert.
  3. Add the label column from campaign.advertising_channel_type and join it to any dashboard that segments by language.
  4. Grep your codebase for writes to CampaignCriterion.language and split the Search path out: on Search it now raises ContextError.OPERATION_NOT_PERMITTED_FOR_CONTEXT, on Performance Max it still succeeds.

The file · language-criterion-audit.gaql

Download
-- Google Ads Query Language: every campaign-level language criterion, with the campaign
-- it sits on, so each row can be labelled after the late-September 2026 change.
-- campaign and language_constant are attributed resources of campaign_criterion,
-- so their fields may be selected alongside it.
SELECT
  campaign.id,
  campaign.name,
  campaign.advertising_channel_type,
  campaign.advertising_channel_sub_type,
  campaign.status,
  campaign_criterion.criterion_id,
  campaign_criterion.status,
  campaign_criterion.negative,
  campaign_criterion.language.language_constant,
  language_constant.code,
  language_constant.name
FROM campaign_criterion
WHERE campaign_criterion.type = 'LANGUAGE'
  AND campaign.status != 'REMOVED'
ORDER BY campaign.advertising_channel_type, campaign.name

-- Label each row from campaign.advertising_channel_type:
--   SEARCH            -> INERT.   The setting no longer targets. Writes to
--                                 CampaignCriterion.language return
--                                 ContextError.OPERATION_NOT_PERMITTED_FOR_CONTEXT.
--   PERFORMANCE_MAX   -> PARTIAL. Dead on the Google Search Network, live on YouTube,
--                                 Display, Discover and Gmail. Shopping ads inside
--                                 Performance Max are not governed by it. Writes
--                                 still succeed, so nothing tells you it half-applies.
--   DISPLAY / VIDEO / DEMAND_GEN -> LIVE. Unchanged.
--
-- Keep this snapshot. After the change it is the only record of what you asked for.
-- Do not report a language dimension from this table for SEARCH rows. The signal that
-- decides delivery is the language of the ad and the landing page, and it has no field
-- in the API: there is no segments.language, and no language attribute on ad_group_ad
-- or on a text asset. The one language field left in reporting is segments.product_language,
-- which comes from the product feed.

Breaks on

The logged changes that can invalidate this artifact. A new one triggers a re-check.

Stamped 10 Sept 2026 · re-checked quarterly or on change · corrections to team@datameasured.com · ← the library