--- name: programmatic-seo description: Create SEO-optimized pages at scale using programmatic/template-based approaches. Use when the user says "programmatic SEO", "pSEO", "pages at scale", "template pages", "dynamic SEO pages", "auto-generate pages", "landing pages at scale", "city pages", "comparison pages", or wants to create many similar pages targeting different keywords. --- # Programmatic SEO Skill You are an expert in programmatic SEO (pSEO) -- the strategy of creating large numbers of targeted pages using templates and data. Help users identify page patterns, build templates, and deploy at scale while avoiding thin content penalties. ## What is Programmatic SEO? Programmatic SEO creates pages at scale by combining: - A **page template** (layout + structure) - A **data source** (database, API, CSV) - **Dynamic content** (unique per page, not just variable substitution) - **SEO optimization** (meta tags, internal links, schema) **Examples of successful pSEO:** - Zapier: "How to connect {App A} to {App B}" (150K+ pages) - Nomad List: "{City} for digital nomads" (1000+ city pages) - Wise: "{Currency A} to {Currency B} exchange rate" (10K+ pages) - G2: "{Software} reviews" (100K+ product pages) - Tripadvisor: "Best {type} in {city}" (millions of pages) ## Process ### Step 1: Identify the Page Pattern Help the user find their pSEO opportunity. Look for patterns where: **Pattern formula:** `{Modifier} + {Head Term} + {Qualifier}` | Pattern Type | Formula | Example | Volume Potential | |-------------|---------|---------|-----------------| | Location + Service | "{service} in {city}" | "plumber in Austin" | Cities x Services | | Comparison | "{product A} vs {product B}" | "Notion vs Asana" | nC2 combinations | | Integration | "{tool A} + {tool B} integration" | "Slack Salesforce integration" | Tools x Tools | | Template/Example | "{type} template" | "invoice template" | Types count | | Stats/Data | "{topic} statistics {year}" | "remote work statistics 2025" | Topics x Years | | Glossary | "What is {term}" | "What is APR" | Terms count | | Best/Top | "Best {product} for {use case}" | "Best CRM for startups" | Products x Uses | | Review | "{product} review" | "Airtable review" | Products count | | Alternative | "{product} alternatives" | "Slack alternatives" | Products count | | Cost/Pricing | "How much does {service} cost" | "How much does a website cost" | Services count | **Qualification criteria for a good pSEO opportunity:** - [ ] Pattern has 100+ possible pages minimum - [ ] Each combination has measurable search volume (even 10-50/mo is fine at scale) - [ ] You can generate genuinely useful, unique content for each page - [ ] The data is available (API, database, web scraping) - [ ] Competitors aren't already dominating with better data - [ ] Pages serve real user intent (not just keyword stuffing) ### Step 2: Build the Data Source Define the data model that powers the pages: ```typescript // Example: City + Service pages interface PageData { // URL parameters slug: string; // "plumber-in-austin-tx" city: string; // "Austin" state: string; // "TX" service: string; // "plumber" // SEO fields title: string; // "Best Plumber in Austin, TX | Top 10 for 2025" metaDescription: string; // "Find the best plumber in Austin, TX..." h1: string; // "Best Plumber in Austin, TX" // Dynamic content providers: Provider[]; // Local providers data avgCost: number; // Average cost in this market reviewCount: number; // Total reviews aggregated faqs: FAQ[]; // Location-specific FAQs // Related pages (internal linking) nearbyPages: string[]; // Nearby cities relatedServices: string[]; // Related services } ``` **Data sources to consider:** - Public APIs (government data, Wikipedia, industry databases) - Web scraping (with permission/robots.txt compliance) - User-generated content (reviews, contributions) - AI-generated unique analysis per entity - Licensed data (paid data providers) - Internal product data (for SaaS/e-commerce) ### Step 3: Create the Page Template Build a template that generates high-quality, unique pages. Critical principle: **each page must provide standalone value.** #### Template Structure ```markdown ## Page Template: {Pattern Name} ### Above the Fold - H1: {dynamic title} - Key stat or hook: {dynamic data point} - Quick summary: 2-3 sentences with unique data - CTA (if commercial intent) ### Main Content Section 1: Overview - {Entity}-specific introduction (NOT generic) - Unique data point 1: {dynamic} - Unique data point 2: {dynamic} - Contextual explanation ### Main Content Section 2: Detailed Analysis - Comparison table or detailed breakdown - {Entity}-specific insights - Data visualizations if applicable ### Main Content Section 3: Practical Information - How-to or action steps - Costs, timing, or specifications - Location-specific or entity-specific details ### Related Content - Internal links to related pages (same cluster) - Links to parent/pillar page - Links to sibling pages ### FAQ Section - 3-5 questions specific to this entity - Answers with unique data points ### Schema Markup - Appropriate structured data for page type ``` #### Avoiding Thin Content **The #1 risk in pSEO is thin content.** Every page must pass this checklist: | Check | Requirement | How | |-------|------------|-----| | Unique content | > 60% of visible text is unique to this page | Dynamic data, unique analysis, UGC | | Sufficient depth | > 500 words of substantive content per page | Template sections + dynamic content | | Unique data | At least 2 data points unique to this entity | API data, calculations, aggregations | | Useful to visitor | Page answers the searcher's query fully | Match search intent | | Not auto-generated feel | Reads naturally, not like a template | Varied sentence structures, context | | Internal value | Links to and from other relevant pages | Related pages section, breadcrumbs | | Visual content | At least 1 unique or relevant image | Maps, charts, entity images | ### Step 4: Next.js Implementation #### Dynamic Routes (App Router) ```typescript // app/[service]/[city]/page.tsx import { Metadata } from 'next'; import { notFound } from 'next/navigation'; import { getPageData, getAllPages } from '@/lib/pseo-data'; interface Props { params: { service: string; city: string }; } // Generate static paths at build time export async function generateStaticParams() { const pages = await getAllPages(); return pages.map((page) => ({ service: page.serviceSlug, city: page.citySlug, })); } // Dynamic meta tags export async function generateMetadata({ params }: Props): Promise { const data = await getPageData(params.service, params.city); if (!data) return {}; return { title: data.title, description: data.metaDescription, alternates: { canonical: `https://example.com/${params.service}/${params.city}`, }, openGraph: { title: data.ogTitle, description: data.ogDescription, url: `https://example.com/${params.service}/${params.city}`, type: 'website', }, }; } export default async function Page({ params }: Props) { const data = await getPageData(params.service, params.city); if (!data) notFound(); const jsonLd = { '@context': 'https://schema.org', '@type': 'WebPage', name: data.title, description: data.metaDescription, // ... additional schema }; return ( <>