AI Keyword Clustering: Turn Search Queries into Content Series and Internal Links

"Google Search Central: Creating helpful, reliable, people-first content"
You have a keywords.csv file with 300 queries spanning Astro, Docker, and Cloudflare. The model produces 42 clusters, but a manual review finds astro seo and astro vs next.js for seo in cluster #1. The first query calls for a tutorial; the second calls for a comparison. One page is unlikely to satisfy both jobs.
That is the practical failure mode in keyword clustering: a model can merge phrases that are semantically close but carry different intent. The result increases the risk of content cannibalization, with several near-duplicate pages competing for the same demand. A list of “keyword groups” also does not tell you which groups need a new article, an update to an old post, an FAQ, or an internal link.
The workflow below starts by combining GSC queries, topic cards, and your content inventory. It normalizes and deduplicates the rows, keeps human review after the model’s first pass, and maps every cluster to an action: new article, old-post refresh, FAQ, internal link, or discard. The deliverables are the input schema, normalization logic, method comparison, AI prompt, cluster map, action framework, and internal-link structure.
1. Start the Keyword-Clustering Workflow with the Right Inputs
The input is not a list of phrases invented by AI. It is your own query/page data, candidate-topic cards, and content inventory. Together, these sources show which topics are worth covering, which already have a page, and which reflect real community questions.
Use this source checklist:
| Source | Fields | How to get it | What to watch |
|---|---|---|---|
| GSC query/page data | query, page, clicks, impressions, CTR, position | Search Console UI / Search Analytics API | API rowLimit defaults to 1,000 and allows up to 25,000 per request; internal limits mean it returns top rows rather than a guaranteed complete set |
| Topic cards | title, intent, source (Reddit/GitHub/competitor), evidence | Topic-research agent output | Review source quality manually |
| Content inventory | article title, URL, series, publication date, traffic, ranking queries | Site database / CMS | Mark matching old posts and series ownership |
| quickWins metrics | high-impression low-CTR queries, refresh opportunities | GSC quickWins analysis | Use them to support the refresh decision |
Steps:
- Export GSC query/page data. The Performance report defaults to the last three months, but you can choose a range that fits the task.
- Merge the topic cards produced by the research agent.
- Extract the content inventory: existing articles, series, and performance.
- Flag quick wins: high-impression low-CTR queries and old-post refresh opportunities.
- Produce a combined table where every row includes the query, initial intent, source, matching old post, and quick-win flag.
2. Normalize Keywords Before You Cluster Them
The merged table will contain duplicates, close variants, and spelling differences. If you skip normalization, the model can treat them as separate keywords and split one intent into several clusters.
Deduplication checklist:
- Exact duplicate:
astro seovsastro seo→ keep one row - Spelling or configuration variants:
docker host.docker.internalvsdocker host-gatewayvsdocker 127.0.0.1→ mark as a same-intent candidate and wait for human confirmation - Synonyms:
cloudflare pricingvscloudflare 价格vscloudflare cost→ mark as one intent - Entity inflection:
astro blogvsastro blogs→ mark as one intent
Normalization rules:
- Standardize terms:
nextjs→next.js,cloudflare workers→Cloudflare Workers - Standardize casing:
Astrovsastro→astro - Standardize punctuation:
astro, seovsastro seo→astro seo
Initial intent labels:
- Informational: how to, what is, guide, tutorial
- Navigational: brand name, product name, specific URL
- Comparative: vs, compare, alternative
- Transactional: pricing, cost, buy, download
Steps:
- Remove exact duplicates from the combined table.
- Detect spelling variants and synonyms, and flag them as same-intent candidates.
- Normalize terminology, casing, and punctuation.
- Give every query an initial intent label: informational, navigational, comparative, or transactional.
- Output a normalized table with query, normalized_query, intent_tag, source, and old_post_match.
3. Compare Three Keyword-Clustering Methods
The method needs to balance cost, accuracy, and risk. The three common approaches fit different stages of a content operation.
| Method | Cost | Accuracy | Best fit | Risk |
|---|---|---|---|---|
| SERP-based | High: many SERP queries and possible anti-bot limits | High: uses actual results and helps evaluate whether pages should be combined | Live content, page-overlap decisions, and a SERP API budget | Expensive; changing SERPs reduce stability |
| Semantic | Low: only embedding API calls | Medium: can miss real search intent and merge different jobs | Early topic maps, limited budgets, and a fast first pass | May merge phrases that are related in meaning but different in intent |
| Hybrid | Medium: semantic first pass + SERP/GSC validation + human review | High: balances cost and accuracy | Ongoing content operations with GSC data and review time | Requires human-review capacity |
Recommended workflow:
- Small team / early site: semantic first pass + GSC/page validation + human review of risky clusters
- Larger team / established content: hybrid clustering with semantic grouping, SERP/GSC validation, and human review
- Sufficient budget + page-overlap decision: SERP-based clustering + human review
I use the hybrid option: semantic similarity creates the first grouping, GSC page data shows which queries already have a landing page, and a reviewer resolves intent conflicts.
4. Run AI-Assisted Clustering with a Defined Output
When you send the normalized keywords to a model, the prompt needs explicit fields and intent rules. This is the prompt structure I use.
AI prompt example:
You are an SEO content planner. Group the normalized keywords below into clusters by search intent. For each cluster, provide:
1. primary_intent
2. canonical_page (recommended target type: new article / old post refresh / FAQ / discard)
3. suggested_action
4. internal_link_targets (existing articles or series that should receive links)
5. risk_notes (possible cannibalization or required human review)
Keywords:
- astro seo
- astro vs next js for seo
- cloudflare pricing
- docker host.docker.internal
- docker host-gateway
...
Return a JSON array. Each cluster must follow:
{
"cluster_id": 1,
"keywords": ["astro seo", ...],
"primary_intent": "informational - tutorial",
"canonical_page": "new article",
"suggested_action": "Create an Astro SEO tutorial",
"internal_link_targets": ["astro-guide series", "cloudflare-bindui series"],
"risk_notes": "Human review required: astro vs next js for seo has comparison intent and should not be merged"
}
Example cluster map:
| cluster_id | keywords | primary_intent | canonical_page | suggested_action | internal_link_targets | risk_notes |
|---|---|---|---|---|---|---|
| 1 | astro seo, astro blog seo | Informational - tutorial | new article | Create an Astro SEO tutorial | astro-guide series, cloudflare-bindui series | None |
| 2 | astro vs next js for seo | Comparative | new article | Create an Astro vs Next.js SEO comparison | astro-guide series, nextjs-blog-series | None |
| 3 | cloudflare pricing, cloudflare cost, cloudflare 价格 | Transactional | old post refresh | Refresh the existing Cloudflare pricing article | cloudflare-bindui series | Existing /cloudflare-pricing post; refresh it |
| 4 | docker host.docker.internal, docker host-gateway, docker 127.0.0.1 | Informational - configuration | FAQ | Add an FAQ to a Docker-series article | docker-practice series | Same-intent variants; merge into an FAQ |
Human-review checklist:
- Mixed intent: a cluster contains both tutorial and comparison queries → split it
- Existing page conflict: a cluster maps to “new article,” but a related page already exists → change the action to old-post refresh or FAQ
- Over-splitting: one intent appears in several clusters → merge them
- Over-merging: the model puts different intents in one cluster → split it
Steps:
- Send the normalized table to the AI clustering tool: Claude, ChatGPT, or an internal model workflow.
- Run the prompt above for the first pass.
- Export the cluster map.
- Review intent conflicts, old-post matches, over-splitting, and over-merging.
- Correct the map and flag high-risk clusters.
5. Map Every Cluster to One Content Action
A finished cluster needs an operational action, not another list of keywords. This is the decision table I use.
| Decision factor | New article | Pillar page | Old-post refresh | FAQ | Internal-link update | Discard |
|---|---|---|---|---|---|---|
| Query count | Many (5+) | Many (5+) | Few (1–3) | Few (1–3) | Few (1–3) | One |
| Intent variation | High | High | Low | Low | Low | Low |
| Matching old post | None | None | Yes | Yes | Yes | Yes |
| Competition | High | High | Medium | Medium | Low | Low |
| Topical fit | High | High | High | Medium | Medium | Low |
| Room for original value | High | High | Low | Low | Low | None |
| Example | astro seo tutorial | astro series | cloudflare pricing | docker host.docker.internal | links between related posts | brand-name variant |
Action definitions:
- New article: create one page that covers every same-intent query in the cluster.
- Pillar page: create a series hub that links to the other pages in the cluster.
- Old-post refresh: extend an existing page to cover the new queries.
- FAQ: add a focused answer to an existing article.
- Internal-link update: add contextual links that point to the canonical page.
- Discard: do not create content; mark the query as low value or outside the target audience.
Steps:
- Score each cluster against the decision factors.
- Map the score to an action.
- Export an action list in the form cluster_id → suggested_action.
- Manually review high-risk actions: new articles and pillar pages.
The query counts in the table are example signals, not hard thresholds. The final action still depends on intent, existing inventory, original value, and maintenance cost.
6. Design Internal Links as a Cluster Navigation Map
Relationships between clusters should become executable internal links, not a generic “related reading” block. This is the link-planning structure I use.
Internal-link principles:
- Bidirectional pillar/cluster links: the pillar links to every cluster page, and every cluster page links back to the pillar.
- Descriptive anchor text: describe the target intent instead of writing “click here.”
- Canonical targets: do not link to duplicate or near-duplicate pages; point links to the preferred canonical page.
- No orphaned articles: plan at least one contextual inbound link for every article, then add more routes when the site structure and reader journey support them.
internal_link_targetsin the cluster map: record which existing articles or series should receive a link.
Internal-link plan:
| cluster_id | pillar_page | cluster_pages | internal_link_targets | Suggested anchor text |
|---|---|---|---|---|
| 1 | Complete Astro SEO guide | astro seo tutorial, astro blog seo | astro-guide series, cloudflare-bindui series | “Astro SEO optimization,” “Cloudflare performance optimization” |
| 2 | - | astro vs next.js for seo | astro-guide series, nextjs-blog-series | “Astro vs Next.js comparison” |
| 3 | - | cloudflare pricing | cloudflare-bindui series | “Cloudflare pricing explained” |
Steps:
- Identify the pillar and cluster pages.
- Design the bidirectional link structure.
- Choose descriptive anchor text for every link.
- Check for orphaned articles.
- Export the internal-link plan to the content task table.
Google currently treats redirects and rel="canonical" as strong canonicalization signals and sitemap inclusion as a weaker signal. The signals can reinforce one another, but Google still chooses the canonical. Internal links should consistently point to the URL you prefer as canonical.
7. Set Quality Gates Before Clustering Becomes a Page Generator
Keyword clustering should not become a bulk page generator. That pattern can quickly diverge from people-first content principles.
Risk boundaries:
- Do not create one page per keyword variant. Queries with the same intent should converge on one page or canonical target.
- Require an original-value decision. Every cluster must map back to a reader problem, the site’s focus, and a meaningful addition rather than a rewrite of existing material.
- Consolidate same-intent queries around a canonical. For duplicate or very similar pages, redirects,
rel="canonical", and sitemaps can express a preference, but the signals have different strengths and do not guarantee Google’s selection. - Avoid search-engine-first content. The main purpose must be helping readers rather than manipulating rankings.
- Reduce cannibalization risk. Publishing many pages for nearly identical keywords can cause several URLs to compete for the same intent.
- Flag risky clusters. Use the cluster map’s risk_notes field to require human review.
Quality red lines:
- When a matching old post exists, prefer a refresh or FAQ over another near-duplicate page.
- Every clustering output must include topical fit and original-value judgments.
- Internal links should point to the canonical article rather than duplicate or near-duplicate pages.
8. Walk Through Astro SEO, Cloudflare Pricing, and Docker Examples
Three scenarios make the workflow concrete.
Example 1: astro seo vs astro vs next.js for seo (different intent, so split them)
- Raw queries:
astro seo,astro vs next.js for seo,astro blog seo - Normalized:
astro seo,astro vs next js for seo,astro blog seo - AI first pass: cluster_id=1, keywords=[astro seo, astro vs next js for seo, astro blog seo]
- Human review:
astro seohas tutorial intent;astro vs next js for seohas comparison intent, so they must be separated. - Final cluster map:
- cluster_id=1: [astro seo, astro blog seo] → create an Astro SEO tutorial
- cluster_id=2: [astro vs next js for seo] → create an Astro vs Next.js SEO comparison
Example 2: cloudflare pricing (an old page exists, so refresh it)
- Raw queries:
cloudflare pricing,cloudflare cost,cloudflare 价格 - Normalized:
cloudflare pricing,cloudflare pricing,cloudflare pricing - AI first pass: cluster_id=3, keywords=[cloudflare pricing, cloudflare pricing, cloudflare pricing]
- Inventory match: an existing
/cloudflare-pricingarticle - Final cluster map:
- cluster_id=3: [cloudflare pricing, cloudflare cost, cloudflare 价格] → refresh
/cloudflare-pricing
- cluster_id=3: [cloudflare pricing, cloudflare cost, cloudflare 价格] → refresh
Example 3: docker host.docker.internal (normalize related technical queries)
- Raw queries:
docker host.docker.internal,docker host-gateway,docker 127.0.0.1 - Normalized: flag them as a candidate group for the same configuration intent.
- AI first pass: cluster_id=4, keywords=[docker host.docker.internal, docker host-gateway, docker 127.0.0.1]
- Inventory match: an existing Docker series
- Final cluster map:
- cluster_id=4: [docker host.docker.internal, docker host-gateway, docker 127.0.0.1] → after human intent validation, add an FAQ or section to a Docker-series article
9. Hand the Content Map to the Next Workflow
Once clustering is complete, move the cluster map into the content task table and define its fields, scoring, state machine, and publishing schedule. The next article in the series, “Designing a Content Task Table: Fields, Scores, States, and Publishing Plans,” covers that step.
For turning a cluster map into a usable outline, continue with “AI Outline Generation: From Cluster Map to Draftable Outline.” The later article “Automated Internal Linking: Batch Link Generation and Common Traps” covers link insertion in detail.
Old-post refresh logic has its own guide: “GSC Content Refresh Strategy: From Quick Wins to Updates.”
Related articles already published:
- Quarterly Content Planning for Technical Blogs with the Pillar-Cluster Model
- GA/GSC Weekly Operations SOP: From Data to Action
- Advanced GSC Techniques for Finding Content Opportunities
Turn Keywords into an Actionable Content Map
A minimal workflow from multi-source inputs to a cluster map, content actions, and an internal-link plan.
⏱️ Estimated time: 60 min
- 1
Step 1: Combine the three input sources
Put GSC query/page data, topic cards, and your existing article inventory in one table. Keep the source, matching post, and quick-win flag for each row. - 2
Step 2: Normalize the keywords
Resolve duplicates, spelling variants, synonyms, casing, and punctuation, then label each query as informational, navigational, comparative, or transactional. - 3
Step 3: Run the semantic first pass
Ask the model to group queries by intent and return primary_intent, canonical_page, suggested_action, internal_link_targets, and risk_notes for every cluster. - 4
Step 4: Validate high-risk clusters
Use GSC page data, selected SERP results, and human judgment to catch mixed tutorial/comparison intent, conflicts with old posts, over-merging, and over-splitting. - 5
Step 5: Assign one primary action
Map each cluster to a new article, pillar page, old-post refresh, FAQ, internal-link update, or discard, and record the reason. - 6
Step 6: Produce the internal-link plan
Define pillar-to-cluster links, canonical targets, and descriptive anchor text, then check for orphaned articles and duplicate-page risk.
FAQ
Should keyword clustering group similar phrases or shared search intent?
When does a keyword deserve its own article instead of an H2 or FAQ in an existing post?
Can GSC queries, Reddit questions, GitHub issues, and competitor titles live in one clustering table?
Why does AI merge semantically similar keywords with different intent?
How should I choose between SERP-based, semantic, and hybrid clustering?
Should I create a new page when an older post already covers the topic?
Can keyword clustering turn into low-value programmatic page generation?
12 min read · Published on: Nov 13, 2026
AI Content Site Automation Guides: Planning, Writing, Publishing, and Ops
If you landed here from search, the fastest way to build context is to jump to the previous or next post in this same series.
Previous
AI Topic Research Agent: Find Technical Blog Ideas with Google, Reddit, and GitHub
Turn GSC queries, Reddit questions, and GitHub issues into technical blog candidates with evidence levels, scoring, deduplication, risk checks, and human review.
Part 2 of 4
Next
How to Write Technical Blog Posts with AI: Research, Code Validation, and Human Review
Build a reliable AI technical-writing workflow that verifies changing facts, runs code examples, records evidence, and sends failed checks back for revision.
Part 4 of 4



Comments
Sign in with GitHub to leave a comment