SEO March 15, 2026 8 min read

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:

  1. Does this site have depth on this topic, or is this an isolated page?
  2. Does the internal link structure confirm this site is authoritative here?
  3. 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:

SitePage / QueryImpressionsPositionClicks
KaiCalls/compare/human-receptionist2086.40
KaiCalls/compare/goodcall624.50
Starrs Party/party-rental-inventory396.10
Starrs Partytent rental query53740.00 (no page)
Starrs Party/blank-1, /blank-9Wix placeholders indexed
ConnorGallic/kai-calls page8.20 (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

FileContents
MASTER-TOPO-MAP.mdFull network diagram, cross-link matrix, 430-impression quick wins
SEO-DOMINATION-PLAN.mdKaiCalls content calendar — 70 existing pages audited, gaps mapped
site-tasks/kaicalls.mdMetadata fixes, FAQPage schema, /solutions/medical expansion, sitemap
site-tasks/starrsparty.mdnext.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.mdPillar article + idea validator support article with JSX
site-tasks/meetkai.mdSitemap fix, two case study page.tsx files, internal link audit
site-tasks/vocalscribe.mdThree full page.tsx files — roundup, tutorial, competitor comparison
site-tasks/zehrava.mdTwo developer articles with copy-paste Node.js + Python code
site-tasks/tropibot.mdProductSchema component, three full blog posts, Florida climate article
Monthly pipelinePython 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:

  1. Pull GSC queries and pages data filtered to positions 1–10
  2. Filter for pages with impressions > 20 and CTR < 1%
  3. Check the actual SERP snippet for each — Google shows you what's rendering
  4. Write page-specific meta descriptions for every hit on that list
  5. 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.