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.
Stratified sampling: by template
Pages are sampled stratified across templates (up to 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. It audits by template: pages sampled stratified across templates, 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; per-template breakdown landed 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.