SEO engine
Programmatic SEO automation that shows you its working.
Programmatic SEO automation in FounderReply crawls a site, computes on-page metrics with no model in the scoring path, reconciles a per-page issue register, samples keyword positions, ingests Search Console where a property is connected, drafts competitor conquest pages, and submits new URLs to IndexNow.
- Scoring is deterministic, not a model’s opinion
- Real-user field data from PageSpeed, alongside the Lighthouse lab run
- Google’s own numbers when Search Console is connected
- An IndexNow ping the moment a page publishes
How a sweep runs
One tick, five bounded steps.
The whole engine is a cron job with budgets. Every stage is capped so a sweep stays cheap, and every substrate that is missing a key is a normal skip rather than a failure — an engine that halts because PageSpeed is unconfigured is an engine that stops running.
- 1
Inventory
Our own site is enumerated from the stores that render its programmatic pages, so the inventory cannot drift from what actually ships. A customer site is discovered from its live URL structure instead.
- 2
Crawl and measure
Due pages are fetched twice — once rendered, once with JavaScript off — then run through the deterministic metric set and, where the quota allows, PageSpeed. Twenty-five pages a sweep, never-crawled first.
- 3
Score and reconcile
Each page gets a 0–100 composite and an issue list. The register is reconciled rather than appended, so a fixed issue disappears instead of accumulating into a number nobody trusts.
- 4
Sample and ingest
The twenty least-recently-checked keywords get a fresh position sample, and Search Console query performance is ingested where a property is connected. The two land in separate columns.
- 5
Write and submit
Gap-driven articles and competitor conquest pages are drafted — as drafts, behind the same publish gate as everything else — and anything that publishes is pushed to IndexNow so the Bing chain sees it without waiting for a crawl.
The check list
Every code it can raise, and what each one costs you.
Severity is a closed vocabulary — error, warn, info, and no fourth tier — and the thresholds are the literal constants in the analyzer. A title warns at 30 characters and again at 60 because those are the numbers in the file.
| Issue code | Severity | Raised when |
|---|---|---|
| http_error | error | The page responded 400 or above. |
| title_missing | error | No <title>, and no og:title to fall back on. |
| title_too_short | warn | Title under 30 characters. |
| title_too_long | warn | Title over 60 characters. |
| meta_description_missing | error | No meta description at all. |
| meta_description_short | warn | Description under 120 characters. |
| meta_description_long | warn | Description over 160 characters. |
| h1_missing | error | The page has no h1. |
| h1_multiple | warn | More than one h1 on the page. |
| heading_hierarchy_skip | info | A heading level was skipped, e.g. h2 straight to h4. |
| thin_content | warn | Under 300 words of body text. |
| content_render_dependent | warn | Over 40% of the body text exists only after JavaScript runs. |
| canonical_missing | warn | No rel=canonical link. |
| canonical_mismatch | warn | The declared canonical is not the URL this path should claim. |
| jsonld_missing | info | No JSON-LD block on the page. |
| jsonld_invalid | warn | JSON-LD is present but does not parse. |
| og_incomplete | info | og:title, og:description or og:image is missing. |
| image_alt_coverage_low | warn | Under 90% of images carry a non-empty alt. |
| keyword_absent_title | warn | The page’s tracked target keyword is not in its title. |
| keyword_absent_h1 | info | The page’s tracked target keyword is not in its h1. |
| performance_poor | warn | Lighthouse performance score below 50. |
| cwv_lcp_poor | warn | Field largest contentful paint over 2500 ms. |
| cwv_inp_poor | warn | Field interaction to next paint over 200 ms. |
| cwv_cls_poor | warn | Field cumulative layout shift over 0.1. |
| lh_seo_* | warn | One code per failing Lighthouse SEO audit, minted from its audit id — the cross-check against our own computed metrics. |
How the 0–100 score is weighted
Weights sum to 100 and each verdict counts as pass, half a pass, or nothing. The weight vector is version-stamped into every stored audit, so revising the weights does not silently rewrite last quarter’s history. When PageSpeed has not run, performance scores as a neutral pass rather than dragging the page down for data that does not exist yet.
The first site it audits is this one
What it actually audits
What one sweep touches, and what bounds it.
How many pages, fetched how many ways, measured against which sources — and which of it you can drive from your own code instead of the dashboard.
- Crawl budget
- Twenty-five pages per site per sweep by default — never-crawled first, then stalest — and every page is re-crawled weekly.A budget is what keeps a tick cheap enough to run on a shared cron rather than as a job somebody has to remember to start.
- How a page is fetched
- Twice: once rendered, and once with JavaScript off, so the engine can see what a crawler without a renderer would get.Over 40% of the body text existing only after JavaScript runs is what raises content_render_dependent.
- Metrics per page
- Roughly thirty discrete values — title and description lengths, heading counts, word count, canonical, indexability, structured-data types, alt coverage, internal and external link counts.Computed from the HTML, which is why they chart over time, reproduce exactly, and can gate CI.
- Performance data
- Field p75 largest contentful paint, interaction to next paint and cumulative layout shift from real Chrome users, plus the Lighthouse lab run and its performance, SEO and accessibility scores.Failing Lighthouse SEO audits are reconciled against our own computed metrics; a disagreement becomes its own issue rather than being quietly dropped.
- Keyword portfolio
- Seeded from the target keywords the blog engine already researched, so a portfolio exists without anyone typing a keyword list.Seeding costs no model call — it reads keywords that already exist. Sampling rotates least-recently-checked first, so a growing portfolio never starves its newest entries.
- Ranking sources
- A sampled SERP position always; Google’s own clicks, impressions, click-through rate and average position per query once a verified Search Console property is connected.A sampled position is an estimate of where you rank; Search Console is a record of where you ranked. Where both exist they are stored as two distinct sources, because the average of an estimate and a record is neither.
- Submission
- Anything that publishes is pushed to IndexNow the moment it goes live.That feeds the Bing chain — which grounds Copilot, ChatGPT Search and part of Perplexity. Google ignores IndexNow and finds the page through your sitemap instead.
- Drive it yourself
- run_seo_auditget_seo_overviewlist_seo_issuestrack_keywordget_keyword_rankingssync_search_consoleadd_seo_sitegenerate_blog_from_seo_gapsgenerate_conquest_postsrefresh_stale_blog_postsTen of the tools on the MCP server, so a sweep can be driven from an assistant or from your own code instead of the dashboard. Source: lib/mcp.ts.
The load-bearing claim
No model decides your score. Arithmetic does.
Every number above is computed from your HTML by pure functions with fixed thresholds. Two runs of the same page produce the same score, which is what makes it chartable over time, reproducible, and safe to gate a build on. The qualitative layer exists, it is separate, and it is optional.
Conquest pages
Comparison pages, with the competitor’s own pages as the source
A competitor’s public URL structure is mined and bucketed by what part of a content strategy each path serves. Comparison, alternatives and migration articles are then drafted against it, citing that competitor’s own pages — with sourced and dated claims, genuine credit where they are better, and never a self-declared number one.
They publish unlisted: out of the blog index, but present in the sitemap and linked from the compare hub. Fully orphaned indexed pages are what the doorway policy targets, so these are deliberately not orphaned.
What gets drafted
Comparison
Us and them, one row per dimension, both columns honest.
Alternatives
The field around a named tool, including tools that are not us.
Migration
What moving actually involves, written for someone who has already decided.
Drafted, not posted: a conquest page is an article like any other and waits at the same publish gate.
The limits
What it guarantees, and what it refuses to claim
An SEO page that promises traffic is making a promise it does not control. These are the two lists that replace it.
The SEO engine, as a contract
Every line on both sides is a behaviour in the shipped code, not a policy we intend to adopt.
FAQ
What people ask before pointing it at a site.
Whether a model decides your score, what changes when Search Console is connected, what IndexNow actually buys you, and the question every SEO page dodges — whether any of this is a traffic promise.
Keep reading
Ready when you are
Point it at your site and read the first audit.
Add a domain and the first sweep returns a scored page list with the exact issue codes printed above. No traffic promise, no vendor score — the thresholds are on this page and the numbers are yours to check.
Free to start · No credit card required · Deterministic scoring