How to Build an SEO Topical Map: 9 Sites, Real GSC Data, One Session
Most SEO audits produce a PDF nobody reads. This one produced 9 developer-ready Next.js files, a 430-impression quick-win list, and a monthly refresh pipeline — in a single session.
Here's exactly how to build an SEO topical map from scratch: what data to pull, how to analyze it, and how to turn findings into implementation files developers can use immediately.
What an SEO Topical Map Actually Does
An SEO topical map defines what each page on your site owns — and what it doesn't. It answers three questions search engines ask before ranking anything:
- Does this site have depth on this topic, or is this an isolated page?
- Does the internal link structure confirm this site is authoritative here?
- Is there a logical content hierarchy from broad to specific?
Google's Helpful Content system and AI Overviews both reward topical authority over individual page optimization. A site with 15 tightly linked pages on "AI call answering for law firms" outranks a site with one well-optimized page on the same topic — even if that single page has better on-page SEO.
The topical map is the architecture that makes depth visible to search engines.
The Starting Point: 9 Sites Across Two Verticals
The network:
AI/SaaS cluster: KaiCalls (AI call answering), BuildWithKai (AI product builder), VocalScribe (transcription), Zehrava (AI agent infrastructure), MeetKai (hub site).
Event/local cluster: Starrs Party (NJ tent rental), Awesome Backyard Parties (event vendor marketplace), ConnorGallic (personal brand hub), Tropibot (pool/lawn robots).
Goal: understand what each site owns topically, where they're bleeding impressions, and how they should cross-link.
Step 1: Pull Real GSC Data Before Writing Anything
No assumptions. Pull actual Google Search Console data for every property first.
cmo gsc queries --site=kaicalls --limit=30
cmo gsc pages --site=kaicalls --limit=30
cmo gsc queries --site=starrsparty --limit=30
# repeat for all 9 sites
Three sites had service account access issues — wrong URL format in .env (sc-domain: vs https://www.). Fixed and confirmed all 9 before proceeding.
What the data showed:
| Site | Page / Query | Impressions | Position | Clicks |
|---|---|---|---|---|
| KaiCalls | /compare/human-receptionist | 208 | 6.4 | 0 |
| KaiCalls | /compare/goodcall | 62 | 4.5 | 0 |
| Starrs Party | /party-rental-inventory | 39 | 6.1 | 0 |
| Starrs Party | tent rental query | 537 | 40.0 | 0 (no page) |
| Starrs Party | /blank-1, /blank-9 | — | — | Wix placeholders indexed |
| ConnorGallic | /kai-calls page | — | 8.2 | 0 (no outbound link) |
The pattern: 430 monthly impressions on page 1, converting at 0%. Not because the pages are uncompetitive — because the SERP snippets use a global meta description fallback instead of page-specific copy. This is the single highest-ROI fix in the entire network. No new content. Five minutes per page.
Step 2: Inspect the Codebase Before Writing Any Tasks
GSC showed Wix-era URLs still indexed on Starrs Party (/blank-1, /book-now, /starrspartyrentalprocess). Before writing redirect instructions, confirmed the actual framework:
gh repo view cgallic/starrs_party --json primaryLanguage,languages
TypeScript + Next.js App Router. Not Wix. The old URLs were migration artifacts from a platform switch. The new site already had route groups (locations) and (services), dynamic [city]/[service] combos, and county pages for Hunterdon, Somerset, Morris.
The fix: next.config.ts redirects sending old indexed URLs to the correct new routes.
The lesson: An SEO topical map built without looking at the actual code produces tasks that don't match the implementation. Always inspect the repo before writing developer instructions.
Step 3: Build the SEO Topical Map
With real data in hand, mapped each site's territory:
AI CLUSTER
kaicalls.com → "AI call answering for law firms / home services"
vocalscribe.xyz → "Voice transcription"
zehrava.com → "AI agent write-path control"
buildwithkai.com → "AI product builder"
meetkai.xyz → hub — case studies linking to all of the above
EVENT CLUSTER
starrsparty.com → "NJ tent and party rental" (local SEO)
abp.com → "Event vendor marketplace" (programmatic geo SEO)
AUTHORITY HUB
connorgallic.com → should link out to every product — currently disconnected
STANDALONE
tropibot.com → "Smart home robots for tropical climates"
Cross-link matrix followed topical adjacency, not keyword convenience. KaiCalls links to Starrs Party because Starrs Party is a real paying KaiCalls customer — an authentic editorial link with context. The /kai-calls page on ConnorGallic ranks at position 8.2 with no outbound link to kaicalls.com. One <a href> tag turns an orphaned ranking page into a referral source.
Step 4: Write Developer Task Files With Actual Code
One task file per site. The constraint: developers shouldn't need any other document. Every file is copy-paste-ready.
// app/compare/human-receptionist/page.tsx
export const metadata: Metadata = {
title: 'AI Receptionist vs Human Receptionist: Full Cost Comparison (2026)',
description: 'Human receptionists cost $2,600–$4,300/month. AI receptionists cost
$69.99–$500/month. Side-by-side breakdown of features, hidden costs, and when
each makes sense.',
}
// components/FaqSchema.tsx — reusable, drop into any page
export function FaqSchema({ items }: { items: FaqItem[] }) {
const schema = {
'@context': 'https://schema.org',
'@type': 'FAQPage',
mainEntity: items.map(item => ({
'@type': 'Question',
name: item.question,
acceptedAnswer: { '@type': 'Answer', text: item.answer },
})),
}
return (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
/>
)
}
9 files. 132KB of implementation-ready code. No instructions requiring interpretation. Developer opens file, copies code, closes file.
For the Starrs Party Bergen County page (63 GSC impressions, no dedicated page), wrote a full page.tsx matching the existing county page structure — metadata export, FaqSchema, styled sections, CTA — ready to drop into app/(locations)/bergen-county/.
Step 5: The Monthly Refresh Pipeline
An SEO topical map is a snapshot. Rankings shift. New keywords emerge. Addressed opportunities open new gaps.
cd /opt/cmo-analytics && source venv/bin/activate
python scripts/network_topo_report.py --discord
Pulls fresh GSC data for all 9 sites on the 1st of every month, diffs against last month's snapshot, flags new keyword opportunities and ranking drops, posts the report to #seo in Discord automatically. The map doesn't go stale.
Full Output of One Session
| File | Contents |
|---|---|
MASTER-TOPO-MAP.md | Full network diagram, cross-link matrix, 430-impression quick wins |
SEO-DOMINATION-PLAN.md | KaiCalls content calendar — 70 existing pages audited, gaps mapped |
site-tasks/kaicalls.md | Metadata fixes, FAQPage schema, /solutions/medical expansion, sitemap |
site-tasks/starrsparty.md | next.config.ts redirects, LocalBusiness schema, Bergen County page |
site-tasks/connorgallic.md | /resources rebuild as hub, author page, cross-link matrix |
site-tasks/abp.md | /vendors page, vendor acquisition articles, KaiCalls case study |
site-tasks/buildwithkai.md | Pillar article + idea validator support article with JSX |
site-tasks/meetkai.md | Sitemap fix, two case study page.tsx files, internal link audit |
site-tasks/vocalscribe.md | Three full page.tsx files — roundup, tutorial, competitor comparison |
site-tasks/zehrava.md | Two developer articles with copy-paste Node.js + Python code |
site-tasks/tropibot.md | ProductSchema component, three full blog posts, Florida climate article |
| Monthly pipeline | Python script + cron, Discord report on the 1st |
The Pattern That Appears in Every Site
Every property in this network had the same structural problem: pages ranking on page 1 with zero clicks.
The cause is always the same. A CMS or framework ships with a global meta description fallback. Nobody adds page-specific descriptions at launch. The page ranks. The SERP snippet shows something generic. Nobody clicks.
The fix is five minutes per page. The return is every impression that's been sitting idle.
Fix the rankings you already earned before building new content. New content only moves the needle if the existing pages aren't sitting idle.
What to Do First on Your Own Site
Run this before touching anything else:
- Pull GSC queries and pages data filtered to positions 1–10
- Filter for pages with impressions > 20 and CTR < 1%
- Check the actual SERP snippet for each — Google shows you what's rendering
- Write page-specific meta descriptions for every hit on that list
- Fix those before writing a single new piece of content
That list is the highest-ROI SEO work on your site right now. Fix the rankings you already earned before building new content.