mine-search-terms
Audit search terms across campaigns, flag wasted spend, and add negative keywords in batches. Pulls search term reports via GAQL, evaluates every term against the ads-playbook methodology, outputs a CSV with reasoning, and offers batch negative keyword execution. Use when: "/mine-search-terms", "audit search terms", "find wasted spend", "negative keywords", "search term mining", "clean up search terms", "block bad keywords".
Mine Search Terms
You are running a search term audit. This is the core workflow of the Paid Ads Autopilot, based on Austin Lau's (Anthropic growth marketer) actual process for managing Google Ads search terms.
Before you start, read the ads-playbook skill to load business context, thresholds, and the negative keyword taxonomy.
Key insight from Austin Lau: "You need to provide a strong base of examples on what to actually query. If you want to know why spend dropped you need to look at impression share as well but Claude might not know that out the gate. Provide details on what to query, not just how to query GAQL." This skill encodes those query patterns so you don't have to figure them out each time.
Step 1: Identify Campaigns to Audit
Ask the user which campaign(s) to audit. Options:
- A specific campaign by name
- All enabled search campaigns
- A list of campaigns
If the user already specified a campaign in their message, use that. Don't ask again.
Step 2: Pull Search Terms via GAQL
Run this query through the Google Ads MCP server. Adjust the LAST_14_DAYS window based on the ads-playbook lookback setting, or if the user requests a different range.
SELECT
search_term_view.search_term,
campaign.name,
ad_group.name,
segments.keyword.info.text,
segments.keyword.info.match_type,
metrics.impressions,
metrics.clicks,
metrics.cost_micros,
metrics.average_cpc,
metrics.ctr,
metrics.conversions,
metrics.cost_per_conversion
FROM search_term_view
WHERE segments.date DURING LAST_14_DAYS
AND metrics.clicks >= 1
ORDER BY metrics.cost_micros DESC
If auditing a specific campaign, add: AND campaign.name = '{campaign_name}'
Cost conversion: Divide all cost_micros values by 1,000,000 for human-readable dollars. Do this for metrics.cost_micros, metrics.average_cpc, and metrics.cost_per_conversion.
If the MCP server isn't connected or the query fails, tell the user. They can also paste exported search term data from Google Ads and you'll analyze that instead.
Step 3: Evaluate Every Search Term
For each search term returned, evaluate it against the ads-playbook methodology. Apply the evaluation logic in this order:
- Fully off-theme? Zero relevance to the business. Block.
- Brand/bottom-funnel leakage? Competitor brands or own brand in wrong campaign. Block.
- Job/interview intent? Career, salary, hiring queries. Block.
- Developer/coding intent? API, SDK, github, code queries. Block.
- Too generic? Single word, no product intent. Block.
- Wrong intent? Informational, free-seeker, definition. Block.
- Research-only? Review/reddit/comparison queries. Flag for review.
- Comparison shoppers? "[X] vs [Y]", alternatives queries. Flag for review.
- Bad metrics? Above max CPA, below min CTR, zero conversions above spend threshold. Flag with metric detail.
- Passes everything? Keep.
For each flagged term, assign:
- Action:
blockorreview - Category: One of the 8 categories from the playbook
- Reasoning: A specific, human-readable sentence explaining WHY. Not just the category name.
Good reasoning examples:
- "Too generic, single word 'minutemate' doesn't indicate meeting notes intent"
- "Off-theme, search term doesn't relate to meeting notes or the 'Meeting Transcription' ad group"
- "Developer/coding intent, off-theme for meeting notes use case"
- "Competitor brand, navigational intent"
- "Competitor brand leaking into action items ad group"
- "Off-topic, user is looking for medical content, not meeting notes"
- "Ambiguous but likely job interview prep, not meeting notes functionality"
- "Bottom-funnel/brand intent, better served by brand campaign"
- "Off-audience, academic lecture notes, not business meeting notes"
Bad reasoning (don't do this):
- "Blocked" (no explanation)
- "Wrong intent" (just repeating the category)
- "Not relevant" (too vague)
Step 4: Generate CSV Output
Save a CSV file named search-term-negatives-YYYY-MM-DD.csv (using today's date) to the user's Desktop (~/Dropbox/Mac/Desktop/ or ~/Desktop/).
CSV columns (exact order):
Campaign,Ad Group,Keyword,Search Term,Match Type,Cost,Clicks,Impressions,CPC,CTR,Conversions,Reasoning
Include ALL flagged terms (both block and review). Don't include terms that passed evaluation.
Cost= cost_micros / 1,000,000, formatted as dollars (e.g., $12.45)CPC= average_cpc / 1,000,000, formatted as dollarsCTR= formatted as percentage (e.g., 3.2%)Reasoning= the specific explanation sentence, quoted if it contains commas
Step 5: Present In-Chat Summary
After generating the CSV, present this summary:
Top 10 Flagged Terms
Show a table with the 10 highest-spend flagged terms:
| Search Term | Ad Group | Cost | Reasoning |
|---|---|---|---|
| [term] | [ad group] | $XX.XX | [reasoning sentence] |
Negation Category Breakdown
Show counts by category:
- Wrong intent: X terms
- Brand/bottom-funnel leakage: X terms
- Job/interview intent: X terms
- Research-only intent: X terms
- Comparison shoppers: X terms
- Developer/coding intent: X terms
- Too generic: X terms
- Fully off-theme: X terms
- Total flagged: X terms
- Total estimated wasted spend: $X,XXX.XX
Totals
- Total search terms analyzed: X
- Terms flagged: X (Y% of total)
- Terms kept: X
- CSV saved to: [path]
Step 6: Negative Keyword Execution
After presenting the summary, ask:
"Want me to add these negatives? I'll batch them by ad group and add at the CAMPAIGN level so the same term can't leak into other ad groups. You'll approve each batch individually."
If the user says yes:
Batching Logic
- Group flagged terms (action =
blockonly, notreview) by ad group - Deduplicate terms across batches. If the same search term appears in multiple ad groups, include it once in the first batch and note the dedup in subsequent batches.
- Present each batch like this:
Batch 1: [Ad Group Name] [X] negatives, $[wasted spend] wasted spend
Terms: [list first 10, then "and X more" if over 10]
Adding at CAMPAIGN level: [Campaign Name]
Proceed? (yes/no)
- Wait for explicit "yes" before executing each batch
- After executing, confirm: "[X] negatives added to [Campaign Name]"
- Move to the next batch
Execution via MCP
For Google Ads, use the MCP write tools to add negative keywords at the campaign level. If using the read-only MCP server, tell the user they need the read+write server (Option B) or to apply manually using the CSV.
If the user wants to add at ad-group level instead of campaign level, they can say so. But default to campaign level.
Edge Cases
- No MCP connected: Analyze pasted data instead. Skip the execution step and point them to the CSV for manual upload via Google Ads Editor.
- No flagged terms: Congratulate them. "Your search terms are clean. No negatives needed. Either your keyword targeting is tight or the lookback window was too short."
- Massive result set (1000+ terms): Process all of them. Don't truncate. The CSV handles the volume; the in-chat summary keeps it digestible.
- Meta Ads: Meta doesn't have search terms the same way. For Meta, analyze audience targeting overlap and placement performance instead. Note this to the user.
- Apple Search Ads: Use the Search Term Report API endpoint. The evaluation logic is the same but the query syntax differs. Adapt accordingly.