Break down Google Ads cost per conversion, campaign by campaign

Each Tuesday: account CPA for last week with an 8-week trend, every campaign's cost per conversion against its target, and which ones moved.

Runs
Tuesdays at 8:30
Posts
Stat tile

From your apps to your board

How the skill works: your agent reads cost and conversions per campaign from Google Ads; google ads customer id, target cpa from What you tell it, tuesdays at 8:30, and gives you cost per conversion and the report in the chat.

Reads

  • Google AdsCost and conversions per campaign
  • What you tell itGoogle Ads customer ID, Target CPA
reads
Your agentClaude Code, Codex, Cursor or GeminiTuesdays at 8:30
posts

You get

  • Cost per conversionA stat tile on your board, with its trend
  • The report in the chatEvery run, with or without Briefkin

What you get

  • Account CPA last week with an 8-week trend.
  • By campaign: cost, conversions, CPA, target and the change on the week before.
  • Over target: campaigns more than 20% above their target, with the spend at stake.
  • Noisy movers: big CPA swings on too few conversions to act on yet.

The card on the right is a sample. Yours shows your own numbers and updates in place every run.

What lands on your board

AcmeCost per conversion$42.8-3% vs previous period, bettervs last report
Cost$7,704
Conversions180
Over target2 campaigns
An example with made-up numbers. Your agent posts your real ones.

What it does

Account CPA is an average that hides the story. A brand campaign at $12 and a competitor campaign at $123 can blend into a respectable $43, right up until the competitor campaign gets more budget. Bidding to a target CPA does not settle it either: targets drift, and a campaign stuck in learning can miss its target for weeks.

The agent breaks CPA out per campaign every Tuesday, sets each against its own target, and keeps the account figure as a trend line. Running on Tuesday gives the weekend's conversions an extra day to arrive.

How it works

  1. 1Pull cost and conversions per campaign

    Run GAQL on campaign with campaign.name, campaign.bidding_strategy_type, metrics.cost_micros and metrics.conversions, WHERE segments.date DURING LAST_WEEK_MON_SUN AND campaign.status = ENABLED.

  2. 2Read each campaign's target

    Select campaign.target_cpa.target_cpa_micros for Target CPA bidding and campaign.maximize_conversions.target_cpa_micros for Maximize conversions with a target. Campaigns without one are compared with the account target you give.

  3. 3

    3Compare with the week before

    Run the same query for the Monday to Sunday before. Flag campaigns more than 20% over target, and mark a CPA that rose 25% or more on fewer than 10 conversions as noise, not a trend.

  4. 4

    4Post the tile

    Account CPA is total cost over total conversions, never an average of campaign CPAs. It is the headline with 8 weeks of history, and the card opens to the campaign table.

  5. 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.

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-cpa-by-campaign/install | sh
  2. Run it

    Ask Claude Code:

    promptRun the google-ads-cpa-by-campaign skill

    Or paste the prompt instead of installing:

    promptUsing the Google Ads API, give me cost per conversion by campaign for last week (GAQL on campaign, segments.date DURING LAST_WEEK_MON_SUN, enabled campaigns): campaign.name, metrics.cost_micros, metrics.conversions and the CPA. Read each campaign's target from campaign.target_cpa.target_cpa_micros or campaign.maximize_conversions.target_cpa_micros; where there is none, use $[target]. Compare with the week before. Show the account CPA as total cost over total conversions, flag campaigns more than 20% over target, and mark big swings on fewer than 10 conversions as noise.
  3. Put it on a schedule

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

    crontab30 8 * * 2 claude -p "Run the google-ads-cpa-by-campaign 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 stat tile. How setup works

View the SKILL.mdRaw file
SKILL.md---
name: google-ads-cpa-by-campaign
description: "Each Tuesday: account CPA for last week with an 8-week trend, every campaign's cost per conversion against its target, and which ones moved. Use when the user asks for this, or when it runs on a schedule."
---

# Google Ads CPA by campaign

Each Tuesday: account CPA for last week with an 8-week trend, every campaign's cost per conversion against its target, and which ones moved.

Account CPA is an average that hides the story. A brand campaign at $12 and a competitor campaign at $123 can blend into a respectable $43, right up until the competitor campaign gets more budget. Bidding to a target CPA does not settle it either: targets drift, and a campaign stuck in learning can miss its target for weeks.

The agent breaks CPA out per campaign every Tuesday, sets each against its own target, and keeps the account figure as a trend line. Running on Tuesday gives the weekend's conversions an extra day to arrive.

## 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 (customer ID) and, through a manager account, its ID as `login-customer-id`.
- Google Ads API credentials (a Google Cloud project with API access and OAuth; developer tokens were retired in September 2026), or the official Google Ads MCP server, which is read-only.
- Your account target CPA, for campaigns that bid without one.

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

### Steps

1. **Pull cost and conversions per campaign** (Google Ads): Run GAQL on `campaign` with `campaign.name`, `campaign.bidding_strategy_type`, `metrics.cost_micros` and `metrics.conversions`, `WHERE segments.date DURING LAST_WEEK_MON_SUN AND campaign.status = ENABLED`.
2. **Read each campaign's target** (Google Ads): Select `campaign.target_cpa.target_cpa_micros` for Target CPA bidding and `campaign.maximize_conversions.target_cpa_micros` for Maximize conversions with a target. Campaigns without one are compared with the account target you give.
3. **Compare with the week before**: Run the same query for the Monday to Sunday before. Flag campaigns more than 20% over target, and mark a CPA that rose 25% or more on fewer than 10 conversions as noise, not a trend.
4. **Post the tile**: Account CPA is total cost over total conversions, never an average of campaign CPAs. It is the headline with 8 weeks of history, and the card opens to the campaign table.

## Route B: the browser

### What you need

- Signed in to Google Ads with at least Read-only access to the account.

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

### Steps

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

1. **Set last week with a comparison**: Open `https://ads.google.com/aw/campaigns`, pick the client account if asked, and in the date picker choose `Last week (Mon - Sun)` with `Compare` on and `Previous period` selected.
2. **Show the right columns**: Under Columns, Modify columns, add `Cost`, `Conversions`, `Cost / conv.`, `Bid strategy type` and `Target CPA`. Filter the table to Campaign status: Enabled.
3. **Read the account and each campaign**: Take account CPA from the Total row (cost over conversions, never an average of campaign CPAs). For each campaign note cost, conversions, Cost / conv., its Target CPA, and the change shown in the comparison row; campaigns without a target use your account target.
4. **Flag and sort the noise**: Flag campaigns more than 20% over their target. A CPA that rose 25% or more on fewer than 10 conversions is noise, not a trend.

Note: Conversions keep arriving for a few days after the click, so reading on Tuesday rather than Monday gives steadier numbers. The Cost / conv. column counts primary conversion actions only, the same as the API.

## The report

- **Account CPA** last week with an 8-week trend.
- **By campaign**: cost, conversions, CPA, target and the change on the week before.
- **Over target**: campaigns more than 20% above their target, with the spend at stake.
- **Noisy movers**: big CPA swings on too few conversions to act on yet.

## 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-cpa-by-campaign/<date of this run, e.g. 2026-10-16>",
  "item_key": "google-ads-cpa-by-campaign",
  "project": "Acme",
  "source": "Google Ads CPA by campaign skill",
  "kind": "metric",
  "title": "Account CPA down to $42.80",
  "metric": {
    "label": "Cost per conversion",
    "value": 42.8,
    "prefix": "$",
    "better": "lower",
    "period_start": "<start of the period, ISO 8601>",
    "period_end": "<end of the period, ISO 8601>"
  },
  "glance": {
    "type": "Grid",
    "props": {
      "columns": 3
    },
    "children": [
      {
        "type": "Stat",
        "props": {
          "label": "Cost",
          "value": 7704,
          "prefix": "$"
        }
      },
      {
        "type": "Stat",
        "props": {
          "label": "Conversions",
          "value": 180
        }
      },
      {
        "type": "Stat",
        "props": {
          "label": "Over target",
          "value": "2 campaigns",
          "tone": "warn"
        }
      }
    ]
  },
  "widget": [
    {
      "type": "Table",
      "props": {
        "columns": [
          "Campaign",
          "Cost",
          "Conv.",
          "CPA",
          "Target",
          "Change"
        ],
        "align": [
          "left",
          "right",
          "right",
          "right",
          "right",
          "right"
        ],
        "rows": [
          [
            "Brand Search",
            "$920",
            74,
            "$12.43",
            "$15",
            "-6%"
          ],
          [
            "Non-brand Search",
            "$3,980",
            62,
            "$64.19",
            "$50",
            "+12%"
          ],
          [
            "Performance Max",
            "$2,064",
            38,
            "$54.32",
            "$55",
            "-9%"
          ],
          [
            "Competitors",
            "$740",
            6,
            "$123.33",
            "$90",
            "+31%"
          ],
          [
            "Account",
            "$7,704",
            180,
            "$42.80",
            "$45",
            "-3%"
          ]
        ]
      }
    },
    {
      "type": "Callout",
      "props": {
        "text": "Non-brand Search is 28% over its $50 target on 62 conversions, a real trend. Competitors is further over, but on 6 conversions it is still noise.",
        "tone": "warn"
      }
    }
  ],
  "size": "md"
}
JSON
```

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

Set `period_start` and `period_end` to the window the number covers: the tile names it from them ("Last 7 days", "Today", "This month"). For a count right now (open items, a balance), send `"period_label": ""` so no period shows; for another name, send it as `period_label` (at most 24 characters).

## Run it on a schedule

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

```bash
30 8 * * 2 claude -p "Run the google-ads-cpa-by-campaign 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-cpa-by-campaign#schedule

Source: https://briefkin.dev/skills/google-ads-cpa-by-campaign

What your agent needs

  • Read access to the Google Ads account (customer ID) and, through a manager account, its ID as login-customer-id.

  • Google Ads API credentials (a Google Cloud project with API access and OAuth; developer tokens were retired in September 2026), or the official Google Ads MCP server, which is read-only.

  • Your account target CPA, for campaigns that bid without one.

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 do conversions have decimals?

With data-driven attribution, Google Ads splits the credit for one conversion across several clicks, so a campaign can show 61.4 conversions. The skill keeps the decimals in the math and rounds only for display.

Should I change targets after one bad week?

Rarely. Smart bidding needs a few weeks after any change, and a week with fewer than 10 conversions is mostly noise. The 8-week line on the tile is there so targets follow a direction, not a blip.

Do I need Briefkin to use the Google Ads CPA by campaign 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 stat tile, 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/campaigns 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.

Watch cost per conversion on your board, run after run

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.