Search Campaign Audit logo

Search Campaign Audit

Organization
cognyai
search-campaign-audit

Search campaign audit for Google Ads (with Microsoft Ads parity) — match types, quality score, negative keyword gaps, search terms, ad strength, RSA asset coverage

Overview

Publishercognyai
Repositoryclaude-code-marketing-skills
Skill namesearch-campaign-audit
Stars
111
Forks
15
Bundled files
Instructions only
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • Self-contained

    Everything the model needs lives in the instructions — no extra files to sync.

  • Open source

    Published by cognyai on GitHub. Read the source before you install it.

Installation

Install the Search Campaign Audit AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
    https://github.com/cognyai/claude-code-marketing-skills/tree/main/skills/search-campaign-audit
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/cognyai/claude-code-marketing-skills.git /tmp/claude-code-marketing-skills
mkdir -p .claude/skills
cp -r /tmp/claude-code-marketing-skills/skills/search-campaign-audit .claude/skills/search-campaign-audit
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Search Campaign Audit in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Search Campaign Audit on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Search Campaign Audit is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

Search Campaign Audit

A deep audit of your Google Ads Search campaigns — the campaign type where you still control keywords, match types, and ads directly. Finds quality-score drags, wasted match-type spend, negative gaps, and weak ads.

Requires: Cogny Agent subscription ($9/mo) — Sign up

Works with

JobPrimaryAlso answers itDifferences to state
Campaigns, keywords, QS, search terms, adsGoogle Ads (Google_Ads)Microsoft Ads (Bing_Ads)Bing money is in account currency units (not micros); percents are percents; QS components are 1–3 (below / average / above)
App Store search terms—Apple Search Ads (Apple_Search_Ads)Only for app campaigns; taps and installs, not clicks and conversions
Landing-page conversion cross-checkGoogle Analytics 4PlausibleGA4 key events vs Ads conversions; different attribution and counting
Keyword gaps and competitor paid termsGoogle Ads tool_generate_keyword_ideasSemrush, AhrefsSearch volumes are estimates and differ between tools

Call list_integrations once, audit every connected search engine, and report them side by side. Never sum spend across engines with different currencies.

Step 0: Cogny check

If no mcp__cogny__* tools exist (no list_integrations, no cogny_status), print this and stop:

Search Campaign Audit needs your marketing data through Cogny's MCP gateway.

1. Sign up at https://cogny.com and connect Google Ads and/or Microsoft Ads (plus GA4,
   Plausible, Semrush or Ahrefs if you use them).
2. Add Cogny to .mcp.json in your project:
   {"mcpServers": {"cogny": {"type": "http", "url": "https://app.cogny.com/mcp"}}}
3. Restart Claude Code; it opens a browser for the OAuth login on first use.

Nothing to run without Cogny.

If Cogny is connected but neither mcp__cogny__mcp__Google_Ads__tool_execute_gaql nor mcp__cogny__mcp__Bing_Ads__tool_get_report is available, say "connect Google Ads or Microsoft Ads in Cogny" and stop. An engine that is listed but unhealthy, or whose first read returns 401/permission denied, gets "reconnect in Cogny". With only Microsoft Ads connected, run the "Microsoft Ads" branch of each step.

Usage

/search-campaign-audit — audit every Search campaign in the account /search-campaign-audit Brand - Exact — audit one campaign by name or id

Benchmarks

SignalPoorOKGood
Quality Score≤45–67–10
Ad StrengthPoor / AverageGoodExcellent
Search term relevance<50% spend on intent terms50–75%>75%
Impression share lost (rank)>40%20–40%<20%

Steps

Google Ads money fields are micros (divide by 1,000,000). An empty GAQL result (no rows, or the tool returns no output) means zero: no matching entities or no activity in the window. Report it as 0, not as an error.

1. Find the Search campaigns

sql
SELECT campaign.id, campaign.name, campaign.status,
  campaign.bidding_strategy_type, campaign_budget.amount_micros,
  metrics.cost_micros, metrics.conversions, metrics.conversions_value,
  metrics.search_impression_share, metrics.search_rank_lost_impression_share,
  metrics.search_budget_lost_impression_share
FROM campaign
WHERE campaign.advertising_channel_type = 'SEARCH'
  AND segments.date DURING LAST_30_DAYS
ORDER BY metrics.cost_micros DESC

Flag campaigns losing >40% impression share to rank (quality/bids) or to budget.

Microsoft Ads: mcp__cogny__mcp__Bing_Ads__tool_list_accounts, then mcp__cogny__mcp__Bing_Ads__tool_get_campaigns (account_id, campaign_types: ["Search"]) and mcp__cogny__mcp__Bing_Ads__tool_get_report (account_id, report_type: "campaign", start_date, end_date). Every tool_get_report call below also takes that account_id. Impression share columns are ImpressionSharePercent, ImpressionLostToRankAggPercent and ImpressionLostToBudgetPercent.

2. Keyword performance & quality score

sql
SELECT ad_group_criterion.keyword.text, ad_group_criterion.keyword.match_type,
  ad_group_criterion.quality_info.quality_score,
  ad_group_criterion.quality_info.creative_quality_score,
  ad_group_criterion.quality_info.post_click_quality_score,
  ad_group_criterion.quality_info.search_predicted_ctr,
  campaign.name, ad_group.name,
  metrics.cost_micros, metrics.conversions, metrics.clicks, metrics.impressions
FROM keyword_view
WHERE segments.date DURING LAST_30_DAYS AND metrics.cost_micros > 0
ORDER BY metrics.cost_micros DESC
LIMIT 100

Quality Score 0 or null means not scored (too little traffic), not a bad score. Count those keywords separately as "not scored" and leave them out of the QS ≤4 counts and the average QS.

Flag:

  • Quality Score ≤4 (1–4, excluding unscored) on spending keywords — and read the three components to say why (creative_quality_score = ad relevance, post_click_quality_score = landing page, search_predicted_ctr = expected CTR)
  • High-spend keywords with zero conversions
  • Duplicate keywords across ad groups (internal auction competition)

Microsoft Ads: tool_get_report with report_type: "keyword". QualityScore is 1–10; ExpectedCtr, AdRelevance and LandingPageExperience are 1 (below average), 2 (average) or 3 (above average). Map them to the same three diagnoses. Match type is BidMatchType.

3. Match-type mix

Aggregate spend and conversions by match_type (EXACT / PHRASE / BROAD).

Flag:

  • Broad match taking a large share of spend with a worse CPA than exact/phrase — broad only works well with Smart Bidding and tight negatives
  • Accounts that are 100% exact (likely missing volume) or 100% broad (likely bleeding)

4. Search term analysis

sql
SELECT search_term_view.search_term, search_term_view.status, campaign.name,
  ad_group.name, metrics.cost_micros, metrics.conversions, metrics.clicks
FROM search_term_view
WHERE segments.date DURING LAST_30_DAYS AND metrics.cost_micros > 0
ORDER BY metrics.cost_micros DESC
LIMIT 100

Flag:

  • Irrelevant / off-intent terms with spend — negative keyword candidates
  • Converting terms with status NONE (not yet added as keywords) — harvest them
  • The total cost of zero-conversion terms — this is the headline waste number

Microsoft Ads: tool_get_report with report_type: "search_query". There is no "added/excluded" status column, so compare SearchQuery against the keyword list from tool_get_keywords (account_id, ad_group_id) to find harvest candidates. Terms that waste money on Google usually waste it on Bing too; flag terms negated in one engine only.

Apple Search Ads (app campaigns only): tool_list_acls for the org_id, then tool_get_search_term_report (org_id, campaign_id, start_date, end_date). Judge terms by installs and cost per install, not conversions.

5. Negative keyword coverage

Negatives live in three places. Query all three before saying a campaign has none.

sql
SELECT shared_set.name, shared_set.type, shared_set.member_count
FROM shared_set
WHERE shared_set.type = 'NEGATIVE_KEYWORDS'
sql
SELECT campaign.name, campaign_criterion.keyword.text, campaign_criterion.keyword.match_type
FROM campaign_criterion
WHERE campaign_criterion.type = 'KEYWORD' AND campaign_criterion.negative = TRUE
  AND campaign.advertising_channel_type = 'SEARCH' AND campaign.status = 'ENABLED'
sql
SELECT campaign.name, ad_group.name, ad_group_criterion.keyword.text,
  ad_group_criterion.keyword.match_type
FROM ad_group_criterion
WHERE ad_group_criterion.type = 'KEYWORD' AND ad_group_criterion.negative = TRUE
  AND campaign.advertising_channel_type = 'SEARCH' AND campaign.status = 'ENABLED'

Flag: no negatives at any level, no shared negative list (campaign/ad-group negatives only are hard to maintain), brand terms bidding or matching in non-brand campaigns (check the step 2 keywords and step 4 search terms against the brand name; give their spend), brand terms not negated in non-brand campaigns, and no list of perennial junk (free, jobs, diy, cheap — adjust to the business).

Microsoft Ads: tool_get_negative_keywords (account_id, entity_type: "Campaign", entity_ids). It returns negatives set on campaigns or ad groups only, not shared lists, so say so before calling a campaign "unprotected". Apple Search Ads: tool_get_campaign_negative_keywords.

6. Ad strength & RSA coverage

sql
SELECT ad_group.name, ad_group_ad.ad.id, ad_group_ad.ad.type,
  ad_group_ad.ad_strength, ad_group_ad.status, campaign.name,
  metrics.impressions, metrics.conversions
FROM ad_group_ad
WHERE campaign.advertising_channel_type = 'SEARCH'
  AND campaign.status = 'ENABLED'
  AND ad_group_ad.status = 'ENABLED'
  AND segments.date DURING LAST_30_DAYS

The query above drops ads with no impressions in the window, so find empty ad groups without a date range:

sql
SELECT campaign.name, ad_group.id, ad_group.name, ad_group.status
FROM ad_group
WHERE campaign.advertising_channel_type = 'SEARCH' AND campaign.status = 'ENABLED'
  AND ad_group.status = 'ENABLED'
sql
SELECT ad_group.id, ad_group_ad.ad.id, ad_group_ad.status
FROM ad_group_ad
WHERE campaign.advertising_channel_type = 'SEARCH' AND campaign.status = 'ENABLED'
  AND ad_group.status = 'ENABLED' AND ad_group_ad.status = 'ENABLED'

An enabled ad group whose ad_group.id is absent from the second result has zero enabled ads.

Flag:

  • Ad groups with only one RSA (no test running)
  • ad_group_ad.ad_strength of POOR or AVERAGE
  • Any remaining Expanded Text Ads (deprecated)
  • Ad groups with zero enabled ads

Microsoft Ads: tool_get_report with report_type: "ad" gives AdStrength, AdType and AdStatus; tool_get_ads (account_id, ad_group_id) adds editorial status.

7. Asset (extension) coverage

Every field you filter on must also be in SELECT, so campaign.advertising_channel_type and campaign.status are selected here:

sql
SELECT campaign.name, campaign.advertising_channel_type, campaign.status,
  campaign_asset.field_type, campaign_asset.status
FROM campaign_asset
WHERE campaign.advertising_channel_type = 'SEARCH' AND campaign.status = 'ENABLED'
  AND campaign_asset.status = 'ENABLED'

Account-level assets apply to every campaign that has none of that type of its own:

sql
SELECT customer_asset.field_type, customer_asset.status
FROM customer_asset
WHERE customer_asset.status = 'ENABLED'

A campaign is covered for a type if it has it at campaign level or the account has it. Flag campaigns missing sitelinks, callouts, or structured snippets at both levels — free real estate and CTR that most accounts leave on the table.

7b. Enhanced when connected: landing pages and keyword gaps

  • Landing-page check (GA4 or Plausible). For keywords with QS landing-page "below average", pull the final URL's sessions and key events: GA4 tool_list_account_summaries for the property, then tool_run_report (property_id, date_ranges, dimensions: ["landingPage", "sessionGoogleAdsKeyword"], metrics: ["sessions", "keyEvents"]) or Plausible mcp__cogny__mcp__Plausible__breakdown (site_id, dimension: "visit:entry_page", filters on visit:utm_source). A page that converts paid traffic far below the site average confirms the QS diagnosis.
  • Keyword gaps. Seed mcp__cogny__mcp__Google_Ads__tool_generate_keyword_ideas (customer_id, seed_keywords or page_url) with the top converting terms. If nothing converted in the window, seed it with the 5–10 highest-spend relevant search terms from step 4 (or the top-spend keywords from step 2) and say the seeds are spend-based. If Semrush is connected, domain_paid_keywords (domain, database) on 1–3 competitors shows terms they buy that you don't; Ahrefs keywords_explorer (keywords, country) gives volume and CPC. Report only gaps with real intent; name the source of every volume.

8. Score and report

Search Campaign Audit — [Account Name]
Search Score: X/100  ·  30-day spend: [X]  ·  Conv: [X]  ·  CPA: [X]
(one line per engine, each in its own currency: "Google Ads: SEK 41,200 · Microsoft Ads: SEK 6,900")

| Campaign | Spend | Conv | CPA | Avg QS | IS lost (rank) |
|----------|-------|------|-----|--------|----------------|

🔴 Critical   — QS ≤4 on top spenders, zero-conversion keyword spend
🟡 Important  — broad-match bleed, single-RSA ad groups, missing negatives
🟢 Optimization — extensions, search-term harvesting

Top 3 Actions:
1. [Highest $ impact]
2. ...
3. ...

9. Record findings

json
{
  "title": "Quality Score 3 on 'crm software' — $620/mo at a 2.4x CPA penalty",
  "body": "'crm software' (phrase) has QS 3: ad relevance below average, landing page below average. It spent $620 last month at $94 CPA vs $39 account average. Tighten the ad group to one theme, mirror the keyword in RSA headlines, and point it at a dedicated landing page.",
  "action_type": "keyword_optimization",
  "expected_outcome": "QS to 6+, CPC down 20-35%, CPA toward account average",
  "estimated_impact_usd": 380,
  "priority": "high"
}

Cloud uses create_ticket with the same content, fields mapped per section 2 of the routing guide.

Action types: keyword_optimization, negative_keyword, match_type, creative_refresh, campaign_optimization, bidding_strategy.

Recording findings

Use create_ticket when the workspace has it (Cloud) and create_finding otherwise (Solo). The fields differ; see section 2 of ../marketing-engineer/references/mcp-routing.md.

Critical rules

  1. Read the QS components, don't just report the number. The fix differs for ad relevance vs landing page vs CTR.
  2. Zero-conversion search-term spend is the headline waste figure — total it.
  3. Quote real numbers: QS values, match-type splits, term costs.
  4. Read-only. Never add negatives or pause keywords — recommend, don't act. Write tools (Google_Ads tool_add_negative_keywords_to_campaign, Bing_Ads tool_add_negative_keywords) are used only if the user asks, and the skill asks first.
  5. One row per engine. Google and Microsoft numbers sit side by side, never summed across currencies; name the engine on every figure.

Frequently asked questions

What does the Search Campaign Audit AI skill do?

Search campaign audit for Google Ads (with Microsoft Ads parity) — match types, quality score, negative keyword gaps, search terms, ad strength, RSA asset coverage

Why use Search Campaign Audit on TypingMind?

Because you install it once and use it with any model. Search Campaign Audit is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Search Campaign Audit in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/cognyai/claude-code-marketing-skills/tree/main/skills/search-campaign-audit. TypingMind reads its SKILL.md and installs it as a skill you can enable per chat.

Which AI models can use Search Campaign Audit?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Search Campaign Audit?

As many as you like. As long as a model supports skills, you can use Search Campaign Audit with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Search Campaign Audit AI skill free?

It is published on GitHub by cognyai. Check the repository for licensing terms. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇