Run a weekly Google Ads audit with your AI agent

Every Monday: last week's Google Ads spend and CPA, wasted spend on search terms, disapproved ads and campaigns held back by budget, in one card.

Runs
Mondays at 8:30
Posts
Report card

What lands on your board

AcmeGoogle Ads weekly audit · Claude Code

CPA up 9%, $1,140 spent on dead search terms

Last week: $12,480 spent (+6%), 296 conversions (-3%), CPA $42.16 (+9%). Four things to fix, starting with $1,140 on search terms that never converted.

Spend$12,480+6.0% vs previous period, worse
Conversions296-3.0% vs previous period, worse
CPA$42.16+9.0% vs previous period, worse
Wasted spend$1,140
CheckFindingWhere
Wasted spend$1,140 on 18 search terms with 0 conversionsNon-brand Search, Competitors
BudgetLost 31% of impression share to budgetNon-brand Search
Disapproved adDestination not workingNon-brand Search > Invoice app
Limited adTrademarks in ad textCompetitors > Alternatives

Fix first: add negatives for the 18 dead search terms, then move that money to Non-brand Search, which lost 31% of its impression share to budget.

Done
An example with made-up numbers. Your agent posts your real ones.

What it does

Most Google Ads accounts do not fail loudly. An ad is disapproved on Thursday and the ad group quietly serves its weaker variant, a campaign runs out of budget by mid-afternoon every day, a broad match keyword starts buying searches for jobs. Each problem surfaces in a different corner of the interface, if you go looking.

The agent goes looking every Monday. It runs the same checks through the Google Ads API, compares spend and CPA with the week before, and lists the problems in the order they cost money, so the account manager starts with the fix instead of the hunt.

How it works

  1. 1Pull last week against the week before

    Run GAQL on customer with metrics.cost_micros, metrics.conversions, metrics.conversions_value and metrics.clicks for segments.date DURING LAST_7_DAYS, then for the 7 days before with segments.date BETWEEN. Divide micros by 1,000,000; CPA is cost over conversions.

  2. 2Find budget-limited campaigns

    Query enabled campaign rows with campaign.primary_status_reasons, campaign_budget.amount_micros and metrics.search_budget_lost_impression_share. Flag those whose reasons include BUDGET_CONSTRAINED or that lost more than 10% of impression share to budget.

  3. 3List disapproved and limited ads

    Query ad_group_ad where ad_group_ad.status = ENABLED and ad_group_ad.policy_summary.approval_status IN (DISAPPROVED, APPROVED_LIMITED), with ad_group_ad.policy_summary.policy_topic_entries for the reason and the campaign and ad group names.

  4. 4Measure wasted spend

    Query search_term_view for LAST_7_DAYS with metrics.conversions = 0 and cost above your target CPA, ordered by metrics.cost_micros. Their total is the week's wasted spend; the top terms go in the card.

  5. 5

    5Rank the fixes by money at stake

    Order the findings by what they cost: wasted spend in dollars, budget-limited campaigns by the impression share they lost, disapprovals by last week's spend in their ad group. Say what to do first in one line.

  6. Post it to the board

    With BRIEFKIN_TOKEN set, the agent sends the result as one card that updates in place every run. Without it, you get the report in the chat.

What you get

  • Spend, conversions and CPA last week, with the change on the week before.
  • Wasted spend: search terms that cost more than your target CPA with no conversions, the total and the top 5.
  • Disapproved and limited ads, with the policy reason and the ad group.
  • Budget-limited campaigns and the impression share they lost to budget.
  • Fix first: the one change with the most money behind it.

Install and run it

Pick your agent, where the data comes from and how to schedule it; the commands below follow.

Agent
Data
Schedule
  1. Install the skill

    One command. It puts the SKILL.md in ~/.claude/skills and in ~/.agents/skills, which Codex, Cursor, Gemini CLI, Copilot CLI and OpenCode read too.

    shellcurl -fsSL https://briefkin.dev/skills/google-ads-weekly-audit/install | sh
  2. Run it

    Ask Claude Code:

    promptRun the google-ads-weekly-audit skill

    Or paste the prompt instead of installing:

    promptAudit my Google Ads account for last week using the Google Ads API (GAQL). 1) Spend, conversions and CPA for LAST_7_DAYS against the 7 days before, from metrics.cost_micros and metrics.conversions on customer. 2) Enabled campaigns whose campaign.primary_status_reasons include BUDGET_CONSTRAINED, or with metrics.search_budget_lost_impression_share above 10%. 3) Enabled ads in ad_group_ad with policy_summary.approval_status DISAPPROVED or APPROVED_LIMITED, with the policy topic. 4) Search terms from search_term_view with zero conversions and cost above my target CPA of $[target]. List the problems in order of money at stake and tell me the one fix to make first. Do not change anything.
  3. Put it on a schedule

    Mondays at 8:30, from cron on any machine where Claude Code is signed in:

    crontab30 8 * * 1 claude -p "Run the google-ads-weekly-audit skill"

    Add it with crontab -e. A laptop asleep at that time runs it when it wakes if you use launchd or Task Scheduler instead; GitHub Actions works for the API route.

  4. Send it to your board

    Connect Briefkin once with curl -fsSL https://briefkin.dev/install | sh and every run lands on your board as a report card. How setup works

View the SKILL.mdRaw file
SKILL.md---
name: google-ads-weekly-audit
description: "Every Monday: last week's Google Ads spend and CPA, wasted spend on search terms, disapproved ads and campaigns held back by budget, in one card. Use when the user asks for this, or when it runs on a schedule."
---

# Google Ads weekly audit

Every Monday: last week's Google Ads spend and CPA, wasted spend on search terms, disapproved ads and campaigns held back by budget, in one card.

Most Google Ads accounts do not fail loudly. An ad is disapproved on Thursday and the ad group quietly serves its weaker variant, a campaign runs out of budget by mid-afternoon every day, a broad match keyword starts buying searches for jobs. Each problem surfaces in a different corner of the interface, if you go looking.

The agent goes looking every Monday. It runs the same checks through the Google Ads API, compares spend and CPA with the week before, and lists the problems in the order they cost money, so the account manager starts with the fix instead of the hunt.

## Two ways to get the data

Use the API or MCP route when you have an MCP server, a CLI or an API token for the app. Otherwise, if you can drive a browser the user is signed in to (Claude in Chrome, a Playwright or Chrome DevTools MCP server), use the browser route. Say which one you used.

## Route A: API or MCP server

### What you need

- Read access to the Google Ads account, or to the manager (MCC) account above it, and the customer ID.
- Google Ads API access: Google's own read-only Google Ads MCP server (`googleads/google-ads-mcp`) or a connector; or OAuth credentials for a user on the account, a developer token where your access level still needs one, and `login-customer-id` for a manager account.
- Your target CPA, so the agent knows what counts as waste.

If something is missing, say what and stop; do not invent numbers.

### Steps

1. **Pull last week against the week before** (Google Ads): Run GAQL on `customer` with `metrics.cost_micros`, `metrics.conversions`, `metrics.conversions_value` and `metrics.clicks` for `segments.date DURING LAST_7_DAYS`, then for the 7 days before with `segments.date BETWEEN`. Divide micros by 1,000,000; CPA is cost over conversions.
2. **Find budget-limited campaigns** (Google Ads): Query enabled `campaign` rows with `campaign.primary_status_reasons`, `campaign_budget.amount_micros` and `metrics.search_budget_lost_impression_share`. Flag those whose reasons include `BUDGET_CONSTRAINED` or that lost more than 10% of impression share to budget.
3. **List disapproved and limited ads** (Google Ads): Query `ad_group_ad` where `ad_group_ad.status = ENABLED` and `ad_group_ad.policy_summary.approval_status IN (DISAPPROVED, APPROVED_LIMITED)`, with `ad_group_ad.policy_summary.policy_topic_entries` for the reason and the campaign and ad group names.
4. **Measure wasted spend** (Google Ads): Query `search_term_view` for `LAST_7_DAYS` with `metrics.conversions = 0` and cost above your target CPA, ordered by `metrics.cost_micros`. Their total is the week's wasted spend; the top terms go in the card.
5. **Rank the fixes by money at stake**: Order the findings by what they cost: wasted spend in dollars, budget-limited campaigns by the impression share they lost, disapprovals by last week's spend in their ad group. Say what to do first in one line.

## Route B: the browser

### What you need

- Signed in to Google Ads with at least Read-only access to the account, or to the manager account above it with the client account selected.

If something is missing, say what and stop; do not invent numbers.

### Steps

Start at https://ads.google.com/aw/overview. The user is signed in already; never type passwords or codes, and ask if a sign-in page appears.

1. **Pick the account and the week**: If you sign in through a manager account, choose the client account in the account picker. In the date range at the top right choose `Last week` (Monday to Sunday), switch on Compare with `Previous period` and Apply.
2. **Read spend, conversions and CPA**: Open Campaigns in the left menu and read the Totals row of the table: Cost, Conversions and Cost / conv. with the comparison values. Use Columns > Modify columns if any of the three is hidden.
3. **Find campaigns held back by budget**: On the same Campaigns page, look for `Limited by budget` in the Status column, and add `Search lost IS (budget)` from Columns > Modify columns > Competitive metrics. Flag campaigns above 10%.
4. **List disapproved and limited ads**: Open Campaigns > Ads, add the `Policy details` column from Attributes, and filter the Status column for `Disapproved` and `Eligible (limited)`. Tools > Troubleshooting > Policy manager > Policy issues shows the same list with the policy and the appeal option.
5. **Measure wasted spend**: Open Campaigns > Insights and reports > Search terms with the same week, add a filter `Conversions = 0` and `Cost >` your target CPA, and sort by Cost. The total is the week's waste; note the top 5 terms with their campaign.
6. **Rank the fixes by money at stake**: Wasted spend in dollars, budget-limited campaigns by lost impression share, disapprovals by last week's spend in their ad group. Say what to do first in one line and change nothing.

Note: Conversions keep arriving for days after the click, so Monday's CPA for last week reads higher than it will by Friday in the UI as well. The Search terms table hides terms below a privacy threshold, so the UI total can be a little lower than the API's. Policy manager and column settings are desktop-only pages.

## The report

- **Spend, conversions and CPA** last week, with the change on the week before.
- **Wasted spend**: search terms that cost more than your target CPA with no conversions, the total and the top 5.
- **Disapproved and limited ads**, with the policy reason and the ad group.
- **Budget-limited campaigns** and the impression share they lost to budget.
- **Fix first**: the one change with the most money behind it.

## Post it to Briefkin

When `BRIEFKIN_TOKEN` is set, or `~/.config/briefkin/env` exists (the installer from https://briefkin.dev/docs writes it), also send the result to the user's Briefkin board as one card. Keep the `item_key` the same on every run so the card updates in place, use a new `event_id` per run, and put today's real numbers in place of the example ones. If the request fails, say so once and carry on.

```bash
[ -n "${BRIEFKIN_TOKEN:-}" ] || . ~/.config/briefkin/env
curl -sS -X POST "${BRIEFKIN_URL:-https://briefkin.dev}/v1/events" \
  -H "authorization: Bearer $BRIEFKIN_TOKEN" \
  -H "content-type: application/json" \
  -d @- <<'JSON'
{
  "v": 1,
  "event_id": "google-ads-weekly-audit/<date of this run, e.g. 2026-10-16>",
  "item_key": "google-ads-weekly-audit",
  "project": "Acme",
  "source": "Google Ads weekly audit skill",
  "kind": "update",
  "title": "CPA up 9%, $1,140 spent on dead search terms",
  "summary": "Last week: **$12,480** spent (+6%), 296 conversions (-3%), CPA **$42.16** (+9%). Four things to fix, starting with $1,140 on search terms that never converted.",
  "widget": [
    {
      "type": "Grid",
      "props": {
        "columns": 4
      },
      "children": [
        {
          "type": "Stat",
          "props": {
            "label": "Spend",
            "value": 12480,
            "prefix": "$",
            "delta": 6
          }
        },
        {
          "type": "Stat",
          "props": {
            "label": "Conversions",
            "value": 296,
            "delta": -3
          }
        },
        {
          "type": "Stat",
          "props": {
            "label": "CPA",
            "value": 42.16,
            "prefix": "$",
            "delta": 9,
            "better": "lower"
          }
        },
        {
          "type": "Stat",
          "props": {
            "label": "Wasted spend",
            "value": 1140,
            "prefix": "$",
            "tone": "bad"
          }
        }
      ]
    },
    {
      "type": "Table",
      "props": {
        "columns": [
          "Check",
          "Finding",
          "Where"
        ],
        "rows": [
          [
            "Wasted spend",
            "$1,140 on 18 search terms with 0 conversions",
            "Non-brand Search, Competitors"
          ],
          [
            "Budget",
            "Lost 31% of impression share to budget",
            "Non-brand Search"
          ],
          [
            "Disapproved ad",
            "Destination not working",
            "Non-brand Search > Invoice app"
          ],
          [
            "Limited ad",
            "Trademarks in ad text",
            "Competitors > Alternatives"
          ]
        ]
      }
    },
    {
      "type": "Callout",
      "props": {
        "text": "Fix first: add negatives for the 18 dead search terms, then move that money to Non-brand Search, which lost 31% of its impression share to budget.",
        "tone": "warn"
      }
    }
  ]
}
JSON
```

Widgets come from the Briefkin catalog (https://briefkin.dev/v1/catalog). Without a token, just give the user the report.

## Run it on a schedule

Suggested: Mondays at 8:30 (`30 8 * * 1`). From cron, with the agent signed in on that machine:

```bash
30 8 * * 1 claude -p "Run the google-ads-weekly-audit skill"
# Codex: codex exec, Gemini CLI: gemini -p, Copilot CLI: copilot -p, OpenCode: opencode run
```

Or use the agent's own scheduler (Claude Code routines, Codex automations, Claude or ChatGPT scheduled tasks): https://briefkin.dev/skills/google-ads-weekly-audit#schedule

Source: https://briefkin.dev/skills/google-ads-weekly-audit

What your agent needs

  • Read access to the Google Ads account, or to the manager (MCC) account above it, and the customer ID.

  • Google Ads API access: Google's own read-only Google Ads MCP server (googleads/google-ads-mcp) or a connector; or OAuth credentials for a user on the account, a developer token where your access level still needs one, and login-customer-id for a manager account.

  • Your target CPA, so the agent knows what counts as waste.

Connecting Google Ads

  1. Google Ads MCP server, Google's open-source, read-only server (google-ads-mcp, source). Run it locally with pipx run --spec git+https://github.com/googleads/google-ads-mcp.git google-ads-mcp, or deploy it to Cloud Run for remote clients. Its tools are list_accessible_customers, search for GAQL and get_resource_metadata (docs).
  2. Google Ads API with OAuth. Enable the Google Ads API in a Google Cloud project, sign in with Application Default Credentials (gcloud auth application-default login with the adwords scope) or an OAuth client, and set GOOGLE_PROJECT_ID. Add GOOGLE_ADS_LOGIN_CUSTOMER_ID when you reach client accounts through a manager account, and write customer IDs without dashes.

The adwords scope can't be narrowed, so sign in as a Google Ads user with Read only access; the MCP server has no write tools either way.

Questions

Why does last week's CPA look worse on Monday than it does by Friday?

Google Ads credits each conversion to the day of the click, and conversions keep arriving for days afterwards, more so with long sales cycles. A Monday audit sees the latest days before their conversions are in. Compare week with week on the same weekday, or move the audit to Wednesday if your conversion lag is long.

Does the audit change anything in the account?

No, every query is read-only. When you want the agent to act on a finding, such as adding the negatives, use the wasted search terms skill, which asks before it writes anything.

Do I need Briefkin to use the Google Ads weekly audit skill?

No. The skill runs with your own agent and gives you the report in the chat. With a Briefkin token set, each run also posts it to your board as a report card, so you can glance at it instead of asking again.

Which agents can run it?

Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot CLI, OpenCode, Windsurf and Amp all read SKILL.md files. The install command puts it in ~/.claude/skills and ~/.agents/skills, where they look. ChatGPT, the Claude app and Grok take the prompt instead. Pick your agent under "Install and run it" and the commands change to match.

Can it work without an API or MCP server, from my browser?

Yes. Switch the data source to "Your browser" and the steps change to the Google Ads pages themselves: the agent opens ads.google.com/aw/overview in a browser you are signed in to and reads the numbers off the page. Claude Code, Codex, Cursor, Claude app and ChatGPT can drive a browser out of the box; Gemini CLI, Copilot CLI and OpenCode need a Playwright or Chrome DevTools MCP server first. It is slower than the API and needs you signed in, so it suits a report you run by hand or from a scheduler on your own computer.

Read this report on your board, not in a chat log

Skills are free to use with any agent. Briefkin is where their results land: one board for every report, number and question your agents send, with a ping when something needs you.

Free during the open beta. No card.