Generate millions of landing pages at the edge. Zero origin server. < 50ms response times.
Built by John Williams — the system behind googleadsagent.ai's 2.3M+ programmatic landing pages.
This engine generates city × service and city × industry landing pages dynamically at the Cloudflare edge. One CLI command produces:
- Cloudflare Pages Functions that render unique HTML for every city/service combination
- XML sitemaps partitioned to stay under Google's 50K URL limit
- Tiered indexing — top metros get
index, follow; smaller cities getnoindex, follow(still usable as paid traffic landing pages) - Lead capture forms via Web3Forms (free)
- Schema.org markup — ProfessionalService, BreadcrumbList, FAQPage
- E-E-A-T signals — author credentials, experience blocks
| Data | Sample (included) | Full (Google Ads geo targets) |
|---|---|---|
| Cities | 30 | 18,744 |
| Service verticals | 10 | 105+ |
| Ecommerce industries | 2 | 20 |
| Service pages | 300 | 1,971,336 |
| Ecommerce pages | 60 | 374,880 |
| Total | 360 | 2,346,216 |
These pages are generated by this exact engine:
- Plumber in New York, NY
- HVAC in Phoenix, AZ
- Pet Supplies Ecommerce in New York, NY
- Electronics Ecommerce in Los Angeles, CA
Service Page — Plumber in New York, NY:
Ecommerce Page — Pet Supplies in New York, NY:
# Clone the repo
git clone https://github.com/itallstartedwithaidea/programmatic-seo-engine.git
cd programmatic-seo-engine
# Install dependencies
npm install
# Preview page counts
node generate.js --count
# Generate everything (functions + sitemaps)
node generate.js
# Preview locally
npm run preview
# → Open http://localhost:8788/services/new-york-ny/plumber/
# Deploy to Cloudflare Pages
npm run deployCopy config.js to config.local.js and customize:
module.exports = {
domain: 'https://yourdomain.com',
brandName: 'Your Brand',
web3formsKey: 'your-key-from-web3forms.com', // free
gtmId: 'GTM-XXXXXXX', // optional
author: {
name: 'Your Name',
title: 'Your Title',
company: 'Your Company',
credentials: 'Your experience and qualifications.',
},
tieredIndexing: true,
sitemapMaxUrls: 45000,
};- Go to web3forms.com
- Enter your email
- Copy the access key
- Paste into
config.local.js
┌─────────────┐ ┌────────────────────┐ ┌──────────────┐
│ generate.js │────→│ [[catchall]].js │────→│ Cloudflare │
│ (build) │ │ (edge function) │ │ Edge (300+ │
│ │ │ │ │ locations) │
│ data/*.json │ │ Embedded: │ │ │
│ templates/ │ │ - 18K cities │ │ < 50ms │
│ config.js │ │ - 105 services │ │ response │
└─────────────┘ │ - HTML template │ └──────────────┘
│ - Region logic │
│ - Schema.org │
└────────────────────┘
generate.jsreads your data files (cities, services, industries) and HTML templates- It produces a single JavaScript file per page type that embeds all data + the template
- This file is deployed as a Cloudflare Pages Function — a serverless edge function
- When a request hits
/services/new-york-ny/plumber/, the function:- Parses the URL → looks up city and service from embedded data
- Checks state restrictions and tiered indexing rules
- Renders the HTML template with city/service-specific variables
- Returns the response with appropriate cache and robots headers
- No database. No API calls. No origin server. Everything is in the function.
- Speed: Pages render in < 50ms at 300+ Cloudflare edge locations worldwide
- Cost: Cloudflare Pages is free for up to 500 builds/month and unlimited requests
- Scale: 2M+ pages from a single ~700KB JavaScript file
- SEO: Each page has a unique title, meta description, canonical URL, Schema.org markup, and content
- No infrastructure: No servers, no databases, no containers, no Kubernetes
- Long-tail keyword capture — "plumber in phoenix az" has less competition than "plumber" but higher conversion intent
- Local relevance signals — Each page contains city-specific content, regional data, and local service information
- Schema.org markup — ProfessionalService, BreadcrumbList, and FAQPage schemas on every page
- E-E-A-T compliance — Author credentials block with real experience, expertise, and trust signals
- Tiered indexing — Only top metros (80% of search volume) are indexed; smaller cities exist for paid traffic without diluting crawl budget
- Unique content per page — Region-specific narratives, category pain points, and service-specific insights prevent thin content flags
- Helpful Content: Each page answers "Why do [service] ads fail in [city]?" with genuine, category-specific insights
- E-E-A-T: Author block with verifiable credentials, company, and experience
- No doorway pages: Each page has unique content driven by real data (category pain points, regional market traits, service-specific insights)
- User intent match: Pages match "near me" and "[service] in [city]" search intent directly
This engine is designed to work with AI search (Google AI Overviews, Perplexity, ChatGPT search):
- Structured data (Schema.org) makes content extractable by AI
- llms.txt at the root tells AI crawlers about your site structure
- FAQ schema provides direct question-answer pairs for AI citations
- Clear entity relationships between author, organization, and services
The engine generates Cloudflare Pages Functions natively. Just run generate.js and deploy.
node generate.js
npm run deployUse a Cloudflare Worker as a reverse proxy in front of Shopify. Programmatic pages render at the edge; everything else passes through to Shopify.
See integrations/shopify/README.md for step-by-step instructions.
Three options:
- Cloudflare reverse proxy (recommended) — same as Shopify approach
- WordPress plugin — renders pages natively in PHP using the same templates
- Static HTML export — generate HTML files and upload to your server
See integrations/wordpress/README.md for all three approaches, including a ready-to-use WordPress plugin.
Edit files in templates/:
service-page.html— Service landing page layoutecommerce-page.html— Ecommerce industry page layout
Templates use {{VARIABLE}} placeholders that the edge function replaces at render time.
| Variable | Description |
|---|---|
{{BRAND_NAME}} |
Your brand name from config |
{{DOMAIN}} |
Your domain URL |
{{SERVICE_NAME}} |
Display name (e.g., "Plumber") |
{{SERVICE_NAME_LOWER}} |
Lowercase (e.g., "plumber") |
{{CITY_NAME}} |
City name (e.g., "Phoenix") |
{{STATE_ABBR}} |
State abbreviation (e.g., "AZ") |
{{REGION}} |
US region (e.g., "Southwest") |
{{CATEGORY_LABEL}} |
Category name (e.g., "Home Services") |
{{PAIN_*}} |
Six category-specific pain points |
{{AUTHOR_*}} |
Author credentials for E-E-A-T |
{{FORM_KEY}} |
Web3Forms access key |
Edit data/services-sample.json:
{
"slug": "pool-cleaning",
"name": "Pool Cleaning",
"category": "home",
"stateRestriction": ["FL", "TX", "AZ", "CA"]
}Download the Google Ads geo targets CSV and extract cities:
# The CSV is freely available from Google
# https://developers.google.com/google-ads/api/data/geotargets
node scripts/extract-cities.js path/to/geotargets.csv > data/cities-full.jsonprogrammatic-seo-engine/
├── generate.js # Main CLI — generates everything
├── config.js # Configuration template
├── package.json # Node.js package
├── wrangler.toml # Cloudflare Pages config
├── data/
│ ├── cities-sample.json # 30 top US metros
│ ├── services-sample.json # 10 service verticals + categories
│ └── industries-sample.json # 2 ecommerce industries
├── templates/
│ ├── service-page.html # Service landing page template
│ └── ecommerce-page.html # Ecommerce landing page template
├── functions/ # Generated Cloudflare Functions (output)
├── sitemaps/ # Generated XML sitemaps (output)
├── integrations/
│ ├── shopify/ # Shopify reverse proxy guide
│ └── wordpress/ # WordPress plugin + guides
├── llms.txt # LLM-readable project index
└── docs/
├── ARCHITECTURE.md
└── SEO-BENEFITS.md
- googleadsagent.ai/services/ — 1,971,336 service landing pages across 18,744 US cities and 105+ verticals
- googleadsagent.ai/ecommerce/ — 374,880 ecommerce industry pages across 20 Google Taxonomy categories
MIT — use it however you want. Built by John Williams through It All Started With A Idea.
If this saves you time, give it a star and follow me on LinkedIn.

