If anything you run touches the Google Ads API — a bid-management tool, an agency reporting pipeline, a Looker Studio connector, a budget-pacing script, a custom dashboard pulling spend into a warehouse — you are on a clock you may not have noticed. Google moved the Google Ads API to a monthly release cycle in 2026, and each major version now lives for roughly one year after launch. Versions are sunsetting faster than they used to, and quietly.
The dates that matter right now:
v20 sunset on June 10, 2026. Requests on v20 now fail.
v21 sunsets around August 6, 2026 — one year after its August 6, 2025 release. ().
The field still exists. Your query still succeeds. But the value you were branching on never arrives anymore. Consider the canonical reporting shape — bucket spend by ad type:
CODETYPE_LABELS = {
"VIDEO_BUMPER": "Bumper",
"VIDEO_OUTSTREAM": "Outstream", # v23: this enum value no longer exists
"VIDEO_TRUEVIEW_IN_STREAM": "In-stream",
}
for row in ga_service.search(customer_id=cid, query=GAQL):
label = TYPE_LABELS.get(row.ad_group_ad.ad.type_.name, "Other")
spend[label] += row.metrics.cost_micros
After the bump, rows that used to come back as
VIDEO_OUTSTREAMcome back asUNKNOWN/UNSPECIFIED(or are reshaped onto a different type).TYPE_LABELS.get(...)doesn't raise — it returns"Other". Your outstream spend silently collapses into a catch-all bucket, or vanishes if you filter that bucket out. The query is valid, the response is 200, the totals at the campaign level still reconcile, and the only symptom is that one row in a breakdown table is wrong. Nobody alerts on that.
The same trap applies anywhere you
WHEREorGROUP BYone of these enums, and to theif channel_sub_type == ...branches that route campaigns to different handlers. A branch that no longer matches doesn't error — it falls through to the default.
2. Asset performance metrics come back empty, not missing
v22 removed
AssetPerformanceLabelfor Performance Max campaigns. v23 went further: it removed aggregate asset performance-label metrics, and the performance-label enum is no longer returned for Search and Display ().
If you wrote graceful budget-error handling — the kind that catches the API's "your daily budget is below the minimum" error and surfaces the actual minimum to the user — you almost certainly hard-coded the misspelled field name, because that's what the API gave you:
CODEexcept GoogleAdsException as ex:
for error in ex.failure.errors:
details = error.details.budget_per_day_minimum_error_details
# pre-v22 field name; returns proto default (0) after the bump
user_message = f"Minimum daily budget is {details.minimum_bugdet_amount_micros / 1e6}"
After the bump,
minimum_bugdet_amount_microsno longer exists on the proto; accessing it returns the default0(proto3 doesn't raise on unknown-as-default access in several client libraries). Your error handler now tells users the minimum budget is $0.00 — at exactly the moment they're hitting a budget error and need a real number. It fires only in the error path, which is the path with the thinnest test coverage.
Bonus: the Feeds removal is the loud one — but it relocated your extension data
v23 removed all feed-related entities —
Feed,FeedMapping,FeedService,AdGroupFeed,feed_placeholder_view, and the rest. Queries against those resources fail loudly, so you'll catch the direct breakage. The quieter follow-on: sitelinks, callouts, and structured snippets that used to live in Feeds now live in Assets, with a different shape and different IDs. Reporting that aggregated extension performance by feed item has to be rebuilt against the asset model, and the rebuild is where double-counting and dropped extensions creep in. That's a migration project, not a one-line version bump.
What to grep for before August 6
CODE# Direct version pins in client config / URLs
grep -rnE 'v2[0-3]|google-ads.*version|GOOGLE_ADS_API_VERSION' src/ config/
# Enum values removed or reshaped in v22/v23
grep -rnE 'VIDEO_OUTSTREAM|AssetPerformanceLabel|url_expansion_opt_out' src/
# The renamed error field
grep -rn 'minimum_bugdet_amount_micros' src/
# Feed entities removed in v23
grep -rnE 'FeedMapping|AdGroupFeed|feed_placeholder_view|FeedService' src/
# Any switch/dict keyed on ad type or channel sub-type — audit for fall-through defaults
grep -rnE 'ad_group_type|advertising_channel_sub_type|ad\.type_' src/
The non-grep check is the persisted-config sweep: any Supermetrics, Funnel, Adverity, or homemade connector with a pinned
v20/v21in its settings needs re-pointing, and every dashboard that buckets by ad type or reads an asset performance label needs a spot-check against a v23 response before the cutover — not after.
Why a forced upgrade fails silently
The mental model for a version sunset is "it either works or it 503s." For the transport layer, that's true. For the data, it isn't. GAQL keeps answering, the protos keep deserializing, and the values inside them quietly change contract: an enum loses a member, a metric stops populating, a field gets renamed out from under your accessor. Every one of those returns 200.
The window between now and August 6 is when the wrong code ships, because both behaviors are observable at once: v21 still answers, v23 already answers, the tests pass, and the dashboards have numbers in them. The numbers are just bucketed wrong, blanked, or zeroed — and "the report has data" is not the same as "the report is right."
FlareCanary watches the response shapes of the API endpoints you depend on and tells you when an enum value disappears, a metric goes empty, or a field gets renamed between versions — the drift that returns 200 and slips past a green healthcheck. A forced version sunset like Google Ads v21 → v23 is exactly the transition where passing tests don't mean what you think they mean. flarecanary.com
↗ Original-Artikel auf dev.to lesenVollständiger Original-ArtikelDen kompletten Beitrag mit allen Details direkt auf dev.to lesen.
SOCIAL SHARE CARD GENERATOR