# Google Ads: read, manage and create

*"How are my Google ads doing?", "List my Google campaigns", "Pause my Google campaign", "Set its budget to €20/day", "Create a Google Search campaign"*

## Contents

- Account ids
- Read chain
- Deeper reads
- Manage: status, budgets, bidding, keywords, targeting
- Add an ad group to an existing campaign
- Extensions
- Never ask what a read tool can answer
- Create a Search campaign
- Create a Performance Max campaign
- Create a Demand Gen campaign
- Create a standard Shopping campaign
- Create a Display campaign
- Create an App campaign
- Upload a video file
- Notes

## Account ids

Google Ads accounts use a **bare 10-digit customer ID** (never `act_`-prefixed). Never pass one platform's ID to the other platform's tools.

## Read chain

1. `adsap_ping`: reports whether Google Ads is connected.
2. `adsap_google_list_ad_accounts`: resolve the customer ID.
3. `adsap_google_list_campaigns`: status, channel type such as Search or Performance Max, daily budget.
4. `adsap_google_get_insights` with `level: 'campaign' | 'ad_group' | 'keyword'`: account summary with previous-period comparison, per-entity table, optional daily timeseries. Keyword row ids are `"<ad_group_id>:<criterion_id>"`; pass them as-is to the manage tools with `entity_type: 'keyword'`.

## Deeper reads

- `adsap_google_search_terms`: the REAL user queries that triggered Search ads (per-term impressions, clicks, cost and conversions; `status: 'NONE'` = not yet a keyword). High-cost zero-conversion terms are negative-keyword candidates; high-converting ones are worth adding as exact keywords.
- `adsap_google_list_conversion_actions`: the account's conversion setup. Run it BEFORE any conversion-based bidding choice: Maximize Conversions and `target_cpa` need at least one ENABLED primary action, `target_roas` additionally needs conversion VALUE history. Warn the user instead of letting the create fail.
- `adsap_google_get_conversion_breakdown`: the RESULTS to that previous tool's SETUP, splitting conversions by conversion action (Purchase vs Lead vs Sign-up) instead of one lumped total. This is the Google twin of Meta custom-conversion reporting. Reach for it when the user asks which conversions the account is getting, whether a specific action is firing, or why All conversions sits above Conversions. `all_conversions` counts every action; `conversions` counts only the primary-for-goal ones that bidding optimizes toward, which is exactly why the two differ. Takes `level: 'account' | 'campaign'` and an optional `campaign_id` (which forces campaign level), and returns each action's share of the total. Google does not report cost segmented by conversion action and none can be derived, so this tool has no spend or CPA column. Take cost from `adsap_google_get_insights`.
- `adsap_google_list_audiences`: Audience resources (**these ids are what the PMax and Demand Gen `audience_ids` inputs expect**) plus UserLists with approximate sizes.
- `adsap_google_list_creative_assets`: the account's existing IMAGE, YOUTUBE_VIDEO and TEXT assets with per-image slot eligibility (`eligible_field_types`). The returned `asset_id`s plug straight into the PMax, Demand Gen, Display and App create tools' image inputs, so re-using an image needs no re-upload. Google assets are immutable and non-deletable; identical re-uploads dedupe to the same id. Pass `usage_for_asset_id` to see where one asset is linked (campaign, ad group or PMax asset group) before unlinking it.
- `adsap_google_list_ad_templates`: wizard-saved campaign setups per channel (budget, bidding, geo, languages, ad text, extensions, asset picks; users create and apply them in the Adsap web wizards). Read one to prefill a create-tool call or to answer "what's our standard setup"; `account_bound: true` templates carry uploaded asset ids and only fit their `customer_id`, portable ones fit any account.
- Asset automation: the PMax and Search create tools accept `campaign.asset_automation` (`{TYPE: 'OPTED_IN' | 'OPTED_OUT'}`; PMax takes all five types; Search ONLY `TEXT_ASSET_AUTOMATION` and `FINAL_URL_EXPANSION_TEXT_ASSET_AUTOMATION`, Google rejects the others there) and the Display create accepts `ad.control_spec` (`enable_asset_enhancements`, `enable_autogen_video`). This is the way to opt Google's AI enhancements out at create time; absent means Google's defaults apply.

## Manage: status, budgets, bidding, keywords, targeting

- `adsap_google_set_entity_status` pauses or activates campaigns, ad groups, ads or keywords (max 20 per call).
- `adsap_google_update_entity` renames a campaign or ad group and sets a campaign's **daily budget in the account currency** (for example `12.50`, never micros or cents). It changes a campaign's **bidding** through `updates.bidding`: swap scheme or edit tCPA, tROAS or ceiling; portfolio strategies are rejected; a per-channel scheme whitelist is enforced. It sets a keyword's **max CPC** through `updates.cpc_bid`, only used under Manual CPC. It edits **asset automation** through `updates.asset_automation`, friendly keys, `true` = opt in. At campaign level that means PMax (all 5 toggles) and Search (`text_asset_automation` and `final_url_expansion` only). At AD level it means Demand Gen ads: image ads take `design_versions_for_images` and `videos_from_other_assets`, video ads take `landing_page_text`, `landing_page_preview`, `shorter_youtube_videos` and `vertical_youtube_videos`. Google rejects turning text automation off while final URL expansion stays on, so turn both off in the same call. A budget warning comes back when the campaign shares its budget with others: relay it.
- `adsap_google_manage_keywords` adds keywords or negatives after creation. Op `add` puts keywords into an existing ad group, including ad-group negatives via `negative: true`. Op `add_negative` adds campaign-level negatives. Op `replace` makes an ATOMIC text or match-type change: the keyword gets a NEW id and history restarts. Op `remove` removes one. Keyword TEXT is immutable; that is what op `replace` is for.
- `adsap_google_manage_targeting` edits a campaign or ad group's targeting after creation: geo and language AND audiences. Op `add` takes locations (`negative: true` = exclude) and/or languages in one atomic batch, OR `audiences`, never combined with geo or language in one call. Audience items are `user_list_id` at campaign level, where TARGET works on Search and Shopping only and EXCLUDE also on Display and PMax. At ad-group level, via `level: "ad_group"` plus `ad_group_id`, target and exclude work on Search, Display and Shopping. Demand Gen instead takes `audience_id` items on the AD GROUP only. The same user list cannot sit at both levels. `mode` `targeting` | `observation` sets the audience setting. With no mode set Google defaults to TARGETING, which NARROWS delivery, so prefer `observation` on Search and Shopping unless narrowing is wanted. Op `remove` takes one criterion as `"<parent_id>:<criterion_id>"`, and `level` picks campaign vs ad group. Geo and language criterion ids EQUAL the constant id (France on campaign 123 is `"123:2250"`), but AUDIENCE criterion ids are Google-internal: capture `created_criteria[].entity_id` from the add response. Channel rules Google enforces on geo and language: Search takes locations but NO languages any more. Google removes language targeting from Search campaigns in late September 2026, and Search ads match on the language of the ad itself. Never add languages to a Search campaign. If a user asks, explain that Google now handles it automatically. Display takes everything, Shopping has no language targeting, Performance Max rejects EXCLUDED locations. Demand Gen rejects campaign-level targeting entirely: its targeting lives at the ad-group level, so set it at creation. Criteria are immutable: change = remove then re-add.
- All manage tools support `dry_run`: the same preview-then-confirm rule as Meta writes. Google's middle level is the **ad group** (not "ad set"), and Google ads have no editable name.

## Add an ad group to an existing campaign

`adsap_google_create_ad_group` is the only way to add structure to a campaign that already exists (Search, Display or Demand Gen; the create-campaign tools always build a fresh one). ONE atomic create: ad group plus optional keywords (Search) plus optionally ONE ad. Declare `channel` matching the campaign's type from `adsap_google_list_campaigns`; the tool verifies and rejects mismatches. PMax uses asset groups (not supported); App and Shopping ad groups are also unsupported. **Safety default: if the parent campaign is ACTIVE the tool creates the new ad group PAUSED**, so it never starts spending unreviewed. Relay the warning, and pass `ad_group.status: 'ENABLED'` only on an explicit ask. Under a PAUSED campaign the ad group starts ENABLED. Ad shapes match the corresponding create-campaign tools (Search RSA, Display RDA, Demand Gen multi_asset or video_responsive). Demand Gen ad groups take geo at the AD GROUP level (`ad_group.geo.location_ids` required there); Search and Display geo lives on the campaign and cannot be set here. An ad group created without an ad (or a Search ad group without keywords) will not serve; a warning says so, relay it. Same rules as other writes: `dry_run: true` first, always. **Existing-group mode:** pass `ad_group_id` INSTEAD of `ad_group` to add ONE responsive search ad to an ad group that already exists. Search only, and `ad` is then required. The tool verifies the group exists, is not removed, and belongs to `campaign_id`. The new ad's own `ad.status` defaults to **PAUSED**. Pass `ENABLED` only on an explicit ask, since inside a running campaign and group an enabled ad serves as soon as Google approves it. This is the same lane the web app's Google Ad Sheet "Launch" uses.

## Extensions

Sitelinks, callouts, structured snippets, call, promotion and price, at campaign OR account level. `adsap_google_list_extensions` shows what is linked per campaign AND at account level (rows with `level: 'account'` and `campaign_id: null` serve with all eligible campaigns; optionally filter by `campaign_id` or `field_type`; no performance metrics in v1). Then `adsap_google_manage_extension`. Op `'create_and_link'` creates up to 20 new extensions and links them in one atomic call, `dry_run: true` first. Ops `'link'` and `'unlink'` attach or detach EXISTING assets. Unlinking is how you stop an extension serving, because Google Ads assets cannot be deleted. Unlink at the SAME level it is linked. Op `'update_asset'` edits an existing asset. **Assets are account-scoped, so an update changes the extension EVERYWHERE it is linked**: warn the user before editing a widely-linked asset. `level: 'campaign'` (default) needs `campaign_id`; `level: 'account'` takes none; all six types are accepted at account level. Rows with source `AUTOMATICALLY_CREATED` are Google-automated: treat them as read-mostly. Sitelink rules: link text at most 25 chars, descriptions at most 35 chars and BOTH or NEITHER. Callouts at most 25 chars. Structured snippets need one of Google's fixed headers plus 3 to 10 values of at most 25 chars. Call extensions need a phone number plus a 2-letter country code.

## Never ask what a read tool can answer

Before offering a bidding choice, call `adsap_google_list_conversion_actions` and present the account's real state. Framing the decision around "if the account has none…" makes the user choose on your speculation, and you will sometimes be wrong. The same applies to geo ids, feed labels and existing assets: resolve, then ask.

## Create a Search campaign

1. `adsap_google_search_geo_targets` resolves location names like "Paris" to numeric geo target ids; pass `country_code` for ambiguous names.
2. `adsap_google_keyword_ideas`, optional but recommended: Keyword Planner suggestions with monthly search volume, competition and top-of-page bid range. Seed with draft keywords and/or the landing-page URL, and pass the campaign's geo ids plus language so volumes match the targeting.
3. `adsap_google_create_search_campaign`: ONE atomic create of budget, campaign, locations, ad group, keywords and responsive search ad.

Do NOT pass `language_ids`. Google removes language targeting from Search campaigns in late September 2026 and matches ads on the language of the ad itself. If a user asks for a language, write the ad in that language instead. Should Google reject language criteria, the tool retries without them and returns a warning; relay it. Always run it with `dry_run: true` first: Google validates the entire plan without creating anything. Show the plan, then execute on confirmation. The tool **always creates the campaign PAUSED**; activate via `adsap_google_set_entity_status` only on an explicit launch. RSA limits: 3 to 15 headlines of at most 30 chars, 2 to 4 descriptions of at most 90 chars. Keywords take at most 80 chars with EXACT, PHRASE or BROAD match types. Budget is a plain number in the account currency. Leave `bidding_strategy` on `'auto'` unless the user asks. It picks maximize_conversions when the account has conversion tracking, otherwise Maximize Clicks; a warning explains the fallback, so relay it and mention conversion tracking. Relay any `policy` status returned on the new ad.

## Create a Performance Max campaign

`adsap_google_upload_asset` first. It creates image assets from the user's Adsap creative library, plus text and YouTube video assets. It reports each image's eligible slots: landscape 1.91:1, square 1:1, portrait 4:5, tall-portrait 9:16, logo. Then `adsap_google_create_pmax_campaign`: ONE atomic create of budget, campaign, locations and languages, one asset group and optional search-theme signals. Asset-group minimums the tool enforces: 3 or more headlines of at most 30 chars, 1 or more long headline of at most 90. Also 2 or more descriptions of at most 90 with **at least one of at most 60**, and a business name of at most 25. Images: 1 or more logo (min 128×128), 1 or more landscape image (min 600×314), 1 or more square image (min 300×300). Video is optional; without one Google may auto-generate video from the other assets (a warning says so; relay it). PMax bidding is maximize_conversions or maximize_conversion_value only; `'auto'` picks maximize_conversions and warns when the account has no conversion tracking: relay that warning, PMax genuinely needs conversion tracking to perform. `retail: true` attaches the account's active Merchant Center link (clean error if none). Same rules as Search: `dry_run: true` first, always created PAUSED.

## Create a Demand Gen campaign

YouTube, Discover, Gmail, Maps and the Google Display Network; the forward-looking way to buy Display inventory is `channels.selected.display: true`. `adsap_google_upload_asset` for the images, then `adsap_google_create_demand_gen_campaign`: ONE atomic create of budget, campaign, one typeless ad group with channel controls and ONE ad. Geo and language apply at the ad-group level, same inputs as Search from your side. Two ad types exist. `ad_type: 'multi_asset'` is an image ad. Text: 1 to 5 headlines of at most 30, 1 to 5 descriptions of at most 90, business name of at most 25, 1 to 5 logos. It needs at least one landscape or square image, optional portrait or tall-portrait 9:16, combined images at most 20. `ad_type: 'video_responsive'` is a YouTube ad with 1 to 5 YouTube video ids. Text: 1 to 5 headlines of at most 40, **1 to 5 long headlines required**, 1 to 5 descriptions of at most 90, 1 to 5 logos. **Daily budget minimum is 5** (Google enforces a 5 USD local-currency-equivalent floor; relay `BUDGET_BELOW_DAILY_MINIMUM` errors as "raise the budget to at least 5/day"). Channels default to ALL_CHANNELS; `channels.selected` picks exact surfaces (at least one true). Bidding: `'auto'` picks maximize_conversions with conversion tracking, else Maximize Clicks; `target_roas` needs conversion history (Google rejects otherwise; relay that plainly). Same rules as Search: `dry_run: true` first, always created PAUSED.

## Create a standard Shopping campaign

Product ads rendered from the linked **Merchant Center**, no creative inputs at all. REQUIRES an active Merchant Center link on the account (a clean `NO_MERCHANT_LINK` error explains the fix; the merchant id is auto-resolved). Optional: `adsap_google_list_feed_labels` to offer a feed-label restriction (pass the label back EXACTLY as returned; an empty list means no products synced yet; omitting `feed_label` targets all feeds). Then `adsap_google_create_shopping_campaign` (ONE atomic create: budget, campaign, locations, Shopping ad group, product ad and an "All products" listing group). No language targeting exists for Shopping; the ad language follows the product feed. `campaign_priority` (0 to 2) only arbitrates between the account's OWN standard Shopping campaigns. **Retail Performance Max always outranks standard Shopping for the same products**: say so whenever the account also runs retail PMax. Bidding: `'auto'` = Maximize Clicks. `'manual_cpc'` requires `cpc_bid` and is the reason to pick standard Shopping over PMax (full bid control). `target_roas` needs about 15 conversions in 30 days. Same rules as Search: `dry_run: true` first, always created PAUSED.

## Create a Display campaign

A classic standalone Display campaign shows image ads on the Google Display Network. `adsap_google_upload_asset` for the images, then `adsap_google_create_display_campaign`: ONE atomic create of budget, campaign, locations and languages, Display ad group and responsive display ad. **Google is migrating standalone Display INTO Demand Gen**, and every response carries a heads-up warning. Relay it, and for new builds suggest Demand Gen with the Display channel instead unless the user wants classic Display parity or its bid control. The ad REQUIRES BOTH image classes: 1 or more landscape (1.91:1, min 600×314) AND 1 or more square (1:1, min 300×300), combined at most 15. Text: 1 to 5 headlines of at most 30, **exactly one `long_headline`** of at most 90, 1 to 5 descriptions of at most 90, `business_name` of at most 25 and `final_url`. Optional: square logos (1:1) and landscape logos (4:1) combined at most 5, at most 5 YouTube video assets, `call_to_action_text` of at most 30. Bidding: `'auto'` = Maximize Clicks; `'manual_cpc'` needs `cpc_bid`. `'maximize_conversions'` takes NO target (Display rejects a CPA target inside it). `'target_cpa'` is the standalone scheme with no conversion-history requirement. `'target_roas'` HAS a conversion-history eligibility gate: if Google rejects it, say so plainly and suggest maximize_conversions or maximize_clicks. Same rules as Search: `dry_run: true` first, always created PAUSED.

## Create an App campaign

Promote a mobile app across Google Search, Google Play, YouTube, Discover and Display; Google assembles the ad from your text and optional media automatically. `adsap_google_create_app_campaign` (ONE atomic create: budget, campaign, locations and languages, ad group and app ad). The app does NOT need to belong to the user; any public store listing works. `app_id` is the Android package name (for example `com.example.app`) with `app_store: 'google_play'`, or the numeric App Store id (for example `570060128`) with `'apple_app_store'`. The tool takes NO URL input; the ad links to the store listing automatically. v1 optimizes installs at `target_cpi` (target cost per install, plain number in the account currency); Google recommends `daily_budget` of about 50 times `target_cpi`; a warning flags lower budgets, relay it. Text: **2 to 5 headlines** of at most 30 chars (Google enforces the floor of 2) plus 1 to 5 descriptions of at most 90. Optional media via `adsap_google_upload_asset`: `image_asset_ids` (1.91:1, 4:5 or 1:1, at most 5 MB) and `video_asset_ids` (YouTube assets, 10 to 60 s); text-only is fully valid. In-app-action, value, engagement and pre-registration goals are not available. If a warning says the account has no conversion tracking, relay it (Android installs normally auto-track once a Google Play link exists). Same rules as Search: `dry_run: true` first, always created PAUSED.

## Upload a video file

Use this when the user has an MP4 but **no YouTube channel**. Demand Gen video ads and the App and Display video slots otherwise require the video to already be on YouTube. Call `adsap_google_upload_video` with `action: 'upload'` and the `storage_path` of the file in their creative library. It goes to a **Google-managed "ad storage" YouTube channel owned by the ad account**, UNLISTED, with no customer channel, no channel linking and no extra permission. Then poll `action: 'status'` with the returned `upload_resource_name`. **YouTube processing takes MINUTES**, so poll roughly every 30 s; you get `state: 'UPLOADED'` plus `still_processing: true` until it flips to `PROCESSED`, at which point the video asset is minted automatically. Then feed it onward: Demand Gen takes the **video id** (`ad.youtube_video_ids`), while Display (`youtube_video_asset_ids`) and App (`video_asset_ids`) take the **asset id**; the response gives you both. The **title is immutable after upload** (Google rejects renames), so confirm it before uploading. Max 256 MB. If the video is already on YouTube, skip this entirely and pass the existing id or asset straight to the campaign tool.

## Notes

Data is cache-first and auto-refreshes from Google when stale: state the date range and mention staleness like with Meta. Google reports plain Clicks, CTR and CPC (no Meta-style link-click split). Every tool outside this file is Meta-only.
