4 pages rank well and get skipped
Four pages earn under half the CTR their position usually gets on this site. A better title on /templates/invoice-pdf alone is worth about 1,260 clicks every 28 days.
Twice a month: pages with many impressions and a CTR well below what their position earns on your site, each with a new title drafted from its top queries.
Reads
You get
The card on the right is a sample. Yours shows your own numbers and updates in place every run.
What lands on your board
Four pages earn under half the CTR their position usually gets on this site. A better title on /templates/invoice-pdf alone is worth about 1,260 clicks every 28 days.
A page ranking third with a 3% click-through rate is telling you something: people see it and choose another result. Usually the title answers a different question than the one searched, or a year in it has gone stale. Nothing has to rank higher to fix it, which makes it one of the faster wins in search.
The agent compares every page's CTR with what pages at the same position get on your own site, flags the ones far below, and drafts a new title and description from the queries the page actually ranks for. You review the wording; the agent does the digging.
Call searchanalytics.query with dimensions: ["page"] for the 28 days ending 2 days ago, type: "web" and rowLimit: 5000, keeping clicks, impressions, CTR and position.
Bucket pages by rounded position (1-2, 3-4, 5-7, 8-10) and take the median CTR of each bucket. Published CTR curves vary too much; your site's own numbers, with your brand and your result types, are the fair benchmark.
Keep pages with at least 2,000 impressions and a CTR below half their bucket's median. Skip pages whose top query is branded.
For each flagged page, query dimensions: ["query"] with a dimensionFilterGroups filter page equals <url>, then fetch the live page for its current <title> and meta description.
Write a title under 60 characters and a description under 155 that lead with the top query's wording and say what the page offers (a free template, a calculator, a list). Show old and new side by side.
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.
Pick your agent, where the data comes from and how to schedule it; the commands below follow.
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-search-console-low-ctr-pages/install | shAsk Claude Code:
promptRun the google-search-console-low-ctr-pages skillOr paste the prompt instead of installing:
promptFrom Google Search Console (searchanalytics.query, dimension page, last 28 days ending 2 days ago), find pages with at least 2,000 impressions whose CTR is under half the median CTR of my pages at the same position (buckets 1-2, 3-4, 5-7, 8-10). Skip pages whose top query is my brand. For each flagged page, pull its top 10 queries, fetch its current title tag and meta description, and propose a new title (under 60 characters) and description (under 155) that use the top query's wording and say what the page offers. Show impressions, position, CTR, the usual CTR for that position and the clicks a fix could add, biggest first.1st and 15th at 9:00, from cron on any machine where Claude Code is signed in:
crontab0 9 1,15 * * claude -p "Run the google-search-console-low-ctr-pages 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.
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
SKILL.md---
name: google-search-console-low-ctr-pages
description: "Twice a month: pages with many impressions and a CTR well below what their position earns on your site, each with a new title drafted from its top queries. Use when the user asks for this, or when it runs on a schedule."
---
# Search Console low CTR pages
Twice a month: pages with many impressions and a CTR well below what their position earns on your site, each with a new title drafted from its top queries.
A page ranking third with a 3% click-through rate is telling you something: people see it and choose another result. Usually the title answers a different question than the one searched, or a year in it has gone stale. Nothing has to rank higher to fix it, which makes it one of the faster wins in search.
The agent compares every page's CTR with what pages at the same position get on your own site, flags the ones far below, and drafts a new title and description from the queries the page actually ranks for. You review the wording; the agent does the digging.
## 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 Search Console property; the agent never changes the site itself.
- Search Console API credentials (`webmasters.readonly`) or a Search Console MCP server or connector, plus plain web access to fetch each page's current title.
If something is missing, say what and stop; do not invent numbers.
### Steps
1. **Pull pages with their position** (Google Search Console): Call `searchanalytics.query` with `dimensions: ["page"]` for the 28 days ending 2 days ago, `type: "web"` and `rowLimit: 5000`, keeping clicks, impressions, CTR and position.
2. **Build your own CTR curve**: Bucket pages by rounded position (1-2, 3-4, 5-7, 8-10) and take the median CTR of each bucket. Published CTR curves vary too much; your site's own numbers, with your brand and your result types, are the fair benchmark.
3. **Flag the underperformers**: Keep pages with at least 2,000 impressions and a CTR below half their bucket's median. Skip pages whose top query is branded.
4. **Read what each page ranks for** (Google Search Console): For each flagged page, query `dimensions: ["query"]` with a `dimensionFilterGroups` filter `page equals <url>`, then fetch the live page for its current `<title>` and meta description.
5. **Draft new titles**: Write a title under 60 characters and a description under 155 that lead with the top query's wording and say what the page offers (a free template, a calculator, a list). Show old and new side by side.
## Route B: the browser
### What you need
- Signed in to Google with at least Restricted access to the Search Console property, and the same Google account able to create a Google Sheet for the export.
If something is missing, say what and stop; do not invent numbers.
### Steps
Start at https://search.google.com/search-console/performance/search-analytics. The user is signed in already; never type passwords or codes, and ask if a sign-in page appears.
1. **Open the Pages tab for 28 days**: Pick the property, open Performance > Search results, set the Date filter to `Last 28 days` and Search type `Web`, turn on all four metrics and switch to the Pages tab.
2. **Export the pages with their position**: Use the filter icon above the table to keep `Impressions` greater than `2000`, then Export > Google Sheets. In the sheet, bucket pages by rounded position (1-2, 3-4, 5-7, 8-10) and take the median CTR of each bucket.
3. **Flag the underperformers**: Keep pages whose CTR is below half their bucket's median. Back in the report, click each flagged page (this adds a Page filter) and open the Queries tab; skip pages whose top query is your brand.
4. **Read the current title**: Open each flagged page in a new tab and read its `<title>` and meta description from the page source or the browser tab.
5. **Draft replacements**: A title under 60 characters and a description under 155 that lead with the top query's wording and say what the page offers. Show old and new side by side, biggest clicks-at-stake first.
Note: The export carries the UI cap of 1,000 rows, which is plenty after the 2,000-impression filter on most sites. The median per bucket is a spreadsheet step; a browser agent that cannot use Sheets can estimate it from the sorted table instead.
## The report
- **Flagged pages**: impressions, position, CTR, and the CTR pages at that position usually get on your site.
- **Clicks left on the table** for each page if it reached the usual CTR.
- **Proposed title and description** per page, next to the current ones, built from its top queries.
- Which page to rewrite first.
## 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-search-console-low-ctr-pages/<date of this run, e.g. 2026-10-16>",
"item_key": "google-search-console-low-ctr-pages",
"project": "example.com",
"source": "Search Console low CTR pages skill",
"kind": "update",
"title": "4 pages rank well and get skipped",
"summary": "Four pages earn under half the CTR their position usually gets on this site. A better title on **/templates/invoice-pdf** alone is worth about 1,260 clicks every 28 days.",
"widget": [
{
"type": "Table",
"props": {
"columns": [
"Page",
"Impressions",
"Position",
"CTR",
"Usual CTR"
],
"align": [
"left",
"right",
"right",
"right",
"right"
],
"rows": [
[
"/templates/invoice-pdf",
21400,
3.4,
"3.9%",
"9.8%"
],
[
"/blog/net-30-payment-terms",
14900,
4.1,
"2.7%",
"9.8%"
],
[
"/guides/late-payment-fees",
9600,
6.3,
"1.4%",
"4.6%"
],
[
"/templates/estimate",
7300,
2.3,
"7.8%",
"21.5%"
]
]
}
},
{
"type": "Table",
"props": {
"columns": [
"Page",
"Current title",
"Proposed title"
],
"rows": [
[
"/templates/invoice-pdf",
"Invoice Templates | Example",
"Free Invoice PDF Template: Fill In, Download, Send"
],
[
"/blog/net-30-payment-terms",
"What You Need to Know About Payment Terms",
"Net 30 Payment Terms: What They Mean, With Examples"
]
]
}
}
]
}
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: 1st and 15th at 9:00 (`0 9 1,15 * *`). From cron, with the agent signed in on that machine:
```bash
0 9 1,15 * * claude -p "Run the google-search-console-low-ctr-pages 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-search-console-low-ctr-pages#schedule
Source: https://briefkin.dev/skills/google-search-console-low-ctr-pages
Read access to the Search Console property; the agent never changes the site itself.
Search Console API credentials (webmasters.readonly) or a Search Console MCP server or connector, plus plain web access to fetch each page's current title.
GOOGLE_APPLICATION_CREDENTIALS at the key and set GSC_SITE_URL to sc-domain:example.com or https://www.example.com/.webmasters.readonly scope and a stored refresh token works for scheduled runs.Ask for webmasters.readonly and add the service account as a Restricted user; the reports only read.
Then a new title helps less. The agent notes when a page's CTR fell over the last 90 days while its position held, the usual sign of something new above you on the results page, and moves those pages to the end of the list.
When Google recrawls the page, often within days for popular pages. Give it two runs of this report before judging; the agent compares the page's CTR with earlier runs. Google also rewrites titles it finds unhelpful, so what shows can differ from what you wrote.
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.
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.
Yes. Switch the data source to "Your browser" and the steps change to the Google Search Console pages themselves: the agent opens search.google.com/search-console/performance/search-analytics 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.
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.