Skip to main content
Glama

NotFair-GoogleAds

Server Details

Google Ads MCP - analyze performance, manage keywords, bids, and campaigns.

Status
Unhealthy
Last Tested
Transport
Streamable HTTP
URL
Repository
nowork-studio/toprank
GitHub Stars
499

Glama MCP Gateway

Connect through Glama MCP Gateway for full control over tool access and complete visibility into every call.

MCP client
Glama
MCP server

Full call logging

Every tool call is logged with complete inputs and outputs, so you can debug issues and audit what your agents are doing.

Tool access control

Enable or disable individual tools per connector, so you decide what your agents can and cannot do.

Managed credentials

Glama handles OAuth flows, token storage, and automatic rotation, so credentials never expire on your clients.

Usage analytics

See which tools your agents call, how often, and when, so you can understand usage patterns and catch anomalies.

100% free. Your data is private.

Tool Definition Quality

Score is being calculated. Check back soon.

Available Tools

89 tools
addExperimentArms
Idempotent
Inspect

Step 2 of 5. Create both arms (control + treatment) in ONE atomic call — Google forbids adding arms incrementally because traffic_split must sum to 100. The control arm references an existing campaign; the treatment arm has Google auto-spawn a trial campaign that you then mutate (returned as inDesignCampaigns[0]). Returns the trial campaign resource name(s) so the agent can apply the change under test BEFORE scheduling. Returns changeId.

ParametersJSON Schema
NameRequiredDescriptionDefault
armsYesProvide both arms in one call. v1 supports exactly one control + one treatment (Google's current limit).
accountIdNoAccount ID (omit for primary)
experimentResourceNameYesResource name from createExperiment, e.g. 'customers/123/experiments/456'.
addKeyword
Idempotent
Inspect

Create/add a new positive keyword in an ad group (starts enabled). Use this for a single new keyword; use bulkAddKeywords to create many positive keywords at once. Returns changeId.

ParametersJSON Schema
NameRequiredDescriptionDefault
keywordYes
accountIdNoAccount ID (omit for primary)
adGroupIdYes
matchTypeNoBROAD
campaignIdYes
acknowledgeExperimentImpactNoDanger override. Set true only after the user explicitly accepts that this mutation touches a campaign in an active experiment, or after applying the same intended change to both arms.
addKeywordToNegativeList
Idempotent
Inspect

Add a keyword to a shared negative keyword list. The keyword will be blocked across all campaigns linked to this list. Returns changeId.

ParametersJSON Schema
NameRequiredDescriptionDefault
keywordYesKeyword text to block
accountIdNoAccount ID (omit for primary)
matchTypeNoMatch type (default: PHRASE)PHRASE
sharedSetIdYesShared set ID (query shared_set WHERE type = NEGATIVE_KEYWORDS via runScript)
addNegativeKeyword
Idempotent
Inspect

Add a negative keyword to a campaign. Also use this to re-enable a previously removed negative keyword (Google Ads has no 'enable' state for negatives). Returns changeId.

ParametersJSON Schema
NameRequiredDescriptionDefault
keywordYesKeyword text to block
accountIdNoAccount ID (omit for primary)
matchTypeNoMatch type for the negative keyword (default: PHRASE)PHRASE
campaignIdYes
acknowledgeExperimentImpactNoDanger override. Set true only after the user explicitly accepts that this mutation touches a campaign in an active experiment, or after applying the same intended change to both arms.
askSupportInspect

Contact NotFair support. Use this tool when the user explicitly wants to reach the support team — for example, they say "contact support", "file a bug", "report an issue", "I need help from the NotFair team", or "this is a NotFair problem not a Google Ads problem".

This sends a message directly to the NotFair team and generates a ticket. The user will receive a response via email within 1 business day.

DO NOT use this for:

  • Routine Google Ads questions you can answer yourself.

  • Internal tool quality issues — use fileInternalNotFairToolFeedback for those.

  • Questions you haven't tried to answer yet.

Only call this when the user has explicitly asked to contact support, or when you've exhausted your ability to help and the user agrees escalation is the right move.

ParametersJSON Schema
NameRequiredDescriptionDefault
contextNoBrief context about what the user was trying to do. Omit PII except what the user explicitly included.
messageYesThe user's message to the support team. Write it in first person as if the user wrote it.
bulkAddKeywords
Idempotent
Inspect

Bulk-create/add up to 100 new positive keywords to an ad group in one call. This is the bulk variant of addKeyword/create keyword. Atomic by default: the server pre-validates every item and executes nothing if any keyword fails static checks such as duplicates, invalid syntax, removed parents, or negative-keyword conflicts. Set continueOnError=true to skip invalid items and add the valid subset. Set dryRun=true to validate only. Returns per-keyword results with individual changeIds when executed.

ParametersJSON Schema
NameRequiredDescriptionDefault
dryRunNoIf true, run pre-validation but do not execute. Returns wouldSucceedIds and structured errors/warnings.
keywordsYes
accountIdNoAccount ID (omit for primary)
adGroupIdYes
campaignIdYesCampaign ID (for logging)
continueOnErrorNoIf true, skip invalid items and execute the valid subset. If false, fail the whole batch before writing when any item fails pre-validation.
acknowledgeExperimentImpactNoDanger override. Set true only after the user explicitly accepts that this mutation touches a campaign in an active experiment, or after applying the same intended change to both arms.
bulkPauseKeywords
DestructiveIdempotent
Inspect

Pause up to 100 POSITIVE keywords in one call. Atomic by default: the server pre-validates every item and executes nothing if any item fails static checks. Does NOT work on negative keywords — for negatives, call removeNegativeKeyword or removeKeywordFromNegativeList; Google Ads has no 'pause' for negatives. Set continueOnError=true to skip invalid items and pause the valid subset. Set dryRun=true to validate only. Returns per-keyword results with individual changeIds when executed.

ParametersJSON Schema
NameRequiredDescriptionDefault
dryRunNoValidate and report what would happen without writing to Google Ads or logging changes.
keywordsYes
accountIdNoAccount ID (omit for primary)
continueOnErrorNoIf true, skip invalid items and execute the valid subset. If false, fail the whole batch before writing when any item fails pre-validation.
acknowledgeExperimentImpactNoDanger override. Set true only after the user explicitly accepts that this mutation touches a campaign in an active experiment, or after applying the same intended change to both arms.
bulkUpdateBids
DestructiveIdempotent
Inspect

Update up to 50 keyword bids in one call. Atomic by default: the server pre-validates every item and executes nothing if any item fails static checks. Set continueOnError=true to skip invalid items and update the valid subset. Set dryRun=true to validate only. Each bid change is capped by the per-account maxBidChangePct guardrail (default 25%); raise it with setGuardrails (max 100% per call) and confirm with the user before stepping bigger — for multi-step ramps, iterate (e.g. 100% → 100% to go 4× in two calls). Returns per-keyword results with individual changeIds when executed.

ParametersJSON Schema
NameRequiredDescriptionDefault
dryRunNoIf true, run pre-validation but do not execute. Returns wouldSucceedIds and structured errors/warnings.
updatesYes
accountIdNoAccount ID (omit for primary)
continueOnErrorNoIf true, skip invalid items and execute the valid subset. If false, fail the whole batch before writing when any item fails pre-validation.
acknowledgeExperimentImpactNoDanger override. Set true only after the user explicitly accepts that this mutation touches a campaign in an active experiment, or after applying the same intended change to both arms.
createAd
Idempotent
Inspect

Create a Responsive Search Ad (RSA) in an ad group. Optionally include path1/path2 for the display URL (the segments shown after the domain, e.g. example.com/path1/path2). Returns changeId.

ParametersJSON Schema
NameRequiredDescriptionDefault
path1NoDisplay URL path 1 (max 15 chars, no spaces). Shown after the domain.
path2NoDisplay URL path 2 (max 15 chars, no spaces). Requires path1.
finalUrlYes
accountIdNoAccount ID (omit for primary)
adGroupIdYes
headlinesYes3-15 headlines, max 30 chars each
campaignIdYesCampaign ID (for logging/undo tracking)
descriptionsYes2-4 descriptions, max 90 chars each
acknowledgeExperimentImpactNoDanger override. Set true only after the user explicitly accepts that this mutation touches a campaign in an active experiment, or after applying the same intended change to both arms.
createAdGroup
Idempotent
Inspect

Create an ad group in a campaign (starts enabled). Returns changeId.

ParametersJSON Schema
NameRequiredDescriptionDefault
accountIdNoAccount ID (omit for primary)
campaignIdYes
adGroupNameYes
acknowledgeExperimentImpactNoDanger override. Set true only after the user explicitly accepts that this mutation touches a campaign in an active experiment, or after applying the same intended change to both arms.
createAdVariationExperiment
Idempotent
Inspect

RSA-asset A/B test shortcut. Bundles createExperiment + addExperimentArms + asset patch on the trial RSA into ONE call. Use to A/B-test an RSA's headlines, descriptions, or final URL against the live version. Internally a SEARCH_CUSTOM experiment whose treatment-arm clone has its RSA patched — Google's verified API path for RSA A/B testing. The base RSA is cloned into a trial campaign; this tool patches the clone and leaves the experiment in SETUP — you call scheduleExperiment to begin serving. Required: at least one of headlines, descriptions, finalUrl. RSA assets are atomic — when patching copy, supply BOTH headlines AND descriptions (Google replaces the full asset set). Returns experimentResourceName, trialCampaignId, trialAdGroupId, trialAdId, and readyToSchedule. Returns changeId.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYesExperiment name, unique under the customer.
suffixNoTrial campaign name suffix. Defaults to '[ad-var]'.
endDateNo
baseAdIdYesRSA ID to clone and vary. Must be a Responsive Search Ad.
finalUrlNoReplacement landing page URL for the trial RSA.
accountIdNoAccount ID (omit for primary)
headlinesNoReplacement headlines for the trial RSA (3–15, ≤30 chars). Omit to keep the original headlines.
startDateNo
descriptionNo
descriptionsNoReplacement descriptions for the trial RSA (2–4, ≤90 chars). Omit to keep originals. If you pass headlines you MUST also pass descriptions (RSA assets are atomic).
baseAdGroupIdYesAd group ID containing the base RSA.
baseCampaignIdYesExisting campaign ID containing the RSA you want to vary.
treatmentTrafficSplitNoPercent of traffic routed to the variation (1–99). Default 50 (50/50).
createAppCampaign
Idempotent
Inspect

Create an App campaign (install-focused) for the Apple App Store or Google Play Store. App ID required. Add image and video assets in Google Ads UI for full serving. Starts PAUSED. Returns changeId.

ParametersJSON Schema
NameRequiredDescriptionDefault
appIdYesApp ID: Apple App Store numeric ID (e.g. '123456789') or Google Play package name (e.g. 'com.example.app').
biddingNoBidding configuration. Defaults to TARGET_CPA.
appStoreYesApp store: GOOGLE_APP_STORE for Android, APPLE_APP_STORE for iOS.
finalUrlYesApp store URL (e.g. https://apps.apple.com/app/id123456789).
accountIdNoAccount ID (omit for primary)
headlinesYes2–5 headlines, max 30 chars each.
languageIdsNoLanguage constant IDs. Defaults to no restriction.
businessNameNoBusiness name shown in ads.
campaignNameYes
descriptionsYes1–5 descriptions, max 90 chars each.
geoTargetIdsNoGeo target constant IDs (e.g. '2840' for US). Use searchGeoTargets to find IDs.
dailyBudgetDollarsYesDaily budget in dollars
createBiddingStrategy
Idempotent
Inspect

Create a portfolio bidding strategy — a shared bidding configuration that multiple campaigns can reference. Supports TARGET_CPA, TARGET_ROAS, MAXIMIZE_CONVERSIONS, and MAXIMIZE_CONVERSION_VALUE. For TARGET_CPA, targetCpa (in dollars) is required. For TARGET_ROAS, targetRoas (e.g. 2.0 = 200%) is required. Returns changeId + biddingStrategyId. Use linkCampaignToBiddingStrategy to attach to campaigns.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYesStrategy name, e.g. 'Lead Gen Target CPA'
typeYes
accountIdNoAccount ID (omit for primary)
targetCpaNoTarget CPA in dollars. Required for TARGET_CPA; optional cap for MAXIMIZE_CONVERSIONS.
targetRoasNoTarget ROAS multiplier (e.g. 2.0 = 200% return). Required for TARGET_ROAS; optional cap for MAXIMIZE_CONVERSION_VALUE.
createCallAsset
Idempotent
Inspect

Create a call asset (phone number + country code) and optionally link it to customer/campaign/ad-group targets in the same atomic mutate. Call assets show a phone number in search ads and enable call tracking. callConversionReportingState defaults to account-level tracking when omitted. Returns changeId, assetId, and link resource names.

ParametersJSON Schema
NameRequiredDescriptionDefault
targetsNoOptional serving targets (customer/campaign/ad_group — call assets don't support asset_group). Omit or pass [] to create the asset only. Array of targets. Each target is an OBJECT with a `level` discriminator — bare resource strings (e.g. "customers/123") are rejected. Shapes: { level: 'customer' } (account-wide), { level: 'campaign', campaignId: '123' }, { level: 'ad_group', adGroupId: '456' }, { level: 'asset_group', assetGroupId: '789' } (Performance Max only). Example: [{ level: 'customer' }, { level: 'campaign', campaignId: '12345' }].
accountIdNoAccount ID (omit for primary)
countryCodeYesTwo-letter ISO 3166-1 alpha-2 country code, e.g. 'US'
phoneNumberYesPhone number in E.164-style or local format, e.g. '+14155550123' or '(415) 555-0123'
callConversionActionNoConversion action resource_name to use when callConversionReportingState is USE_RESOURCE_LEVEL_CALL_CONVERSION_ACTION. Format: customers/{customer_id}/conversionActions/{id}.
acknowledgeExperimentImpactNoDanger override. Set true only after the user explicitly accepts that this mutation touches a campaign in an active experiment, or after applying the same intended change to both arms.
callConversionReportingStateNoCall conversion reporting behavior. Omit to use account-level tracking (default).
createCalloutAsset
Idempotent
Inspect

Create a callout asset (≤25 char snippet shown under text ads, e.g. 'Free shipping'). Optionally link it to customer, campaign, or ad group targets in the same atomic mutate via targets. Returns changeId, assetId, and link resource names. To attach an existing callout to more targets later, call linkAsset.

ParametersJSON Schema
NameRequiredDescriptionDefault
textYesCallout text (≤25 chars), e.g. 'Free shipping'
targetsNoOptional serving targets. Omit or pass [] to create the asset only; pass targets to link it in the same mutate. Array of targets. Each target is an OBJECT with a `level` discriminator — bare resource strings (e.g. "customers/123") are rejected. Shapes: { level: 'customer' } (account-wide), { level: 'campaign', campaignId: '123' }, { level: 'ad_group', adGroupId: '456' }, { level: 'asset_group', assetGroupId: '789' } (Performance Max only). Example: [{ level: 'customer' }, { level: 'campaign', campaignId: '12345' }].
accountIdNoAccount ID (omit for primary)
acknowledgeExperimentImpactNoDanger override. Set true only after the user explicitly accepts that this mutation touches a campaign in an active experiment, or after applying the same intended change to both arms.
createCampaign
Idempotent
Inspect

Create a Search campaign with budget, ad group, keywords, and a Responsive Search Ad. Starts PAUSED — use enableCampaign to go live. Returns changeId. For other campaign types use: createShoppingCampaign, createPerformanceMaxCampaign, createDemandGenCampaign, createDisplayCampaign, createVideoCampaign, createAppCampaign.

ParametersJSON Schema
NameRequiredDescriptionDefault
biddingNoBidding configuration. Defaults to MAXIMIZE_CONVERSIONS.
finalUrlYesPrimary landing page URL.
keywordsYesKeywords to target (at least 1 required).
accountIdNoAccount ID (omit for primary)
headlinesYes3–15 headlines, max 30 chars each.
languageIdsNoLanguage constant IDs (e.g. '1000' for English). Defaults to no restriction.
campaignNameYes
descriptionsYes2–4 descriptions, max 90 chars each.
geoTargetIdsNoGeo target constant IDs (e.g. '2840' for US). Use searchGeoTargets to find IDs.
keywordMatchTypeNoKeyword match type. Defaults to BROAD.
dailyBudgetDollarsYesDaily budget in dollars
createConversionAction
Idempotent
Inspect

Create a conversion action for tracking offline conversions (imports), web events, or calls. Optionally enable Enhanced Conversions for Leads (ECFL) for user-data matching. Returns changeId.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYesConversion action name, e.g. 'First Booking'
typeNoUPLOAD_CLICKS for offline/import conversions, WEBPAGE for website events, UPLOAD_CALLS for call trackingUPLOAD_CLICKS
statusNoENABLED
categoryNoPURCHASE
accountIdNoAccount ID (omit for primary)
countingTypeNoONE_PER_CLICK counts one conversion per click (leads), MANY_PER_CLICK counts every conversion (purchases)ONE_PER_CLICK
defaultValueNoDefault conversion value in account currency
primaryForGoalNotrue = primary (included in Conversions column for bidding), false = secondary (observation only)
alwaysUseDefaultValueNoAlways use default value vs. transaction-specific values
enhancedConversionsForLeadsNoEnable Enhanced Conversions for Leads at account level. Requires customer data terms to be accepted in Google Ads UI first.
viewThroughLookbackWindowDaysNoView-through conversion lookback window (1-30 days)
clickThroughLookbackWindowDaysNoClick-through conversion lookback window (1-90 days)
createDemandGenCampaign
Idempotent
Inspect

Create a Demand Gen campaign serving on YouTube/Gmail/Discover. Asset-based discovery campaigns. Add image assets in Google Ads UI for full ad delivery. Starts PAUSED. Returns changeId.

ParametersJSON Schema
NameRequiredDescriptionDefault
biddingNoBidding configuration. Defaults to MAXIMIZE_CONVERSIONS.
finalUrlYesPrimary landing page URL.
accountIdNoAccount ID (omit for primary)
headlinesYes3–5 headlines, max 40 chars each.
languageIdsNoLanguage constant IDs. Defaults to no restriction.
businessNameYesBusiness name shown in ads.
campaignNameYes
descriptionsYes2–5 descriptions, max 90 chars each.
geoTargetIdsNoGeo target constant IDs (e.g. '2840' for US). Use searchGeoTargets to find IDs.
longHeadlinesYes1–5 long headlines, max 90 chars each.
dailyBudgetDollarsYesDaily budget in dollars
createDisplayCampaign
Idempotent
Inspect

Create a Display Network campaign with a Responsive Display Ad. Image assets must be uploaded first via createImageAsset; pass the resulting asset resource names. Starts PAUSED. Returns changeId.

ParametersJSON Schema
NameRequiredDescriptionDefault
biddingNoBidding configuration. Defaults to MAXIMIZE_CONVERSIONS.
finalUrlYesPrimary landing page URL.
accountIdNoAccount ID (omit for primary)
headlinesYes1–5 short headlines, max 30 chars each.
adGroupNameNoAd group name. Defaults to '{campaignName} - Ad Group 1'.
languageIdsNoLanguage constant IDs. Defaults to no restriction.
businessNameYesBusiness name shown in ads.
campaignNameYes
descriptionsYes1–5 descriptions, max 90 chars each.
geoTargetIdsNoGeo target constant IDs (e.g. '2840' for US). Use searchGeoTargets to find IDs.
longHeadlineYesSingle long headline, max 90 chars.
logoImageAssetIdNoOptional logo image asset resource name.
dailyBudgetDollarsYesDaily budget in dollars
marketingImageAssetIdYesAsset resource name for landscape marketing image (1200×628). Create via createImageAsset first.
squareMarketingImageAssetIdYesAsset resource name for square marketing image (1200×1200). Create via createImageAsset first.
createExperiment
Idempotent
Inspect

Create a Google Ads experiment in SETUP status. Step 1 of 5 — next call addExperimentArms with one control + one treatment arm. Type SEARCH_CUSTOM for general search experiments (compare ads/keywords/landing pages); SEARCH_AUTOMATED_BIDDING_STRATEGY to compare bidding strategies on the same campaign. The experiment doesn't serve traffic until scheduleExperiment is called. Returns experimentResourceName.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYesExperiment name, unique under the customer.
typeYesSEARCH_CUSTOM for ad/keyword/landing-page tests; SEARCH_AUTOMATED_BIDDING_STRATEGY to compare bidding strategies.
suffixNoString appended to the trial campaign name. Defaults to '[experiment]'.
endDateNoYYYY-MM-DD. Defaults to the base campaign's end date. Recommended: ≥14 days after start for stat significance.
accountIdNoAccount ID (omit for primary)
startDateNoYYYY-MM-DD. Defaults to today (or campaign start, whichever is later).
descriptionNo
syncEnabledNoIf true, edits to the base campaign also propagate into the trial. Immutable after creation.
createImageAsset
Idempotent
Inspect

Upload a PNG/JPEG image asset from an HTTPS URL. Pick the field type by SERVING SLOT, not by aspect ratio: MARKETING_IMAGE (Display/PMax 1.91:1, min 600x314) | SQUARE_MARKETING_IMAGE (Display/PMax 1:1, min 300x300) | AD_IMAGE (Search/Display 'image extension' on RSAs — accepts either 1.91:1 OR 1:1 source, campaign/ad_group link levels only). Optionally link it to serving targets via targets. Returns changeId, assetId, and link resource names. To attach an existing image to more targets later, call linkAsset.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYesAsset name shown in Google Ads, e.g. 'Spring promo landscape'
targetsNoOptional serving targets. MARKETING_IMAGE/SQUARE_MARKETING_IMAGE support all 4 levels (customer/campaign/ad_group/asset_group for Performance Max). AD_IMAGE supports campaign/ad_group only. Omit or pass [] to create the asset only. Array of targets. Each target is an OBJECT with a `level` discriminator — bare resource strings (e.g. "customers/123") are rejected. Shapes: { level: 'customer' } (account-wide), { level: 'campaign', campaignId: '123' }, { level: 'ad_group', adGroupId: '456' }, { level: 'asset_group', assetGroupId: '789' } (Performance Max only). Example: [{ level: 'customer' }, { level: 'campaign', campaignId: '12345' }].
imageUrlYesPublic HTTPS URL for the PNG/JPEG image to upload. Max 5 MB.
accountIdNoAccount ID (omit for primary)
fieldTypeYesServing slot; pre-validates dimensions and (when `targets` is set) used as the link field_type. AD_IMAGE accepts either 1.91:1 or 1:1 source dimensions.
acknowledgeExperimentImpactNoDanger override. Set true only after the user explicitly accepts that this mutation touches a campaign in an active experiment, or after applying the same intended change to both arms.
createNegativeKeywordList
Idempotent
Inspect

Create a shared negative keyword list. After creating, add keywords with addKeywordToNegativeList and link to campaigns with linkNegativeListToCampaign. Returns changeId + sharedSetId.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYesList name, e.g. 'Brand Negatives' or 'Competitor Terms'
accountIdNoAccount ID (omit for primary)
createPerformanceMaxCampaign
Idempotent
Inspect

Create a Performance Max campaign that serves across all Google channels via asset groups. Pass merchantId+salesCountry for retail PMax linked to Merchant Center. Starts PAUSED. Add image and video assets in Google Ads UI before enabling for full serving scale. Returns changeId.

ParametersJSON Schema
NameRequiredDescriptionDefault
biddingNoBidding configuration. Defaults to MAXIMIZE_CONVERSIONS.
finalUrlYesPrimary landing page URL.
accountIdNoAccount ID (omit for primary)
headlinesYes3–15 headlines, max 30 chars each.
merchantIdNoGoogle Merchant Center account ID. Optional — links to product feed for retail PMax.
languageIdsNoLanguage constant IDs. Defaults to no restriction.
businessNameYesBusiness name shown in ads.
campaignNameYes
descriptionsYes2–5 descriptions, max 90 chars each.
geoTargetIdsNoGeo target constant IDs (e.g. '2840' for US). Use searchGeoTargets to find IDs.
salesCountryNoISO-3166-1 alpha-2 sales country (e.g. 'US'). Required when merchantId is provided.
longHeadlinesYes1–5 long headlines, max 90 chars each.
dailyBudgetDollarsYesDaily budget in dollars
createShoppingCampaign
Idempotent
Inspect

Create a Standard Shopping campaign linked to a Merchant Center feed. Optional inventoryFilter scopes the campaign to a product_type or custom_label. Starts PAUSED. Returns changeId.

ParametersJSON Schema
NameRequiredDescriptionDefault
biddingNoBidding configuration. Defaults to MANUAL_CPC.
accountIdNoAccount ID (omit for primary)
merchantIdYesGoogle Merchant Center account ID.
enableLocalNoEnable local inventory ads. Defaults to false.
languageIdsNoIgnored for Standard Shopping: Google Ads does not support language campaign criteria on Shopping campaigns. Use Merchant Center feed language/feed label plus salesCountry and geoTargetIds instead.
campaignNameYes
geoTargetIdsNoGeo target constant IDs (e.g. '2840' for US). Use searchGeoTargets to find IDs.
salesCountryYesISO-3166-1 alpha-2 sales country (e.g. 'US').
searchPartnersNoInclude search partner network. Defaults to false.
inventoryFilterNoInventory filter dimensions restricting campaign to matching products. Omit to show all products.
campaignPriorityNoCampaign priority: 0=LOW (default), 1=MEDIUM, 2=HIGH.
dailyBudgetDollarsYesDaily budget in dollars
createSitelinkAsset
Idempotent
Inspect

Create a sitelink asset (link text + destination URL + optional description pair). Optionally link it to customer/campaign/ad-group targets via targets. Sitelink text ≤25 chars; descriptions ≤35 chars each and must be provided as a pair. Returns changeId, assetId, and link resource names. To attach an existing sitelink to more targets later, call linkAsset.

ParametersJSON Schema
NameRequiredDescriptionDefault
targetsNoOptional serving targets. Omit or pass [] to create the asset only. Array of targets. Each target is an OBJECT with a `level` discriminator — bare resource strings (e.g. "customers/123") are rejected. Shapes: { level: 'customer' } (account-wide), { level: 'campaign', campaignId: '123' }, { level: 'ad_group', adGroupId: '456' }, { level: 'asset_group', assetGroupId: '789' } (Performance Max only). Example: [{ level: 'customer' }, { level: 'campaign', campaignId: '12345' }].
finalUrlYesDestination URL for the sitelink
linkTextYesSitelink text (≤25 chars), e.g. 'Pricing'
accountIdNoAccount ID (omit for primary)
description1NoOptional sitelink description line 1 (≤35 chars). If provided, description2 is also required.
description2NoOptional sitelink description line 2 (≤35 chars). If provided, description1 is also required.
acknowledgeExperimentImpactNoDanger override. Set true only after the user explicitly accepts that this mutation touches a campaign in an active experiment, or after applying the same intended change to both arms.
createStructuredSnippetAsset
Idempotent
Inspect

Create a structured snippet asset (header + 3-10 values, each ≤25 chars). Optionally link it to customer/campaign/ad-group targets via targets. Valid headers: Brands, Amenities, Styles, Types, Destinations, Services, Courses, Neighborhoods, Shows, Insurance coverage, Degree programs, Featured Hotels, Models. Alias accepted: "Service catalog" → "Services". Returns changeId, assetId, and link resource names. To attach an existing snippet to more targets later, call linkAsset.

ParametersJSON Schema
NameRequiredDescriptionDefault
headerYesStructured snippet header. Must be one of: Brands, Amenities, Styles, Types, Destinations, Services, Courses, Neighborhoods, Shows, Insurance coverage, Degree programs, Featured Hotels, Models. "Service catalog" is accepted and normalized to "Services".
valuesYesSnippet values, 3-10 items, each ≤25 chars
targetsNoOptional serving targets. Omit or pass [] to create the asset only. Array of targets. Each target is an OBJECT with a `level` discriminator — bare resource strings (e.g. "customers/123") are rejected. Shapes: { level: 'customer' } (account-wide), { level: 'campaign', campaignId: '123' }, { level: 'ad_group', adGroupId: '456' }, { level: 'asset_group', assetGroupId: '789' } (Performance Max only). Example: [{ level: 'customer' }, { level: 'campaign', campaignId: '12345' }].
accountIdNoAccount ID (omit for primary)
acknowledgeExperimentImpactNoDanger override. Set true only after the user explicitly accepts that this mutation touches a campaign in an active experiment, or after applying the same intended change to both arms.
createVideoCampaign
Idempotent
Inspect

Create a YouTube TrueView in-stream video campaign. Requires an existing YouTube video ID. Starts PAUSED. Returns changeId.

ParametersJSON Schema
NameRequiredDescriptionDefault
adNameNoAd name. Defaults to '{campaignName} - Video Ad'.
biddingNoBidding configuration. Defaults to TARGET_CPV.
finalUrlYesPrimary landing page URL.
headlineYesShort headline, max 30 chars.
accountIdNoAccount ID (omit for primary)
descriptionNoAd description, max 90 chars.
languageIdsNoLanguage constant IDs. Defaults to no restriction.
callToActionNoCall-to-action text (e.g. 'LEARN_MORE', 'SHOP_NOW'). Omit to use Google's default.
campaignNameYes
geoTargetIdsNoGeo target constant IDs (e.g. '2840' for US). Use searchGeoTargets to find IDs.
longHeadlineNoLong headline, max 90 chars.
youtubeVideoIdYesYouTube video ID (e.g. 'abc123XYZ' from youtube.com/watch?v=abc123XYZ). Must be uploaded to YouTube.
dailyBudgetDollarsYesDaily budget in dollars
enableAd
Idempotent
Inspect

Re-enable a paused ad. Returns changeId.

ParametersJSON Schema
NameRequiredDescriptionDefault
adIdYes
accountIdNoAccount ID (omit for primary)
adGroupIdYes
campaignIdYesCampaign ID (for logging)
acknowledgeExperimentImpactNoDanger override. Set true only after the user explicitly accepts that this mutation touches a campaign in an active experiment, or after applying the same intended change to both arms.
enableCampaign
Idempotent
Inspect

Re-enable a paused campaign. Returns changeId.

ParametersJSON Schema
NameRequiredDescriptionDefault
accountIdNoAccount ID (omit for primary)
campaignIdYes
acknowledgeExperimentImpactNoDanger override. Set true only after the user explicitly accepts that this mutation touches a campaign in an active experiment, or after applying the same intended change to both arms.
enableKeyword
Idempotent
Inspect

Re-enable a paused keyword. Only needs adGroupId + criterionId (no campaignId, unlike pauseKeyword). Returns changeId.

ParametersJSON Schema
NameRequiredDescriptionDefault
accountIdNoAccount ID (omit for primary)
adGroupIdYes
criterionIdYesKeyword criterion ID (query keyword_view via runScript)
enablePmaxAssetGroup
Idempotent
Inspect

Re-enable a paused Performance Max asset group so it can serve ads again. Use getPmaxAssetGroups to find asset group IDs. Returns a changeId for undo support.

ParametersJSON Schema
NameRequiredDescriptionDefault
accountIdNoAccount ID (omit for primary)
campaignIdYesPerformance Max campaign ID
assetGroupIdYesAsset group ID to enable (query asset_group WHERE type = PERFORMANCE_MAX via runScript)
acknowledgeExperimentImpactNoDanger override. Set true only after the user explicitly accepts that this mutation touches a campaign in an active experiment, or after applying the same intended change to both arms.
endExperiment
DestructiveIdempotent
Inspect

Stop a running experiment immediately, without waiting for the scheduled end date. The trial campaign keeps its current state but stops splitting traffic. Use when the test has produced enough data and you DON'T want to apply the changes back to the base campaign. Status precondition: experiment must be ENABLED, INITIATED, or HALTED. Returns changeId.

ParametersJSON Schema
NameRequiredDescriptionDefault
accountIdNoAccount ID (omit for primary)
experimentResourceNameYes
evaluateChangeIntervention
Read-only
Inspect

Run the server-side observational evaluation for one Impact Monitor intervention. Compares the campaign's 7-day before window vs the post-change window, counts same-campaign confounders, stores an evaluation row, and returns a conservative verdict.

ParametersJSON Schema
NameRequiredDescriptionDefault
accountIdNoAccount ID (omit for primary)
afterWindowDaysNo
baselineWindowDaysNo
changeInterventionIdYes
fileInternalNotFairToolFeedbackInspect

Internal NotFair tool-feedback channel. Privately report MCP/tool friction that got in the way of helping the user — unclear descriptions, missing capabilities, clunky workflows, confusing errors, or duplicate tools.

This is not customer support, not user feedback, and not a success/quality rating. It is an internal NotFair engineering signal. When tool design gets in the way of a real user task, file one concrete report here so we can fix the tool surface. The user benefits in their next session; every other agent serving every other user benefits too.

AUTO-SURFACE THIS WHEN:

  • A tool description was unclear and you weren't sure how to use it.

  • You wanted to accomplish something for the user but no tool existed for it.

  • A workflow took many tool calls when one bulk operation could have replaced them.

  • An error message returned by a tool didn't help you debug or recover.

  • Two tools have overlapping purposes and the choice was confusing.

TIMING — file NOW, not later. This is the most important rule here:

  • File at the moment of friction, BEFORE you implement the workaround. Once you've routed around the issue, you will forget — that's the dominant failure mode for this tool.

  • If you've told the user "I'll file feedback" or anything similar, call this BEFORE your next user-facing message. No "I'll get to it after" — file first, then continue the task.

  • Filing is one tool call. It does not need the user's permission and does not need to be announced to them. Just file it.

DO NOT call this for:

  • Individual operation errors (those are tracked automatically — never call this just because a tool returned an error).

  • Confirming that a task succeeded.

  • Rating your own output quality.

  • Anything the user explicitly asked you to escalate (use the in-app feedback form for that).

Be specific. Reference tools by name and propose a concrete change. Submissions go directly to the NotFair team; the user does not see this channel.

Volume: file freely up to 5 per session. Quality of each report matters far more than parsimony — one specific, well-grounded report beats three vague ones, but underreporting is the bigger risk than overreporting.

ParametersJSON Schema
NameRequiredDescriptionDefault
categoryYesType of feedback. description_unclear=tool docs were ambiguous; missing_capability=no tool for the intent; ergonomic=workflow took too many calls; error_message_unclear=error didn't help debugging; workflow_gap=tools couldn't be composed for the goal; duplicate_tools=two tools confusingly overlap; other=anything else.
user_goalNoWhat the user was trying to accomplish — gives the team the use case context. Avoid PII.
suggestionYesConcrete change you'd recommend.
observationYesWhat was confusing, painful, or missing. Be specific — quote what tripped you up.
affected_toolYesTool name (e.g. 'pauseKeyword'), or 'general' if cross-cutting.
generate_imageInspect

Generate one image from a prompt using OpenAI GPT Image 2. Returns a public URL you can embed in markdown or pass to a creative-asset tool (e.g. Google Ads createImageAsset). Counts against the user's monthly quota.

Prompt craft (GPT Image 2 rewards long, specific, instruction-style prompts — write a paragraph, not keywords):

  • Lead with the medium: photograph, 3D render, isometric vector, watercolor, flat illustration, studio product shot. Single biggest quality lever.

  • Then specify subject, setting, mood, color palette, lighting (e.g. 'golden hour, soft backlight'), and camera/perspective (close-up, wide, overhead, low angle, macro).

  • Keep the focal subject in the center 80% of the frame — ad platforms crop edges across placements.

  • Prefer lifestyle / in-context scenes over isolated-on-white product shots. Google explicitly recommends 'physical settings with organic shadows and lighting' for ad creative.

  • Don't render text unless the user asks for specific copy. Overlaid text is often unreadable at small ad sizes and Google flags it as a quality issue.

  • Avoid negative prompts ('no X, no Y'). GPT Image often pulls the rejected concept in — describe what you want instead.

Ad-policy rules to bake into prompts:

  • No collages, borders, watermarks, mirrored / skewed / over-filtered looks.

  • No fake UI elements (play buttons, download/close icons) — Google Ads policy violation.

  • Don't overlay a logo on the photo; logos belong inside the scene (on a product, sign, storefront).

  • Blank space should be under 80% of the frame — the subject is the focus.

Aspect ratios — match the target placement:

  • Google Ads asset slots: '1.91:1' landscape (required), '1:1' square (required), '4:5' portrait, '9:16' vertical (Demand Gen / Shorts).

  • Meta / social: '1:1' or '4:5' feed; '9:16' stories/reels; '1.91:1' link previews.

  • Hero / web banners: '16:9' or '3:2'. Default is '1:1'.

Quality vs latency: 'low' ~5s drafts; 'medium' balanced; 'high' runs the four-stage Understand/Plan/Generate/Review pipeline (30–50× slower than low) — use only for production-final fidelity.

Output format: default 'png' (lossless). Use 'webp' or 'jpeg' for smaller photographic assets. background='transparent' requires png/webp (use for logos, cutouts, UI assets).

ParametersJSON Schema
NameRequiredDescriptionDefault
modelNoOpenAI image model override. Defaults to gpt-image-2. Use 'gpt-image-1.5' or 'gpt-image-1-mini' to opt into older/cheaper models.
promptYesImage prompt. Describe medium, subject, setting, mood, lighting, color palette, and camera/perspective in natural language — paragraph form outperforms keyword lists. See the tool description for ad-platform rules. Up to 32K chars.
qualityNoGeneration quality. Defaults to 'auto' (OpenAI picks). 'low' is fastest (~5s), 'medium' is balanced, 'high' runs the four-stage Understand/Plan/Generate/Review pipeline (30–50× slower than low) and produces the most refined output.
backgroundNoBackground handling. 'transparent' requires outputFormat='png' or 'webp' (use for logos, cutouts, UI assets). 'opaque' forces a solid background. 'auto' (default) lets the model decide.
aspectRatioNoAspect ratio. Common values: '1:1' (square), '16:9' (landscape), '9:16' (portrait/story), '4:5' (feed post). Defaults to '1:1'. Mapped to a 16-aligned WxH size server-side.
outputFormatNoImage file format. Defaults to 'png'. Use 'webp' or 'jpeg' for smaller photographic assets.
getChangeIntervention
Read-only
Inspect

Get one Impact Monitor intervention with its linked operations and latest evaluation.

ParametersJSON Schema
NameRequiredDescriptionDefault
accountIdNoAccount ID (omit for primary)
changeInterventionIdYes
getChanges
Read-only
Inspect

Recent changes made to the account via NotFair. Each change has a changeId usable with undoChange. Also returns derived changeGroups that group atomic write rows into likely user-intent episodes by requestId/scope/time so agents don't misread bulk edits as isolated one-offs. Reads NotFair's internal change log (Postgres), not Google's change_event API — for Google-side edits use runScript with SELECT ... FROM change_event.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNo
accountIdNoAccount ID (omit for primary)
campaignIdNo
getGuardrails
Read-only
Inspect

Get current guardrail limits. Returns campaign-specific guardrails if set, otherwise account-level defaults. Shows target CPA, monthly cap, and max change percentages for bids, budgets, and keyword pauses.

ParametersJSON Schema
NameRequiredDescriptionDefault
accountIdNoAccount ID (omit for primary)
campaignIdNoCampaign ID to check campaign-specific guardrails
getKeywordIdeas
Read-only
Inspect

Get keyword ideas with real search volume, competition, and CPC data from Google Ads Keyword Planner. Provide seed keywords and/or a URL to discover new keyword opportunities. Returns avg monthly searches, competition level, average CPC, and top-of-page bid estimates. No Google Ads account connection required — works for all users. Use searchGeoTargets first to find geo target IDs for location targeting. Keyword Planner is a separate API (not GAQL) — use this tool, not runScript.

ParametersJSON Schema
NameRequiredDescriptionDefault
urlNoPage URL to generate ideas from (combines with keywords if both provided)
keywordsYesSeed keywords to generate ideas from
languageNoLanguage constant ID (default: 1000 for English). Example: 1000=English, 1003=Spanish, 1001=French
pageSizeNoNumber of keyword ideas to return (max 50)
geoTargetIdsNoGeo target constant IDs for location targeting (e.g. ['2840'] for US). Use searchGeoTargets to find IDs.
getResourceMetadata
Read-only
Inspect

Discover available fields for a GAQL resource. Returns selectable, filterable, and sortable fields with data types. Call this before writing a runScript that queries an unfamiliar resource, so you use valid field names. Example: getResourceMetadata('campaign') returns all campaign.* fields.

ParametersJSON Schema
NameRequiredDescriptionDefault
accountIdNoAccount ID (omit for primary)
resourceNameYesThe GAQL resource name (e.g. 'campaign', 'ad_group', 'keyword_view', 'search_term_view')
get_usage
Read-only
Inspect

Show the current monthly image generation quota and usage for this account.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

graduateExperiment
Idempotent
Inspect

Permanently fork the trial campaign into a standalone campaign that runs alongside the base. The agent only needs to supply the new budget — the trial campaign resource is resolved automatically. Use when both control and treatment are valuable and you want to keep them both running independently. Status precondition: experiment must be ENABLED. Returns changeId.

ParametersJSON Schema
NameRequiredDescriptionDefault
accountIdNoAccount ID (omit for primary)
experimentResourceNameYes
campaignBudgetResourceNameYesFull resource name of the budget the standalone graduated campaign should use, e.g. 'customers/123/campaignBudgets/789'.
linkAsset
Idempotent
Inspect

Link an existing asset to one or more serving targets in a single atomic mutate. Bulk-by-default: pass a single-element targets array for one target, or many for fan-out. Field types: CALLOUT, STRUCTURED_SNIPPET, SITELINK, CALL, MARKETING_IMAGE, SQUARE_MARKETING_IMAGE, AD_IMAGE. Level support varies by field type: MARKETING_IMAGE / SQUARE_MARKETING_IMAGE support all 4 levels including asset_group (Performance Max); CALLOUT / SITELINK / STRUCTURED_SNIPPET / CALL support customer/campaign/ad_group only; AD_IMAGE (Search/Display 'image extension' on RSAs) supports campaign/ad_group only. The underlying asset is field-type-agnostic — the same IMAGE asset can be linked as MARKETING_IMAGE at one target and AD_IMAGE at another. Auto-generated assets (asset.source = AUTOMATICALLY_CREATED) are rejected before the mutate. To remove links, use unlinkAssetLinks with the link resource_names returned here. Returns changeId and link resource names.

ParametersJSON Schema
NameRequiredDescriptionDefault
assetIdYesAsset ID (query `asset` via runScript, or pass the assetId returned from a create*Asset call)
targetsYesOne or more serving targets. Array of targets. Each target is an OBJECT with a `level` discriminator — bare resource strings (e.g. "customers/123") are rejected. Shapes: { level: 'customer' } (account-wide), { level: 'campaign', campaignId: '123' }, { level: 'ad_group', adGroupId: '456' }, { level: 'asset_group', assetGroupId: '789' } (Performance Max only). Example: [{ level: 'customer' }, { level: 'campaign', campaignId: '12345' }].
accountIdNoAccount ID (omit for primary)
fieldTypeYesAsset field type — what kind of asset this is and which serving slot it goes in.
acknowledgeExperimentImpactNoDanger override. Set true only after the user explicitly accepts that this mutation touches a campaign in an active experiment, or after applying the same intended change to both arms.
linkCampaignToBiddingStrategy
Idempotent
Inspect

Link a campaign to a portfolio bidding strategy — the campaign will use the shared strategy's configuration. This replaces any standard (campaign-level) bidding config. Use listBiddingStrategies to find strategy IDs. Returns changeId.

ParametersJSON Schema
NameRequiredDescriptionDefault
accountIdNoAccount ID (omit for primary)
campaignIdYes
biddingStrategyIdYes
acknowledgeExperimentImpactNoDanger override. Set true only after the user explicitly accepts that this mutation touches a campaign in an active experiment, or after applying the same intended change to both arms.
linkNegativeListToCampaign
Idempotent
Inspect

Link a shared negative keyword list to a campaign. All keywords in the list will be blocked for this campaign. Returns changeId.

ParametersJSON Schema
NameRequiredDescriptionDefault
accountIdNoAccount ID (omit for primary)
campaignIdYes
sharedSetIdYesShared set ID (query shared_set WHERE type = NEGATIVE_KEYWORDS via runScript)
acknowledgeExperimentImpactNoDanger override. Set true only after the user explicitly accepts that this mutation touches a campaign in an active experiment, or after applying the same intended change to both arms.
listActiveExperiments
Read-only
Inspect

One-call, safety-oriented view of currently running Google Ads experiments. Returns ENABLED experiments only, with control/treatment campaign IDs and names, traffic split, dates, and recent campaign metrics for both arms. Use this before experiment analysis or before planning campaign mutations; do not stitch raw experiment + experiment_arm GAQL unless you need historical/removed experiments.

ParametersJSON Schema
NameRequiredDescriptionDefault
daysNoRecent metrics window in days. Default 14, max 90.
accountIdNoAccount ID (omit for primary)
listChangeInterventions
Read-only
Inspect

List Impact Monitor interventions grouped at the campaign episode level. Returns campaign-scoped write bundles with status, summary, requestIds, operation counts, and the latest evaluation if one exists.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNo
offsetNo
statusNo
accountIdNoAccount ID (omit for primary)
campaignIdNo
listConnectedAccounts
Read-only
Inspect

List Google Ads accounts connected to this session. Returns accountIds for use with all other tools.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

listExperimentAsyncErrors
Read-only
Inspect

Read errors logged during the most recent scheduleExperiment or promoteExperiment long-running operation. An empty list means the LRO succeeded. A non-empty list means forking or promotion failed — usually a campaign-config issue (invalid budget, conflicting bidding strategy, missing conversion action). Call this after every scheduleExperiment / promoteExperiment.

ParametersJSON Schema
NameRequiredDescriptionDefault
pageSizeNo
accountIdNoAccount ID (omit for primary)
pageTokenNo
experimentResourceNameYes
listKeywords
Read-only
Inspect

Typed keyword inventory for safe mutation prep. Use this when you need keyword criterion IDs for bulkPauseKeywords, bulkUpdateBids, moveKeywords, or to inspect current positive/negative keyword state. This is intentionally narrow: for performance analysis, date-ranged metrics, search terms, or custom joins, use runScript. Defaults are safety-oriented: positive keywords only, enabled criteria only, and rows under REMOVED campaigns/ad groups excluded.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMaximum keywords to return. Default 500, max 1000.
positiveNotrue = positive keywords only (default). false = negative keywords only.
accountIdNoAccount ID (omit for primary)
adGroupIdNoOptional ad group ID to narrow the inventory.
campaignIdNoOptional campaign ID to narrow the inventory.
enabledOnlyNotrue = only ENABLED keyword criteria (default). false = include PAUSED, still excluding REMOVED criteria.
includeBidInfoNoInclude CPC bid fields.
includeQualityInfoNoInclude quality score sub-fields.
excludeRemovedParentsNoExclude keywords whose campaign or ad group is REMOVED. Default true.
listQueryableResources
Read-only
Inspect

List all queryable GAQL resources (e.g. campaign, ad_group, keyword_view). Pair with getResourceMetadata to discover fields, then write a runScript against them.

ParametersJSON Schema
NameRequiredDescriptionDefault
accountIdNoAccount ID (omit for primary)
moveKeywords
DestructiveIdempotent
Inspect

Move keywords between ad groups in the same campaign. Inherits match type from source keywords by default — specify matchType only to override. Allows partial success: successfully-added keywords are paused in source, failed ones are left untouched. Returns changeIds for both adds and pauses.

ParametersJSON Schema
NameRequiredDescriptionDefault
accountIdNoAccount ID (omit for primary)
matchTypeNoOverride match type in destination — omit to inherit from source
campaignIdYes
toAdGroupIdYes
criterionIdsYesKeyword criterion IDs (query keyword_view via runScript)
fromAdGroupIdYes
acknowledgeExperimentImpactNoDanger override. Set true only after the user explicitly accepts that this mutation touches a campaign in an active experiment, or after applying the same intended change to both arms.
pauseAd
Idempotent
Inspect

Pause an active ad. Returns changeId.

ParametersJSON Schema
NameRequiredDescriptionDefault
adIdYes
accountIdNoAccount ID (omit for primary)
adGroupIdYes
campaignIdYesCampaign ID (for logging)
acknowledgeExperimentImpactNoDanger override. Set true only after the user explicitly accepts that this mutation touches a campaign in an active experiment, or after applying the same intended change to both arms.
pauseCampaign
DestructiveIdempotent
Inspect

Pause a campaign, stopping all its ads. Returns changeId.

ParametersJSON Schema
NameRequiredDescriptionDefault
accountIdNoAccount ID (omit for primary)
campaignIdYes
acknowledgeExperimentImpactNoDanger override. Set true only after the user explicitly accepts that this mutation touches a campaign in an active experiment, or after applying the same intended change to both arms.
pauseKeyword
Idempotent
Inspect

Pause a POSITIVE (active) keyword. Does NOT work on negative keywords — Google Ads has no 'pause' for negatives; call removeNegativeKeyword instead (and addNegativeKeyword to re-add later). Returns changeId.

ParametersJSON Schema
NameRequiredDescriptionDefault
accountIdNoAccount ID (omit for primary)
adGroupIdYes
campaignIdYes
criterionIdYesKeyword criterion ID (query keyword_view via runScript)
acknowledgeExperimentImpactNoDanger override. Set true only after the user explicitly accepts that this mutation touches a campaign in an active experiment, or after applying the same intended change to both arms.
pausePmaxAssetGroup
Idempotent
Inspect

Pause a Performance Max asset group. When paused, Google stops serving ads from this asset group while the campaign and other asset groups remain active. Use getPmaxAssetGroups to find asset group IDs. Returns a changeId for undo support.

ParametersJSON Schema
NameRequiredDescriptionDefault
accountIdNoAccount ID (omit for primary)
campaignIdYesPerformance Max campaign ID
assetGroupIdYesAsset group ID to pause (query asset_group WHERE type = PERFORMANCE_MAX via runScript)
acknowledgeExperimentImpactNoDanger override. Set true only after the user explicitly accepts that this mutation touches a campaign in an active experiment, or after applying the same intended change to both arms.
promoteExperiment
Idempotent
Inspect

Apply the treatment arm's changes back onto the base campaign and stop the trial. Long-running — like scheduleExperiment, returns immediately and you must follow up with listExperimentAsyncErrors. Use when the treatment is a clear winner and you want the base campaign to inherit the changes. Status precondition: experiment must be ENABLED. Returns changeId.

ParametersJSON Schema
NameRequiredDescriptionDefault
accountIdNoAccount ID (omit for primary)
experimentResourceNameYes
removeAd
Idempotent
Inspect

Permanently remove an ad from an ad group. This cannot be undone. Returns changeId.

ParametersJSON Schema
NameRequiredDescriptionDefault
adIdYes
accountIdNoAccount ID (omit for primary)
adGroupIdYes
campaignIdYesCampaign ID (for logging)
acknowledgeExperimentImpactNoDanger override. Set true only after the user explicitly accepts that this mutation touches a campaign in an active experiment, or after applying the same intended change to both arms.
removeBiddingStrategy
DestructiveIdempotent
Inspect

Remove a portfolio bidding strategy. All campaigns currently linked to it must be unlinked first (Google Ads will reject otherwise). Returns changeId.

ParametersJSON Schema
NameRequiredDescriptionDefault
accountIdNoAccount ID (omit for primary)
biddingStrategyIdYes
removeCampaign
DestructiveIdempotent
Inspect

PERMANENTLY remove a campaign — cannot be undone, not even with undoChange. The campaign and all its ad groups, ads, and keywords will be deleted. Prefer pauseCampaign in most cases. Returns changeId.

ParametersJSON Schema
NameRequiredDescriptionDefault
accountIdNoAccount ID (omit for primary)
campaignIdYes
acknowledgeExperimentImpactNoDanger override. Set true only after the user explicitly accepts that this mutation touches a campaign in an active experiment, or after applying the same intended change to both arms.
removeConversionAction
Idempotent
Inspect

Permanently delete a conversion action. Not undoable. Use this instead of updateConversionAction with status=REMOVED — Google rejects that with request_error=18. Conversion actions imported from GA4/UA/Floodlight/Firebase/Salesforce/Search Ads 360, Smart Campaign auto-actions, Store Visits, app-store actions, local_services_* / Local Services Ads actions, and manager-inherited actions are read-only via the API — the remove call will be rejected locally before reaching Google. To check before calling: read conversion_action.type and conversion_action.owner_customer via runScript (e.g. await ads.gaql(ads.queries.conversionActions)) or write a direct FROM conversion_action query. Modify read-only actions in the Google Ads UI or in the source system (GA4, Firebase, Salesforce, Floodlight). Returns changeId.

ParametersJSON Schema
NameRequiredDescriptionDefault
accountIdNoAccount ID (omit for primary)
conversionActionIdYesConversion action ID to permanently delete
removeKeywordFromNegativeList
DestructiveIdempotent
Inspect

Remove a keyword from a shared negative keyword list. If the same keyword text exists under multiple match types, specify matchType to remove the correct one. Returns changeId.

ParametersJSON Schema
NameRequiredDescriptionDefault
keywordYesExact keyword text to remove
accountIdNoAccount ID (omit for primary)
matchTypeNoMatch type to disambiguate
sharedSetIdYesShared set ID (query shared_set WHERE type = NEGATIVE_KEYWORDS via runScript)
removeNegativeKeyword
DestructiveIdempotent
Inspect

Remove a negative keyword from a campaign. This is the correct tool for 'pausing' or 'disabling' a negative keyword — Google Ads has no pause state for negatives, removing is the equivalent. To re-add later, call addNegativeKeyword with the same text and match type. If the same keyword text exists under multiple match types, specify matchType to remove the correct one. Returns changeId.

ParametersJSON Schema
NameRequiredDescriptionDefault
keywordYesExact negative keyword text to remove
accountIdNoAccount ID (omit for primary)
matchTypeNoMatch type to disambiguate if the same text exists under multiple match types
campaignIdYes
acknowledgeExperimentImpactNoDanger override. Set true only after the user explicitly accepts that this mutation touches a campaign in an active experiment, or after applying the same intended change to both arms.
removeNegativeKeywordList
DestructiveIdempotent
Inspect

Delete a shared negative keyword list. This also unlinks it from all campaigns. Permanent — cannot be undone. Use listNegativeKeywordLists to find the sharedSetId. Returns changeId.

ParametersJSON Schema
NameRequiredDescriptionDefault
accountIdNoAccount ID (omit for primary)
sharedSetIdYesShared set ID (query shared_set WHERE type = NEGATIVE_KEYWORDS via runScript)
renameAdGroup
Idempotent
Inspect

Rename an ad group. Returns changeId.

ParametersJSON Schema
NameRequiredDescriptionDefault
newNameYes
accountIdNoAccount ID (omit for primary)
adGroupIdYes
campaignIdYes
acknowledgeExperimentImpactNoDanger override. Set true only after the user explicitly accepts that this mutation touches a campaign in an active experiment, or after applying the same intended change to both arms.
renameCampaign
Idempotent
Inspect

Rename a campaign. Returns changeId.

ParametersJSON Schema
NameRequiredDescriptionDefault
newNameYes
accountIdNoAccount ID (omit for primary)
campaignIdYes
acknowledgeExperimentImpactNoDanger override. Set true only after the user explicitly accepts that this mutation touches a campaign in an active experiment, or after applying the same intended change to both arms.
reviewChangeImpact
Read-only
Inspect

Estimate correlational impact of every successful change in the last days using daily campaign snapshots (captured by cron). For each change: compares 7-day daily averages BEFORE vs AFTER the change date on the affected campaign, classifies direction (improved/worsened/neutral/unknown), and returns cost/conversion/CPA deltas plus otherChangesInWindow so you can spot confounders (other writes in the 14-day envelope). Response includes per-action counts and a campaign-deduped aggregate sum — use this instead of stitching getChanges + a runScript performance query by hand. Ideal for weekly or ad-hoc impact reviews. Caveats: impact is correlational (seasonality, competitor bids, Google's algorithm also move numbers); changes <3 days old are typically 'tooNew' because the snapshot cron lags a day; keyword/ad changes attribute to the containing campaign (campaign-level granularity only); window boundaries are UTC.

ParametersJSON Schema
NameRequiredDescriptionDefault
daysNoLookback window in days. Default 7 (weekly review); max 90.
limitNoMax changes to attribute. Default 50; max 200.
accountIdNoAccount ID (omit for primary)
runScript
Read-only
Inspect

Run a JavaScript orchestration script in a sandboxed QuickJS runtime. This is a REPLACEMENT for chaining individual tool calls, not a supplement — one runScript call does what would otherwise take 10+ sequential tool invocations.

── READ-ONLY (analytics and reporting only) ──

runScript is a READ-ONLY analytics sandbox. ads.gaql() and ads.gaqlParallel() only execute SELECT GAQL queries — they cannot pause, update, create, or delete anything. To mutate the account (pause keywords, update bids, create campaigns, add negatives, etc.), call the dedicated mutation tools (pauseKeyword, updateBid, bulkPauseKeywords, pauseCampaign, createCampaign, addNegativeKeyword, etc.) directly. Never try to perform mutations inside a runScript call.

── WHEN TO USE THIS ──

This is the DEFAULT tool for any open-ended analytical question about a Google Ads account. Reach for it first when you see:

  • "How is my account doing?" / "What's working?" / "What's broken?" / "How did last week go?"

  • "Audit my account" / "Find wasted spend" / "What should I change?" / "Any quick wins?"

  • Any question where you would otherwise fire 3+ read tools back-to-back

  • Any question that benefits from correlating surfaces (spend + search terms + quality scores + change events) in a single pass

runScript owns EVERY read of Google Ads data. There are no point-query read tools anymore — if the caller asks for spend, CPA, search terms, keywords, ads, impression share, or anything else expressible in GAQL, you write a runScript that queries it. The only non-runScript reads are for specialized services that aren't GAQL-expressible: searchGeoTargets, getChanges (NotFair's own change log), reviewChangeImpact, getKeywordIdeas. For schema discovery before a query, use getResourceMetadata and listQueryableResources.

── BATCHING DISCIPLINE (read this first) ──

Prefer ONE runScript call that fans out with ads.gaqlParallel (up to 20 queries concurrently) and does the full analysis in-script. Each runScript invocation costs ~5–10s of model deliberation PLUS the max GAQL latency across its queries. Batching 15 queries in one call ≈ 1 round-trip; doing the same across 5 calls ≈ 5 round-trips (5x slower).

Rules of thumb:

  • Cast a wide net on the first call. You have 20 parallel slots — use them even if you're not sure yet what you'll need. Filtering in-script is free.

  • Do NOT make follow-up runScript calls just to pull one more surface you should have included. If you catch yourself about to call runScript a second time, ask: "could I have put this in the first batch?" (almost always yes).

  • Return the finished analysis (rankings, top offenders, aggregates), not raw GaqlReport.rows arrays. The caller reads your return value into context — summarize first.

── API SURFACE (all on the ads namespace) ──

Async RPCs:

  • ads.gaql(query, limit?, options?) -> GaqlReport — single GAQL query. THIS IS THE ENTRY POINT FOR AD-HOC QUERIES. For one-off data pulls, use return await ads.gaql('SELECT ...') — there is no separate runGaqlQuery tool.

  • ads.gaqlParallel([{name, query, limit?}, ...], options?) -> { [name]: GaqlReport } — max 20 per call. USE THIS for multi-surface analysis. Fails the whole call if any subquery errors; pass { partial: true } only when you explicitly want { error } entries mixed with successful reports.

  • options.excludeRemovedParents defaults to true. Rows under REMOVED campaigns/ad groups are filtered out server-side because most audits need current serving state. Pass { excludeRemovedParents: false } only for historical analysis.

Canonical gaqlParallel shape:

const r = await ads.gaqlParallel([ { name: "campaigns", query: SELECT campaign.id, campaign.name, metrics.cost_micros FROM campaign WHERE segments.date DURING LAST_30_DAYS, limit: 50 }, { name: "searchTerms", query: SELECT search_term_view.search_term, metrics.clicks, metrics.conversions FROM search_term_view WHERE segments.date DURING LAST_30_DAYS, limit: 100 }, ]); const campaigns = r.campaigns.rows ?? [];

For intentional partial success:

const r = await ads.gaqlParallel([...], { partial: true }); const rows = "error" in r.searchTerms ? [] : r.searchTerms.rows;

Pre-built GAQL strings (sync, no RPC cost):

  • Parameterless: ads.queries.accountInfo | geoTargeting | qualityScores | adGroups | conversionActions | recommendations | billingSetups | audienceSegmentCheck | negativeKeywords | campaignAssets | adGroupAssets | sharedNegativeKeywordLists | sharedNegativeKeywordMembers | pausedCampaigns | customerManagerLinks

  • Date-windowed builders (call with YYYY-MM-DD): ads.queries.campaigns(start,end) | keywords | searchTerms | convertingSearchTerms | zeroConversionKeywords | ads | devicePerformance | networkSegmentation | landingPages | changeEvents | dailyCampaignMetrics | conversionActionPerformance

  • Canonical audit pack: ads.queries.auditPack(start,end) -> 23 named queries covering setup, campaigns, keywords, search terms, ads/assets, negatives, conversion actions/performance, recommendations, billing setup, paused campaigns, manager links, and recent Google-side change events. Prefer this for account audits instead of hand-selecting a narrow subset.

Sync helpers: ads.helpers.getDateRange(days), formatDate, micros, toMicros, normalizeCustomerId, daysBetween, extractChangedFields, generateBrandVariants Constants: ads.constants.RESOURCE_CHANGE_OP, CHANGE_RESOURCE_TYPE, CHANGE_CLIENT_TYPE (numeric enum → label maps)

── HUMANIZED RESPONSES + REPORT METADATA ──

Every GaqlReport includes meta: asOf, resource, dateRange/days, currencyCode/timeZone when selected, reportingLagDays, row limits/truncation, removed-parent behavior, campaign/ad-group status filters, campaign type filters, and data-completeness warnings. Read meta before making freshness/exhaustiveness claims.

Rows are augmented post-fetch so you can read the LLM-friendly form directly:

  • Enum integer fields get a sibling <field>_name (canonical Google Ads enum name). Read bidding_strategy_type_name === "MAXIMIZE_CONVERSIONS", not the integer 10. Avoids the BiddingStrategyType landmines (10=MAX_CONVERSIONS, 11=MAX_CONVERSION_VALUE, 9=TARGET_SPEND/MaxClicks, 15=TARGET_IMPRESSION_SHARE).

  • Money fields ending _micros get a sibling <base>_value in major units (cost_micros: 11_000_000cost_value: 11). Currency-agnostic — works for USD/EUR/JPY. Raw _micros is preserved. ⚠ IMPORTANT: _name / _value siblings are NOT GAQL fields — do NOT put them in SELECT or WHERE. They appear automatically in result rows when the corresponding raw field is selected (_name → base enum field; _value → the _micros field).

── DATE LITERALS (GAQL only supports a fixed set) ──

Valid DURING literals: TODAY, YESTERDAY, LAST_7_DAYS, LAST_14_DAYS, LAST_30_DAYS, THIS_MONTH, LAST_MONTH, LAST_BUSINESS_WEEK, LAST_WEEK_MON_SUN, LAST_WEEK_SUN_SAT, THIS_WEEK_MON_TODAY, THIS_WEEK_SUN_TODAY. There is no LAST_60_DAYS, LAST_90_DAYS, LAST_180_DAYS, THIS_YEAR, or LAST_YEAR. For windows >30 days, use a custom range:

const { start, end } = ads.helpers.getDateRange(90); const q = SELECT campaign.id, metrics.cost_micros FROM campaign WHERE segments.date BETWEEN '${start}' AND '${end}';

(As a backstop, the server auto-rewrites unsupported DURING LAST_N_DAYS/THIS_YEAR/LAST_YEAR to BETWEEN, but writing it correctly is faster and clearer.)

Note: change_event only supports the last 30 days regardless of how you express the range.

── COMMON GOTCHAS (the validator will reject these before they reach Google) ──

  • change_event REQUIRES change_event.change_date_time in WHERE. segments.date DURING ... does NOT work for this resource (Google rejects with change_event_error=3). Window cap is 30 rolling days. Easiest: ads.queries.changeEvents(start, end) builds the right shape.

  • GAQL has no SQL JOIN. Select compatible related-resource fields directly from one FROM resource (campaign_budget.amount_micros can be selected from FROM campaign), or run two queries and join rows in JavaScript.

  • Enums in WHERE are STRING names, not numbers. Write WHERE campaign.status = 'PAUSED', never = 3. Same for ad_group.status, ad_group_ad.status, ad_group_criterion.status, conversion_action.status, asset_group.status. Valid status values: ENABLED, PAUSED, REMOVED. For other enums (advertising_channel_type, bidding_strategy_type, etc.), call getResourceMetadata with the query's FROM resource, e.g. getResourceMetadata('campaign').

  • Manager-link status has no REMOVED enum. For customer_manager_link.status, use ACTIVE, INACTIVE, PENDING, REFUSED, or CANCELED; omit the filter if you only want all rows.

  • metrics.* is NOT selectable from FROM conversion_action. That resource carries dimensional fields only (name, type, status, counting). To break down metric counts by conversion action: query FROM campaign (or ad_group) and SELECT segments.conversion_action_name. To list configured actions: drop the metrics and keep only conversion_action.* fields.

  • Local Services conversion actions are often segment-only. LSA / local_services_* conversion names can appear in segments.conversion_action_name but not as mutable rows in FROM conversion_action. Before calling updateConversionAction / removeConversionAction, check conversion_action.type and conversion_action.owner_customer (e.g. via ads.gaql(ads.queries.conversionActions)); if the type is GA4/UA/Floodlight/Firebase/Salesforce/SA360 imports, Smart Campaign auto-actions, Store Visits, app-store actions, or the owner_customer points at a different customer (manager-inherited), treat as Google-managed/read-only.

  • segments.conversion_action_name and friends don't pair with metrics.cost_micros. Google reports cost at the campaign/ad_group level, not per conversion action — pick one or the other (query_error=53). For per-action cost-per-conversion, divide cost_micros (campaign-total) by per-action metrics.conversions in-script.

  • Fields used in WHERE must also be in SELECT (query_error=16). The server auto-injects campaign.status/ad_group.status for REMOVED-parent filters and promotes non-date segments.* predicate fields into SELECT automatically. Date segments are left unselected to avoid changing row granularity.

  • segments.date BETWEEN takes explicit ISO dates only. Do not write BETWEEN 'LAST_30_DAYS' AND 'undefined'; use segments.date DURING LAST_30_DAYS, or use ads.helpers.getDateRange(days) and interpolate YYYY-MM-DD dates.

  • search_term_view requires a finite segments.date filter. Include segments.date DURING LAST_30_DAYS or a BETWEEN 'YYYY-MM-DD' AND 'YYYY-MM-DD' clause.

  • keyword_view includes ad-group-level NEGATIVES. Filter ad_group_criterion.negative = FALSE for positives only — and add ad_group_criterion.negative to your SELECT (predicate-fields-must-be-in-SELECT applies). Negatives have 0 impressions/clicks/cost/conversions by definition (they block serving), so any metrics.* = 0 filter without this predicate sweeps up every negative in the account.

  • Keyword quality fields are split by resource. Query delivery metrics (metrics.clicks, metrics.cost_micros, conversions, etc.) from FROM keyword_view. Query quality-score fields from FROM ad_group_criterion without metrics: ad_group_criterion.quality_info.quality_score, creative_quality_score, post_click_quality_score, and search_predicted_ctr. There is no metrics.quality_info.quality_score, ad_group_criterion.quality_info.ad_relevance, or ad_group_criterion.quality_info.landing_page_experience.

  • Known hallucinated fields: there is no metrics.average_cpc_micros, metrics.cost_per_conversion_micros, metrics.impression_share, metrics.search_lost_is_rank, metrics.search_lost_is_budget, metrics.conversion_rate, metrics.quality_info.quality_score, asset.status, asset_group_asset.performance_label, asset.sitelink_asset.final_urls, campaign.url_expansion_opt_out, campaign.budget_micros, campaign.budget_amount_micros, campaign_criterion.proximity.address.city, campaign_criterion.audience.audience, change_event.campaign.name, change_event.resource_type, ad_group_criterion.quality_info.ad_relevance, ad_group_criterion.quality_info.landing_page_experience, campaign_experiment.*, conversion_action.default_value, conversion_action.last_conversion_date, conversion_action.most_recent_conversion_date, recommendation.impact.base_metrics.*, recommendation.keyword_match_type, billing_setup.payments_account_info.*, auction_insight.domain, or bare resource_name. Use metrics.average_cpc; use metrics.cost_per_conversion; for Search campaigns use metrics.search_impression_share, metrics.search_rank_lost_impression_share, and metrics.search_budget_lost_impression_share; calculate conversion rate from metrics.conversions / metrics.clicks; budget lives on campaign_budget.amount_micros; asset serving status lives on the link resource (campaign_asset.status, ad_group_asset.status, asset_group_asset.status, customer_asset.status); use campaign_criterion.proximity.address.city_name; use change_event.change_resource_type; use conversion_action.value_settings.default_value; use ads.queries.billingSetups for safe billing reads; replace resource_name with <resource>.resource_name; call getResourceMetadata(<resource>) for the rest.

Rules: top-level await works; no fetch/require/process/fs; return value must be JSON-serializable; defaults are 30000ms (30s) timeout, max 45000ms (45s), 500KB return cap, 100K log chars.

── CANONICAL AUDIT (one call, wide net, filter in-script) ──

const { start, end } = ads.helpers.getDateRange(30); const r = await ads.gaqlParallel(ads.queries.auditPack(start, end)); // Inspect r.campaigns.meta / r.searchTerms.meta for freshness, filters, and truncation before concluding. const worstCampaigns = (r.campaigns.rows ?? []) .map(c => ({ name: c.campaign.name, spend: c.metrics.cost_micros / 1e6, cpa: (c.metrics.cost_micros / 1e6) / (c.metrics.conversions || 1), convRate: c.metrics.conversions / (c.metrics.clicks || 1), })) .sort((a, b) => b.cpa - a.cpa).slice(0, 5); const topZeroConvKws = (r.zeroConversionKeywords.rows ?? []).slice(0, 10).map(k => ({ text: k.ad_group_criterion.keyword.text, spend: k.metrics.cost_micros / 1e6, })); return { worstCampaigns, topZeroConvKws, /* ... aggregates only, not raw rows ... */ };

── ANTI-PATTERNS (don't) ──

  • Calling runScript 5+ times in sequence to fetch different surfaces — that's exactly what gaqlParallel replaces.

  • Using ads.gaql in a JS loop when the queries are independent — use gaqlParallel.

  • Returning entire GaqlReport.rows arrays — summarize, rank, or aggregate first.

  • Passing non-SELECT statements to ads.gaql() — GAQL is read-only, the call will throw immediately. Mutations go through dedicated tools, not runScript.

ParametersJSON Schema
NameRequiredDescriptionDefault
codeYesJavaScript source. Top-level await allowed. See tool description for the API surface.
accountIdNoAccount ID (omit for primary)
timeoutMsNoWall-clock cap in MILLISECONDS before the script is interrupted. Default 30000 (30s), max 45000 (45s). Examples: pass 45000 for a 45-second cap. Do NOT pass 45 — that's 45ms and will be rejected. Raise to 45000 when batching 15+ parallel queries via gaqlParallel.
scheduleExperiment
Idempotent
Inspect

Step 4 of 5. Kick off the experiment — Google forks the in-design (trial) campaign into a real serving campaign. Returns immediately with an operation name; forking happens asynchronously over a few seconds to a few minutes. ALWAYS follow up with listExperimentAsyncErrors to verify forking succeeded — async errors don't surface from this call. Status precondition: experiment must be SETUP. Returns changeId.

ParametersJSON Schema
NameRequiredDescriptionDefault
accountIdNoAccount ID (omit for primary)
experimentResourceNameYesResource name of the experiment to schedule.
searchGeoTargets
Read-only
Inspect

Search for geo target locations by name (cities, counties, states, countries). Returns geo target constant IDs that can be used with updateCampaignSettings locationTargeting and negativeLocationTargeting. Example: search 'Kitsap County' to get its ID, then pass that ID to updateCampaignSettings to target or exclude it.

ParametersJSON Schema
NameRequiredDescriptionDefault
queryYesLocation name to search for (e.g. 'Kitsap County', 'Seattle', 'Washington', 'United States')
localeNoLocale for results (default: 'en')
accountIdNoAccount ID (omit for primary)
countryCodeNoISO 3166-1 alpha-2 country code to narrow results (e.g. 'US', 'CA', 'GB')
setGuardrails
Idempotent
Inspect

Set guardrail limits for bid changes, budget changes, and keyword pauses. Can be set at account level (omit campaignId) or per-campaign. These limits cap how much the AI can change in a single operation.

ParametersJSON Schema
NameRequiredDescriptionDefault
accountIdNoAccount ID (omit for primary)
targetCpaNoTarget CPA in dollars
campaignIdNoCampaign ID for campaign-specific guardrails (omit for account-level defaults)
monthlyCapNoMonthly spend cap in dollars
maxBidChangePctNoMax bid change per adjustment as decimal (e.g. 0.25 = 25%)
maxBudgetChangePctNoMax budget change per adjustment as decimal (e.g. 0.50 = 50%)
maxKeywordPausePctNoMax fraction of keywords that can be paused at once (e.g. 0.30 = 30%)
setTrackingTemplate
Idempotent
Inspect

Set or clear the click-tracking URL suffix at the account, campaign, ad group, or ad level. Uses ValueTrack parameters. Pass empty string to clear. Returns changeId.

ParametersJSON Schema
NameRequiredDescriptionDefault
adIdNoThe ad ID. Required when level is 'ad'.
levelYes
accountIdNoAccount ID (omit for primary)
adGroupIdNoThe ad group ID. Required when level is 'ad_group'.
campaignIdNoThe campaign ID. Required when level is 'campaign'.
trackingTemplateYesTracking URL template (e.g. '{lpurl}?utm_source=google&utm_medium=cpc'). Empty string to remove.
acknowledgeExperimentImpactNoDanger override. Set true only after the user explicitly accepts that this mutation touches a campaign in an active experiment, or after applying the same intended change to both arms.
summarizeAccountSetup
Read-only
Inspect

One-shot, human-readable snapshot of how the account is configured: currency + time zone, every non-removed campaign with its bidding strategy and tCPA/tROAS in major units, every conversion action with category + primary_for_goal flag, plus diagnostic notes when the setup is unusual (no primary conversion action, mixed optimization modes). Call this FIRST in any strategic conversation — it gives you the conversion hierarchy and bidding posture as named strings so you don't misread enum integers (the BiddingStrategyType landmines: 10=MAXIMIZE_CONVERSIONS, 11=MAXIMIZE_CONVERSION_VALUE, 9=TARGET_SPEND, 15=TARGET_IMPRESSION_SHARE) or treat micros as dollars. Replaces 3+ runScript calls (account info + campaigns + conversion actions) for the canonical setup question.

ParametersJSON Schema
NameRequiredDescriptionDefault
accountIdNoAccount ID (omit for primary)
undoChange
Destructive
Inspect

Undo a previous write operation by changeId. Only works within 7 days AND only if the entity hasn't been modified since the original change. Returns error if either condition is not met.

ParametersJSON Schema
NameRequiredDescriptionDefault
changeIdYeschangeId returned by the original write operation
accountIdNoAccount ID (omit for primary)
unlinkNegativeListFromCampaign
DestructiveIdempotent
Inspect

Unlink a shared negative keyword list from a campaign. The list's keywords will no longer be blocked for this campaign. Returns changeId.

ParametersJSON Schema
NameRequiredDescriptionDefault
accountIdNoAccount ID (omit for primary)
campaignIdYes
sharedSetIdYesShared set ID (query shared_set WHERE type = NEGATIVE_KEYWORDS via runScript)
acknowledgeExperimentImpactNoDanger override. Set true only after the user explicitly accepts that this mutation touches a campaign in an active experiment, or after applying the same intended change to both arms.
updateAdAssets
DestructiveIdempotent
Inspect

Replace headlines and descriptions for a Responsive Search Ad. Headlines and descriptions are COMPLETE replacement — provide every asset, not just changed ones. Display URL paths (path1/path2) are partial: omit them and existing values are preserved; provide them to override. Optionally pin assets to fixed positions. Returns changeId.

ParametersJSON Schema
NameRequiredDescriptionDefault
adIdYes
path1NoDisplay URL path 1 (max 15 chars, no spaces). Omit to preserve the existing path.
path2NoDisplay URL path 2 (max 15 chars, no spaces). Requires path1. Omit to preserve the existing path.
accountIdNoAccount ID (omit for primary)
adGroupIdYes
headlinesYesComplete replacement headlines (3-15, max 30 chars each)
campaignIdYesCampaign ID (for logging)
descriptionsYesComplete replacement descriptions (2-4, max 90 chars each)
acknowledgeExperimentImpactNoDanger override. Set true only after the user explicitly accepts that this mutation touches a campaign in an active experiment, or after applying the same intended change to both arms.
updateAdFinalUrl
Idempotent
Inspect

Update the landing page URL for an ad. Returns changeId.

ParametersJSON Schema
NameRequiredDescriptionDefault
adIdYes
finalUrlYes
accountIdNoAccount ID (omit for primary)
adGroupIdYes
campaignIdYesCampaign ID (for logging)
acknowledgeExperimentImpactNoDanger override. Set true only after the user explicitly accepts that this mutation touches a campaign in an active experiment, or after applying the same intended change to both arms.
updateAdGroup
Idempotent
Inspect

Update an ad group's default max CPC bid, target CPA, status, and/or name in one call. Use cpcBidDollars to set the ad-group default bid (only effective on MANUAL_CPC / ENHANCED_CPC campaigns); use targetCpaDollars to override the campaign's target CPA at the ad-group level (only effective on TARGET_CPA / MAXIMIZE_CONVERSIONS campaigns — surfaces a warning otherwise, no error). Subject to the per-account maxBidChangePct guardrail (default 25%, raise with setGuardrails). The guardrail is bypassed only when there's no real ad-group-level bid yet — cpc_bid_micros is either null (inheriting the campaign default) or 0 (set-but-unset). Any positive existing bid — including Google's €0.01 (10,000 micros) placeholder on newly-created MANUAL_CPC ad groups — is treated as a real value and the cap applies. To ramp a freshly-launched ad group from the placeholder, call setGuardrails ({ maxBidChangePct: 1.0 }) first, do the bumps, then restore the guardrail. Returns changeId.

ParametersJSON Schema
NameRequiredDescriptionDefault
statusNoSet ad group status.
newNameNoRename the ad group. Use this OR renameAdGroup — equivalent behavior.
accountIdNoAccount ID (omit for primary)
adGroupIdYes
campaignIdYesCampaign ID (for logging and guardrail resolution)
cpcBidDollarsNoNew ad-group default max CPC in dollars (e.g. 1.50). Only honored for MANUAL_CPC/ENHANCED_CPC campaigns.
targetCpaDollarsNoOverride the campaign's target CPA at the ad-group level, in dollars (minimum 0.10). Only effective on TARGET_CPA / MAXIMIZE_CONVERSIONS campaigns.
acknowledgeExperimentImpactNoDanger override. Set true only after the user explicitly accepts that this mutation touches a campaign in an active experiment, or after applying the same intended change to both arms.
updateBid
Idempotent
Inspect

Update a keyword's CPC bid. Only works with MANUAL_CPC or ENHANCED_CPC bidding. Each call is capped by the per-account/per-campaign maxBidChangePct guardrail (default 25%); raise it with setGuardrails (max 100% per call) and confirm with the user before stepping bigger. Returns changeId.

ParametersJSON Schema
NameRequiredDescriptionDefault
accountIdNoAccount ID (omit for primary)
adGroupIdYes
campaignIdYes
criterionIdYesKeyword criterion ID (query keyword_view via runScript)
newBidDollarsYesNew bid in dollars (e.g. 1.50)
acknowledgeExperimentImpactNoDanger override. Set true only after the user explicitly accepts that this mutation touches a campaign in an active experiment, or after applying the same intended change to both arms.
updateBiddingStrategy
Idempotent
Inspect

Edit a portfolio bidding strategy's name and/or target value. You can change targetCpa on TARGET_CPA/MAXIMIZE_CONVERSIONS strategies, and targetRoas on TARGET_ROAS/MAXIMIZE_CONVERSION_VALUE strategies. The strategy type itself cannot be changed. Returns changeId.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameNo
accountIdNoAccount ID (omit for primary)
targetCpaNoNew target CPA in dollars
targetRoasNoNew target ROAS multiplier
biddingStrategyIdYes
updateCampaignBidding
Idempotent
Inspect

Update a campaign's bidding strategy. Supports: TARGET_CPA (set a target cost per acquisition), MAXIMIZE_CONVERSIONS (optionally with a target CPA cap), MAXIMIZE_CONVERSION_VALUE (maximize total conversion value, optionally with a target ROAS — required for PMAX value-based bidding), TARGET_ROAS (target return on ad spend), MAXIMIZE_CLICKS (optionally with cpcBidCeiling), MANUAL_CPC, TARGET_IMPRESSION_SHARE (presence-based — 'just win' on a given SERP position, ideal for brand campaigns). For TARGET_CPA, targetCpa is required (in dollars). For MAXIMIZE_CONVERSIONS, targetCpa is optional (acts as a cap). For TARGET_ROAS and MAXIMIZE_CONVERSION_VALUE, targetRoas is required/optional respectively (e.g. 2.0 = 200% ROAS). For TARGET_IMPRESSION_SHARE, impressionShareLocation, locationFraction, and cpcBidCeiling are all required — Google will not accept this strategy without all three. For MAXIMIZE_CLICKS, cpcBidCeiling is optional (defaults to effectively uncapped). Passing cpcBidCeiling with any other strategy is rejected. Returns a changeId for undo support.

ParametersJSON Schema
NameRequiredDescriptionDefault
accountIdNoAccount ID (omit for primary)
targetCpaNoTarget CPA in dollars (e.g. 10.50 for $10.50). Required for TARGET_CPA, optional cap for MAXIMIZE_CONVERSIONS.
campaignIdYes
targetRoasNoTarget ROAS as a multiplier (e.g. 2.0 = 200% return). Required for TARGET_ROAS, optional cap for MAXIMIZE_CONVERSION_VALUE.
cpcBidCeilingNoMax CPC bid cap in dollars (e.g. 2.00 = $2.00). REQUIRED for TARGET_IMPRESSION_SHARE — without a ceiling Google can bid unbounded to hit the IS target. OPTIONAL for MAXIMIZE_CLICKS (default: effectively uncapped at $10,000) — use this to lower an oversized cap on Target Spend / Maximize Clicks campaigns. REJECTED for all other strategies; they have no campaign-level CPC ceiling.
biddingStrategyYesThe bidding strategy to set. Use MAXIMIZE_CONVERSION_VALUE for Performance Max campaigns optimizing for revenue/value. Use TARGET_IMPRESSION_SHARE for brand campaigns where 'just win the auction' matters more than per-conversion efficiency.
locationFractionNoTARGET_IMPRESSION_SHARE only: the IS target as a fraction from 0.01 to 1.00 (e.g. 0.95 = 95%). Typical brand target is 0.90–0.95.
impressionShareLocationNoTARGET_IMPRESSION_SHARE only: where on the SERP to target. TOP_OF_PAGE = above organic results (most common for brand). ABSOLUTE_TOP_OF_PAGE = position 1. ANYWHERE_ON_PAGE = any paid slot.
acknowledgeExperimentImpactNoDanger override. Set true only after the user explicitly accepts that this mutation touches a campaign in an active experiment, or after applying the same intended change to both arms.
updateCampaignBudget
Idempotent
Inspect

Update a campaign's daily budget. Capped at 50% change per adjustment, minimum $1/day. Returns changeId.

ParametersJSON Schema
NameRequiredDescriptionDefault
accountIdNoAccount ID (omit for primary)
campaignIdYes
newDailyBudgetDollarsYesNew daily budget in dollars (e.g. 25.00)
acknowledgeExperimentImpactNoDanger override. Set true only after the user explicitly accepts that this mutation touches a campaign in an active experiment, or after applying the same intended change to both arms.
updateCampaignGoals
Idempotent
Inspect

Switch a campaign between campaign-specific and account-level conversion goals. Set to CUSTOMER to use account-level goals (required before switching to non-conversion bidding strategies like MAXIMIZE_CLICKS or MANUAL_CPC). Set to CAMPAIGN for campaign-specific goals. Note: updateCampaignBidding auto-handles this when switching to MAXIMIZE_CLICKS or MANUAL_CPC, so this tool is only needed for manual goal config changes.

ParametersJSON Schema
NameRequiredDescriptionDefault
accountIdNoAccount ID (omit for primary)
campaignIdYes
goalConfigLevelYesCUSTOMER = use account-level conversion goals. CAMPAIGN = use campaign-specific conversion goals.
acknowledgeExperimentImpactNoDanger override. Set true only after the user explicitly accepts that this mutation touches a campaign in an active experiment, or after applying the same intended change to both arms.
updateCampaignLanguages
Idempotent
Inspect

Add or remove language targeting criteria on a campaign. Pass language constant IDs (e.g. '1000' for English, '1003' for Spanish). Returns a changeId per mutation.

ParametersJSON Schema
NameRequiredDescriptionDefault
addNoLanguage constant IDs to add (e.g. ['1000'] for English)
removeNoLanguage constant IDs to remove
accountIdNoAccount ID (omit for primary)
campaignIdYes
acknowledgeExperimentImpactNoDanger override. Set true only after the user explicitly accepts that this mutation touches a campaign in an active experiment, or after applying the same intended change to both arms.
updateCampaignSettings
Idempotent
Inspect

Update campaign network targeting, location targeting, and/or ad schedule. Networks: toggle Google Search, Search Partners, Display Network. Locations: add/remove geo targets (positive or negative) by geo target constant ID (e.g. '2840' for US, '200840' for Seattle-Tacoma DMA). Ad schedule: replace the entire schedule with a list of slots (use dayOfWeek 'ALL' as a shortcut for all 7 days; pass an empty array to clear the schedule and run 24/7). NOTE: If the campaign uses smart bidding (TARGET_CPA/TARGET_ROAS/MAXIMIZE_CONVERSIONS/MAXIMIZE_CONVERSION_VALUE), schedule restrictions are respected but can hurt performance by removing learning signal. Prefer 24/7 schedules unless you have strong evidence specific hours are unprofitable. Returns a changeId per mutation plus any warnings. Geo intent: set positiveGeoTargetType to PRESENCE (only people physically in the area) or PRESENCE_OR_INTEREST (default — also includes people searching for the area). Proximity: add radius-based targeting (5-mile circles) by lat/lng via proximityTargeting.add; remove by criterionId via proximityTargeting.remove (get criterionIds from getCampaignSettings or runScript).

ParametersJSON Schema
NameRequiredDescriptionDefault
networksNoNetwork targeting toggles — only specified fields are changed
accountIdNoAccount ID (omit for primary)
adScheduleNoAd schedule (dayparting) — REPLACES the entire current schedule. For smart-bidding campaigns, non-24/7 schedules can reduce learning signal; the tool returns a SMART_BIDDING_SCHEDULE_RESTRICTION warning when detected.
campaignIdYes
locationTargetingNoPositive location targeting — where ads should show
proximityTargetingNoRadius-based proximity targeting — target people within N miles/km of a lat/lng point.
negativeGeoTargetTypeNoWho is excluded based on excluded locations. PRESENCE: exclude people physically there. PRESENCE_OR_INTEREST: exclude people in or interested in the excluded area.
positiveGeoTargetTypeNoWho sees ads based on location intent. PRESENCE: only people physically in the targeted area. PRESENCE_OR_INTEREST: people in OR interested in the area (Google default). Use PRESENCE for purely local intent; use PRESENCE_OR_INTEREST for broader reach.
negativeLocationTargetingNoNegative location targeting — where ads should NOT show
acknowledgeExperimentImpactNoDanger override. Set true only after the user explicitly accepts that this mutation touches a campaign in an active experiment, or after applying the same intended change to both arms.
updateConversionAction
Idempotent
Inspect

Update an existing conversion action's settings — promote secondary to primary, change value, rename, fix currency. Conversion actions imported from GA4/UA/Floodlight/Firebase/Salesforce/Search Ads 360, Smart Campaign auto-actions, Store Visits, app-store actions, local_services_* / Local Services Ads actions, and manager-inherited actions are read-only via the API — the update call will be rejected locally before reaching Google. To check before calling: read conversion_action.type and conversion_action.owner_customer via runScript (e.g. await ads.gaql(ads.queries.conversionActions)) or write a direct FROM conversion_action query. LSA conversion names may appear in segments.conversion_action_name without appearing as mutable FROM conversion_action rows. To delete a conversion action, use removeConversionAction (status=REMOVED is not accepted by Google for updates). Returns changeId.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameNo
statusNoENABLED = active. To delete, use removeConversionAction instead — Google rejects status=REMOVED on update.
categoryNo
accountIdNoAccount ID (omit for primary)
countingTypeNo
currencyCodeNoISO 4217 currency code (e.g. 'USD', 'EUR') for this action's conversion values. Use this to migrate legacy 'XXX' (unset) actions to a real currency so reporting can roll up. 'XXX' is rejected on writes.
defaultValueNoDefault conversion value in account currency
primaryForGoalNotrue = primary (included in Conversions column for bidding), false = secondary (observation only)
conversionActionIdYesConversion action ID (query conversion_action via runScript)
alwaysUseDefaultValueNo
enhancedConversionsForLeadsNoEnable Enhanced Conversions for Leads at account level
viewThroughLookbackWindowDaysNo
clickThroughLookbackWindowDaysNo
uploadClickConversions
Idempotent
Inspect

Upload offline click conversions to Google Ads for attribution. Supports Enhanced Conversions for Leads via hashed email/phone matching. Each conversion needs a gclid OR hashed user identifiers. Max 2000 conversions per call. Partial failures are reported per-row.

ParametersJSON Schema
NameRequiredDescriptionDefault
accountIdNoAccount ID (omit for primary)
conversionsYesConversions to upload (max 2000 per request)
conversionActionIdYesConversion action ID to attribute conversions to

Discussions

No comments yet. Be the first to start the discussion!

Try in Browser

Your Connectors

Sign in to create a connector for this server.