airport-hotels.example
Doorway garden
240k pages across /hotel-[city] templates. 94% of pages scored below 30 on uniqueness. SpamBrain treats these as a single thin cluster — one structural fix, not 240,000.
pseolint is the open-source linter for programmatic SEO. Paste a URL or drop it in CI: it finds the doorway clusters, near-duplicates, and thin templates that trip SpamBrain, then tells you which template to fix — one fix, N pages. It also flags which pages can get cited in ChatGPT, Perplexity, and Google AI Overviews.
No signup · 3 audits/day50 pages per audit24h retention (anon)See all limits
Analytics-safe — audit runs won't touch your GA, PostHog, Mixpanel, or session-replay dashboards.
↑ Each tile = one page. Color = worst rule that fires on that page. Watch the identity mark in the nav.
Who it's for
City × service grids, directories, state × fee matrices — content generated from a template at scale.
Thousands of pages from one template. One structural fix should cover all of them — pseolint tells you which.
Gate every client build before it goes live. Catch the deindexing risk in the PR, not in a ranking drop.
Integration, comparison, and feature pages that quietly drifted into doorway territory while you shipped.
Pinpoint which template is broken. Fix one template, fix N pages.
This is the site verdict. siteVerdictFromTemplates picks the worst template with ≥5% URL coverage. /listing/:slug covers 97.3% of the site — so its concerning verdict drives the headline, even though /article/:slug is clean. One template-level fix, not 8,201 page-by-page investigations.
Sampling model — before & after
v0.5 — flat random sample
200 URLs drawn across the whole site. On an 8,200-URL directory, 0.2% coverage. The /listing/*template's thin-content crisis averages out with the clean article pages. Site scores caution — the problem is invisible.
v0.6 — K=10 per template
3 templates × 10 samples = 30 fetches (vs 200). Each template gets its own verdict. The /listing/* cluster surfaces an 8/10 thin-content fire rate — unmistakable. /article/* gets credit for being clean.
Scope
Self-select before you run it. The audit is narrow on purpose; if you want a 360° SEO crawl, the right tool is somewhere else on this page.
What pseolint is
An audit specifically for programmatic-SEO sites (template-driven content at scale) and AI Overview readiness. v0.6 audits by template — K=10 URLs sampled per template, one verdict per template, site verdict = worst template above 5% coverage. Catches SpamBrain-classifier triggers from the March 27, 2026 core update, the May 7, 2024 site-reputation-abuse policy, the March 5, 2024 scaled-content-abuse update, and the AEO patterns that determine whether ChatGPT, Perplexity, and Google AI Overviews cite your pages.
Use it when
What pseolint isn't
A general SEO audit. We don't measure Core Web Vitals, broken links, competitor research, keyword research, or backlink audits. If that's what you need, run one of these instead:
Three archetypes
Illustrative archetypes, not real sites — the shapes pseolint actually finds. Each grid shows a dominant template's sample; lower score = safer. The verifiable version is your own scan and the public leaderboard.
airport-hotels.example
240k pages across /hotel-[city] templates. 94% of pages scored below 30 on uniqueness. SpamBrain treats these as a single thin cluster — one structural fix, not 240,000.
legal-directory.example
Structural hygiene is fine. Every article is credible prose. Zero citations, zero bylines — signals SpamBrain increasingly uses to distinguish generation from reporting.
recipe-programmatic.example
180 category pages with genuine variation in ingredient lists, prep time, first-person intros. Templates exist, but each page has a human-sized reason to exist. Rare.
By the numbers
Specific values you can cite — what we run, what we cap, and which Google policies the ruleset maps to.
links/host-section-divergence; v0.5.2 added 4 content-quality rules; v0.6 added per-template breakdown across the full ruleset (live list).--sample-seed makes verdicts reproducible across runs. Info-severity findings can't accumulate past a per-bucket cap. The open-source calibration corpus + runner + regression tests guard against engine drift on each release. Full engineering log at /methodology.links/host-section-divergence), the March 5, 2024 scaled-content-abuse update, and the 2022 SpamBrain rebuild that moved enforcement from manual review to silent classifier-time suppression.robots.txt respect including Crawl-delay capped at 2 minutes.Ship it in CI
Drop this into .github/workflows/pseolint.yml. It audits your build output on every PR, posts a SpamBrain Risk Score summary as a comment, and fails the check when the score crosses your threshold. Two minutes to wire up.
Prefer the CLI? npx pseolint ./out --ci-threshold concerning --format json does the same thing locally — see the MCP server for editor + agent setups.
name: pSEO Lint
on: [pull_request]
jobs:
audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '20' }
- run: npm run build
- uses: ouranos-labs/pseolint@action-v1
with:
source: ./out
threshold: 40FAQ
Early-stage and built in the open. Open-source (MIT), launched January 2026, with rules across 8 categories mapped to current Google policy. We run pseolint on pseolint.dev itself and publish the result on the public leaderboard — verdict Ready, origin handled the crawl at a 106ms median TTFB, and the audit's open findings are literally our own SEO to-do list. The traction is the receipts, not a number we made up.