Skip to content
Velaris

Engineering

Programmatic SEO without shipping doorway pages

We generated 600 integration pages. The difference between a page set that ranks and one that gets demoted is what varies between the pages.

Vithu ·

Programmatic SEO doorway pages are what you ship when you generate a large page set from one template with the nouns swapped. Google names the pattern explicitly and demotes it — and the assessment lands on the whole domain, not only the offending URLs. We just expanded from 68 generated pages to 600, so this was the problem we had to solve first.

The templating trap

Here’s the shape almost everyone starts with. One sentence, two variables:

blurb: `An agent watches ${a.reads} in ${A}, decides what matters, and ${b.writes} in ${B}.`;

At 68 curated pages, that’s fine. Those pages exist because someone decided the query was worth targeting, and the template is a consistent frame around a real answer.

At 600, it stops being a frame and becomes the entire content. Every page says the same thing. The only difference between them is two proper nouns, and a reader who lands on two of them learns nothing the second time. That’s the definition Google is describing — pages that exist for the index rather than the reader.

The naive response is to abandon scale and hand-write everything. That doesn’t work either: nobody writes 600 pages, and the ones that exist go stale.

Vary what’s actually different

The way out is to notice that the pages are genuinely different — you’re just not saying how.

Each integration in our data already carried facts that differ per tool: a category, a tagline, and a list of real capabilities it advertises. That’s the raw material. Instead of one template with names substituted, compose the copy from those per-tool facts:

`Velaris can ${capabilityOf(from)} in ${A}, judge what's worth acting on,
 and ${roleOf(to).writes} in ${B} — ${intentOf(categoryOf(to))}.`;

Now two pages sharing a source diverge, because the destination contributes its own capability and its own category purpose:

→ Slack: …and posts a message or thread in Slack — so the team sees it where they already work, without another tab.

→ QuickBooks: …and drafts an invoice or categorizes an expense in QuickBooks — so the books match reality without a month-end scramble.

Same generator. Genuinely different pages, because the underlying facts were genuinely different.

Category pairing is the highest-leverage signal we found. Moving email into a chat tool is a different job from moving it into a finance ledger, and the sentence should say so. Same-category flows — chat to chat — get their own framing about field mapping, because that’s the real question there.

Test the variance, don’t assume it

The useful check is mechanical. Join each page’s full body into a string and count the unique ones:

const bodies = PAGES.map((p) => [p.blurb, ...p.examples].join(" "));
console.log(new Set(bodies).size, "/", bodies.length);

We hold 600 of 600. If that number drops well below the total, your generator is producing duplicates and you’ll find out from a script rather than from Search Console six months later.

Orphans are the other way these fail

Even good generated pages die if nothing links to them. A page reachable only from the sitemap is a page the crawler deprioritises, and a large set of them looks exactly like an index-stuffing attempt.

So each page links to siblings sharing a tool, ordered so the pages with real search demand come first. Then verify there are no orphans:

pages: 600 · orphans (0 inbound internal links): 0

That assertion is worth writing down as a test, because orphans appear silently whenever the generator’s inputs change.

Two things worth being honest about

Volume doesn’t substitute for editorial content. A site whose page count is dominated by generated pages, with almost no hand-written depth, reads as thin regardless of how well the generator varies. The generated set is the long tail; it needs a body of real writing alongside it.

Not everything deserves a page. We excluded tool pairings with no capability data behind them, because a page about software we haven’t shipped is filler no amount of templating rescues. If you can’t say something specific about a combination, that’s a signal not to generate it.

Velaris ships 600 integration pages built this way — see how tool selection works underneath.