superdesigndev/treg · Archived

google-ads

Traps when running Google Ads through treg — the API requirements and cleanup semantics that cost round-trips or money. Use whenever asked to analyse ad performance, audit spend, create or change campaigns, adjust budgets or bids, or do media buying.

First seen Aug 14, 2026

Installation

$ npx skills add superdesigndev/treg --skill google-ads

Stronger alternatives

This repository is archived — consider an actively maintained alternative.

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from superdesigndev/treg.

npx skills add superdesigndev/treg

Browse all from superdesigndev/treg

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Repository health

Stars 1.2K
License LICENSE
Default branch main
Open issues 5
Status Archived

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 7,604 B
  • docs SUMMARY.md 270 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 1 installs

SKILL.md

Google Ads via treg — the traps

Verified live on 2026-07-22 against a live account, via treg call google-ads. Each trap is tagged with how many independent agents hit it when working without this file, so you know which are real and which are here for insurance.

Account id: treg connections ls → the google-ads row; treg connections resources <id> to list. Never guess an account id — you may be spending someone else's money.

1. Pin the API version — there is no discovery ⚠️ 2/2 agents hit this

Paths are versioned and a wrong guess returns a Google HTML 404, not JSON. Nothing in the API or in treg tool ls reports the current version — one agent burned five calls walking v14→v18 before recovering the answer from the treg calls audit log.

Use v25 (released 2026-07-22, live-verified through the proxy 2026-08-17; sunsets ~Aug 2027). Do not guess downward.

Two different failures, two different fixes:

  • HTML 404 — the version never existed (you guessed too high, or typo'd the path).
  • JSON 400 UNSUPPORTED_VERSION — the version existed and has been sunset. Google ships a

major roughly quarterly and each lives ~12 months, so a pin that worked for months dies on a date, not on a deploy. v21 died this way on 2026-08-05. Always jump to the newest GA version, not the next one up — the next one up may be months from its own sunset.

Release notes and sunset dates: <https://developers.google.com/google-ads/api/docs/sunset-dates>;

2. containseupolitical_advertising is required on campaign create ⚠️ 3/3 hit this

Absent from essentially every code sample. Campaign create fails REQUIRED without it:

"containsEuPoliticalAdvertising": "DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING"

3. Bidding strategy must be the sub-message, not the enum ⚠️ 1/2 hit this

"biddingStrategyType":"TARGETSPEND" alone fails REQUIRED on campaignbidding_strategy. It's a protobuf oneof — send the field itself: "targetSpend":{}, "manualCpc":{}, "maximizeConversions":{}. The enum is output-only.

4. Removing a parent orphans children — and their status lies ⚠️ 2/2 confused by this

Remove a campaign and its ad groups/criteria/ads become immutable but keep reporting their old status — a removed campaign's ad group still reads ENABLED. Mutating one returns OPERATIONNOTPERMITTEDFORREMOVED_RESOURCE.

Verify teardown on campaign.status, never on a child's. Checking the child looks like cleanup silently failed. Remove campaign first, then its budget — the budget is not removed for you, and an orphaned budget is easy to leave behind.

Nothing is ever hard-deleted: REMOVED is terminal. Filter with WHERE campaign.status != 'REMOVED' or your campaign list fills with corpses.

5. Money is in micros ✅ 0/2 got this wrong — kept for asymmetry

amountMicros: 3000000 = $3.00/day. Both agents handled this correctly, so it is not a common failure — but a 10⁶ slip creates a $3,000/day budget that the API accepts without any error, and it is the only mistake here that spends money silently. Compute dollars * 1000000 explicitly and re-read it before sending.

Currency is not always USD — the account may bill in a non-USD currency (e.g. AUD):

SELECT customer.currency_code, customer.time_zone FROM customer

6. updateMask controls what changes

{"operations":[{"updateMask":"amount_micros",
  "update":{"resourceName":"customers/<CID>/campaignBudgets/<BID>","amountMicros":3000000}}]}

Only listed fields change; a field you set but don't list is ignored, one you list but don't set is cleared. Both amount_micros and amountMicros are accepted (verified).

7. login-customer-id failures look like auth failures

Acting on a client account under a manager needs the header:

treg call google-ads "<path>" --method POST --header 'login-customer-id: 9876543210' --data '...'

A wrong or unauthorised value returns 401 UNAUTHENTICATED, not a targeting error. Check the header before debugging OAuth.

validateOnly — free insurance on any mutation

{"validateOnly":true,"operations":[...]}   // returns {} on success, changes nothing

Verified: a validateOnly budget change returned {} and left the value untouched. Neither unskilled agent used it and neither needed it — their mistakes were rejected by the API anyway. Its real value is the case they never hit: a mutation that is valid but wrong (a mistyped budget), which no error will catch. Use it whenever the operation spends money or you can't cheaply undo it.

Reading performance

treg call google-ads "v25/customers/<CID>/googleAds:search" --method POST --data '{
  "query":"SELECT campaign.name, campaign.status, campaign_budget.amount_micros,
           metrics.impressions, metrics.clicks, metrics.cost_micros, metrics.conversions
           FROM campaign WHERE segments.date DURING LAST_30_DAYS
           ORDER BY metrics.cost_micros DESC"}'

Metrics need a date condition (DURING LAST30DAYS, or BETWEEN '2026-06-01' AND '2026-06-30') — without one you get lifetime totals. Use googleAds:searchStream for large unpaginated pulls. Keyword-level detail lives in keyword_view.

On zero conversions (✅ 2/2 agents already handle this): an audited account showed ~$300 spent, ~170 clicks, 0 conversions because nothing is instrumented. Both unskilled agents correctly said they couldn't distinguish "no tracking" from "no results" — keep doing that, and check before treating zero as a performance verdict.

Creating a campaign — minimum that works

Budget first, then campaign. Verified end-to-end.

// campaignBudgets:mutate
{"operations":[{"create":{"name":"...","amountMicros":3000000,
  "deliveryMethod":"STANDARD","explicitlyShared":false}}]}

// campaigns:mutate
{"operations":[{"create":{
  "name":"...", "status":"PAUSED",
  "advertisingChannelType":"SEARCH",
  "campaignBudget":"customers/<CID>/campaignBudgets/<BID>",
  "manualCpc":{},
  "containsEuPoliticalAdvertising":"DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING",
  "networkSettings":{"targetGoogleSearch":true,"targetSearchNetwork":false,"targetContentNetwork":false}
}}]}

Then adGroups:mutate → adGroupCriteria:mutate (keywords) → adGroupAds:mutate (responsive search ad). A campaign with no ad group, keywords or ads cannot serve, whatever its status.

✅ Both unskilled agents defaulted to PAUSED on a real-money account without being told, and explained why. Keep that default; enabling is a one-field update, an unwanted live campaign is a refund request.

Test accounts solve less than you'd hope

Created under a test manager — a separate Google account; a production manager cannot host them. They have no serving data and cannot test conversion uploads, so they only prove mutation mechanics. Performance and conversion work must happen on a live account.