RUNS LOCALLY · 30+ CHECKS · NO ACCOUNT

The SEO workbench that reads your own exports

Drop in the CSVs you already download from Search Console and Analytics. Searchbench tells you what they say, shows the arithmetic behind every claim, and writes the pages worth writing. Your exports stay on your machine; only what you explicitly send for writing ever leaves it.

No accountExports never uploaded Reading costs nothingYour own API key to write
What happened
Overview
Pages 345
Queries 978
Segments 3
What to do
Actions 51
Mapping 979
Brief
Make it
Studio
Library
5,818Clicks, second half vs first
3.02%Average click-through rate
11.7Average position
Rewrite title and meta low effortcounted +142 clicks

Ranks 4.2 on 246 impressions but draws 0.21× the clicks typical for that position

Add internal links low effortread from page +95 clicks

9,461 impressions and no internal links pointing here

Build cluster hub medium effortcounted +88 clicks

285 pages under /resource-center/ and nothing at /resource-center in your exports

Search Console · PagesSearch Console · Queries Per-page query pairsDevicesCountries Search appearanceDatesGA4 pages GA4 landing pagesCrawler export Previous periodPage contentPrompt list Search Console · PagesSearch Console · Queries Per-page query pairsDevicesCountries Search appearanceDatesGA4 pages GA4 landing pagesCrawler export Previous periodPage contentPrompt list
Project overview

What this is, and what I built

Project
Searchbench, a local SEO workbench
Type
Independent product development
Role
Product design, architecture and development. Everything on this page and in the tool.
Problem
SEO data arrives as a pile of disconnected exports, and the tools that read them hide their arithmetic behind a score.You end up trusting a number you cannot check, then writing the pages somewhere else entirely.
Approach
Local-first analysis with deterministic checks, and AI-assisted writing kept strictly separate from it.Nothing a model produces is allowed to become a finding.
Stack
Python 3.11, FastAPI, vanilla JavaScript with no build step, the Claude API for writing only
Scope
SEO analysis, GEO and AI-retrievability checks, programmatic page generation, and the content operations around it
Scale
Built and tuned against 345 pages and 1,300 queries. 439 tests.
0pages matched against 1,300 queries in a single run
0checks over your exports and your own pages
0tests, green before any change ships
0to read and check everything you upload
A worked example

From a CSV to a published page

One real path through the tool, start to finish, with the arithmetic shown at the point it happens.

01
Drop in the exports

Search Console pages and queries, plus GA4 if you have it. Each file is classified from its column headings, cleaned, and reported back: 345 pages, 978 queries, three breakdowns, two files tidied.

02
Something stands out

A page ranks 4.2 on 246 impressions and draws 8 clicks. The click-through rate typical for position 4.2 is about 15%, so it should be drawing closer to 37. It is at 0.21× what the position is worth.

03
The evidence is on the row

No score, no grade. The impressions, the position, the curve and the shortfall all sit beside the finding, labelled counted rather than rule of thumb, so you can verify it against the same export in a spreadsheet if you want to.

04
Open the brief

Every query mapped to that page, each marked measured or inferred. What is wrong with it, a checklist of specific instructions, and whatever the crawler read from the page itself. Free to open, and nothing in it is generated.

05
Rewrite it, keeping what ranks

The queries it already earns clicks on are named in the prompt and preserved. Research and draft run as separate calls, your brand kit is applied while writing and verified afterwards by counting, and internal links are checked against pages that exist.

06
Read it, then take it

It lands in a library. A blocking issue refuses approval outright. What you approve exports as Markdown or Word, and you publish it yourself. Total spend for that page: about $0.05.

Capabilities

Not a dashboard. A workbench.

Every number comes from a row in a file you uploaded or a page the crawler read. No model touches the analysis, so nothing has to be taken on trust.

Reads whatever you give it

Fourteen kinds of export recognised from their column headings, not their filenames. Thousands separators, totals rows and percentages written as less-than are cleaned, with a note saying exactly what changed.

14 file kinds

Findings, with their working

Thirty-odd checks counting and comparing. Each one carries the rows it came from, how it was reached, and a rough size in clicks so the list can be ordered by what it recovers.

Counted, not judged

Every search matched to a page

Measured pairs where the export carries them, stemmed overlap where it does not, and a pin when you disagree. Your answer overrules the tool from then on.

Measured before guessed

Reads your pages, politely

One request a second, robots.txt obeyed and fetched under the tool's own name, only the pages you tick. The whole readable text is kept, which is what lets a check look past the opening paragraph.

Or import a crawl

Hundreds of pages from a table

Each row becomes a page. Patterns build the title and address, the tool fills columns in for you, research and drafting split into separate calls, and conditions skip steps a row cannot support.

A model per step

Checked before you spend

Rows with nothing to say, rows nearly identical to one another, addresses clashing with a real page and steps no row satisfies are all caught before the first call is made.

Preflight

Your rules, on every page

Tone, banned words, reading level and sentence length applied while the page is written, then verified afterwards by counting rather than by opinion.

Brand kit

Links only to pages that exist

Targets chosen by the mapper from your real pages, the page itself excluded by comparing final segments, and anything the model invents flagged before you publish it.

Verified after writing

A ceiling on the spend

Set a budget and the run stops at it. Pause and resume without paying twice for steps already done. Every call recorded with its model, its tokens and its cost.

Per-model ledger

Rewrite what you already have

The searches a page already earns clicks on are named in the prompt and kept, because a rewrite that forgets them trades a position you have for one you only hope for.

Keeps what ranks

Re-run against newer data

Paste the table again when your figures move. Rows that did not change keep their pages; only the ones that did are rewritten, and you read the plan before anything runs.

Resync

Nothing leaves unread

Everything written waits in a library for you to read. Blocking issues refuse approval outright. It exports as Markdown or Word for you to publish yourself; the tool never touches your site.

Approve and export
How it runs

Three phases, in the order the work happens

No configuration and no mapping wizard. Every screen ends by naming what comes next.

01

Drop in the exports

Each file is classified from its headings and cleaned. Pages, queries, devices, countries, appearance, dates, Analytics, a crawler export, or a page-and-query pair file. Anything still missing says which export it needs and where to get it.

Minutes, not setup
02

Read what is there

Every search matched to the page that should answer it. Every page compared against the click-through rate typical for where it ranks. Structure, decay, cannibalisation and thin content all surfaced, ranked by what they recover.

No model called
03

Write what is missing

Research, outline and draft as separate calls. Your tone and banned words applied while writing and checked after by counting. Internal links chosen from pages that exist. Everything waits in a library until you approve it.

Your own API key
The crawl

It reads your pages the way a model would

A normal crawler checks whether a title exists. This checks whether the page can be extracted, quoted and reconstructed by something that is not a browser, which is what decides whether you get cited rather than merely indexed.

/resource-center/money1,537 wordsAI score 74
GPTBot robots.txtallowed
ClaudeBot robots.txtallowed
PerplexityBot robots.txtblocked
Needs JavaScript body text in markupno
Summary block answer above the foldmissing
Citable facts figures, dates, sources0 found
Heading order h2 after h1, no skipsclean
Boilerplate ratio shared chrome vs its own text31%
Author signal byline or reviewernone
Schema structured data presentnone
Near-duplicate of shingled similarity2 pages

What a title-and-meta checker misses

Who you are blocking without knowing

GPTBot, ClaudeBot and PerplexityBot are checked separately. A site can be open to Google and closed to everything that answers questions about it.

Whether the answer is extractable

Retrieval reads passages, not pages. If the answer only emerges over three scrolls there is nothing clean to lift, however good the page is.

Whether there is anything to cite

Answers get built from specifics. A page of general advice has nothing a model can attribute, so it tends to get read and not credited.

The whole text, not the opening

The readable body is kept, so a check can look past the first paragraph and the rewrite has the page to work from instead of asking you to paste it.

Or skip the crawl entirely and import an export from the crawler you already use.

Where it fits

Four jobs, one set of files

The same exports and the same crawl feed all of these. Nothing is a separate product or a separate upload.

SEOClassic search

The ordinary work, done against your own numbers rather than a third party's estimate of them.

  • Pages ranking well and drawing few clicks
  • Searches with nothing to land on
  • Two pages competing for one query
  • Decay, once you load a second period
  • A device or country far below the rest
GEOBeing cited, not just found

Whether an answer engine can reach your page, lift a passage from it, and have something worth attributing.

  • AI crawler access, checked per bot
  • A self-contained answer near the top
  • Figures, dates and named sources to quote
  • Chunk quality, section by section
  • Questions people ask that you do not answer
TECHThe build underneath

The structural problems that hold a whole section back at once, rather than one page at a time.

  • Families of pages with no hub to anchor them
  • Content that needs JavaScript to read at all
  • Heading levels that skip, duplicate h1s
  • Boilerplate drowning the page's own words
  • A shared template dragging every child down
PROGProgrammatic at scale

Hundreds of pages from one table, with the checks that stop programmatic SEO becoming programmatic duplication.

  • Title, address and target query built from patterns
  • Columns the tool works out per row
  • Near-duplicate rows caught before you spend
  • Addresses clashing with pages you already have
  • Re-run against newer data without rewriting the lot
Decisions

Three choices that shaped everything else

Each of these closed a door on purpose. The costs are as interesting as the benefits.

Deterministic analysis, with no model anywhere near it

Problem

A tool that asks a model to judge your pages gives a different answer on Tuesday, cannot show its working, and costs money every time you look. Nobody can audit a finding they cannot reproduce.

Decision

Every check is arithmetic: stemmed matching, shingled similarity, a click-through curve by position. No model is called to produce a finding, ever.

Outcome

Reading costs nothing and runs offline. Each finding carries the rows it came from. The cost is that it cannot make judgements a human would, so it does not pretend to.

Manual exports instead of API integrations

Problem

Connecting to Search Console, Analytics and a keyword API means OAuth, stored credentials, rate limits, a server to hold them, and three paid subscriptions before the tool does anything.

Decision

You export the CSVs you already download and drop them in. Fourteen kinds are recognised from their column headings rather than their names.

Outcome

No account, no credentials, no subscription, no data on anyone else's server. The cost is a minute of exporting and no scheduled runs, which is the right trade for a tool you own.

Spend only after a free check says it is worth it

Problem

Programmatic SEO fails by writing four hundred near-identical pages at cost, then discovering the rows were thin, duplicated, or clashing with pages that already existed.

Decision

Preflight reads every row before a single call. Thin rows, near-duplicates and address clashes are flagged for free. Budgets stop a run; a resume does not pay twice.

Outcome

The expensive mistake becomes a free one. Writing fifty pages costs a few dollars because the rows that were never worth writing got caught before they were.

Measured, not claimed

What it does on a real site

Figures from a 345-page site with 1,300 queries, which is 448,500 comparisons every time the mapping is built.

Post-crawl reload, beforethree endpoints, each rebuilding the same mapping
/ three requests in parallel, contending
Post-crawl reload, aftercached tokeniser, mapping built once behind a lock
/ 217 times faster
Mapping view weight50 rows sharing one page list, not 50 copies of it
/ down from 1,340 kB
Cost to read and checkno model involved in any finding
/ writing uses your own key, with a ceiling
The hard parts

What was difficult to get right

The problems that took the longest, and what each one actually turned out to be.

Matching queries to pages with no pairs

Search Console will give you a list of queries and a list of pages, but not which query belongs to which page unless you export per page. Stemmed lexical overlap with a confidence score handles the rest, measured pairs always win over inferred ones, and a pin lets you overrule both. The lesson was to label the difference on screen rather than blend them into one number.

Absence of evidence read as absence

An export only lists pages that earned an impression, so a page that ranks for nothing looks identical to a page that was never built. Eight separate places in the codebase made that leap independently. A test now walks every file and fails if a ninth appears.

448,500 comparisons on every screen

345 pages against 1,300 queries re-tokenised the same strings three million times, and three endpoints each rebuilt the same mapping in parallel. Caching the stemmer and building the mapping once behind a lock took the post-crawl reload from 21.7 seconds to 0.1.

Keeping generation out of the analysis

It is tempting to let a model summarise a finding or guess a mapping, and every time it would have been easier. Holding that line is what makes a finding checkable, and it is why reading costs nothing. The separation had to be structural rather than a convention, or it would have eroded.

The checks

Thirty-odd checks, all of them arithmetic

Each one counts and compares. Every finding carries the rows it came from, so any of them can be verified against your own export.

Under-performing click-through for the position Searches with no page to land on Two pages competing for one search A family of pages with no hub Earned clicks last period, none this one A template dragging a whole family down AI crawlers blocked in robots.txt Content that needs JavaScript to read No self-contained answer in the opening No figures, dates or named sources Missing or duplicate H1 Heading levels that skip Very thin content High boilerplate ratio No author or review signal Missing structured data Near-duplicate pages No internal links pointing in Path buried too deep Images with no alt text Reading age above the audience An entity named inconsistently A device or country far below the rest Brand searches landing on the wrong page Questions your pages do not answer
Built

Local first. No build step.

One command starts it. The workspace is plain files on your own disk, readable without the tool. The only thing that ever leaves the machine is a page you explicitly ask a model to write.

Backend

  • Python 3.11 and FastAPI
  • Flat-file workspace, no database to install
  • Stemmer and tokeniser cached across calls
  • Mapping built once behind a lock, not three times in parallel

Frontend

  • Vanilla JavaScript modules, no framework
  • No bundler, no node_modules, no build step
  • Charts drawn as inline SVG by hand
  • The whole interface is readable source

Analysis — no model

  • Stemmed lexical matching for query to page
  • Shingled similarity for near-duplicates
  • A click-through curve by position
  • Same input, same answer, every time

Writing — your key

  • Your own API key, your choice of model per step
  • Tokens and cost recorded per call
  • A ceiling you set, and a resume that does not pay twice
  • 439 tests green before anything ships
What it costs

Reading is free. Writing is cents.

No model is involved in reading your exports, mapping queries, running the checks or building a brief, so all of that costs nothing however many times you do it. Only writing calls a model, through your own key, and you can see the sum.

Fifty pages, worked through

Reading every export, every time$0.00
Mapping 1,300 queries to 345 pages$0.00
Thirty-odd checks over all of it$0.00
A brief for any page, as often as you like$0.00
Research step, 50 pages, cheap model~$0.15
Draft step, 50 pages, better model~$2.60
Fifty pages written~$2.75

A worked example, not a price list. The real figure depends on which models you pick and how long your pages are, and the tool shows the running total per page and per model as it goes. Set a ceiling and the run stops at it.

Where the money usually goes

A seat, every month, forever

Hosted platforms bill whether you opened them or not. This bills nothing to look, and only what the model actually charged to write.

Per-word content pricing

Writing fifty location pages the usual way is a quote with a comma in it. Here it is the model's own token cost and nothing on top.

Paying twice for the same work

A run that fails halfway resumes from where it stopped. Steps already paid for are not repeated, and a changed instruction is the only thing that forces a redo.

Spending before you know it is worth it

Preflight runs over every row for free and tells you what is thin, duplicated or clashing before a single call is made.

The brief

Every page gets its own case file

Open any page and the tool assembles what it knows: how it performs, what is wrong with it, what to do, the searches it should own, and what is actually on it. Nothing in it is generated, and it costs nothing to open.

Not a score. The evidence.

Where it stands, in one line

Position, impressions, clicks, and how that compares to the rate typical for where it ranks. A page at 4.2 drawing a fifth of the usual clicks says so plainly.

A checklist you can work through

Each item is a specific instruction, not a theme. Rules of thumb are separated from counted facts and labelled, so you know which to check yourself.

The searches it should own

Every query mapped to this page, with its clicks, position, and whether that mapping was measured by Search Console or inferred by the tool.

It says what it cannot know

A page absent from an export has not necessarily been deleted; it may simply rank for nothing. The brief tells those two apart instead of guessing.

/adhd read in full 237 clicks on the table

Ranks 3.0 on 9,461 impressions for 792 clicks. 0.77× the clicks typical for that position. Five queries land here.

What is wrong
9,461 impressions and 0 internal links pointing here
No self-contained answer in the opening passage
No figures, dates or named sources in the opening
What to do
Link to this page from / and from its siblings
Open with one or two sentences answering the query on their own
Keep that passage under 50 words, above the first subheading
Add at least two specifics worth quoting
Queries this page should own
psychiatrist near me208 · pos 2.3 · measured
adhd medication management94 · pos 4.1 · measured
psychiatric nurse practitioner31 · pos 8.6 · inferred
The questions worth asking

Does it actually do the job?

Four questions a tool like this should have to answer, and what backs each answer. Every one of these can be checked against your own files rather than taken on trust.

Yes
Does it identify genuinely useful SEO opportunities?

Every finding is counted from your own rows, not scored by a model. A page ranking 4.2 and drawing a fifth of the clicks typical for that position is a fact about your export, and the impressions, the position and the curve all sit beside the claim. Findings are ordered by what they could recover, so the list starts where the clicks are.

30+ checksEvidence on every rowRanked by recoverable clicks
Yes
Does it reduce the time spent researching and planning?

A brief assembles everything known about a page in one place: how it performs, what is wrong, a checklist of specific instructions, the searches it should own, and what the crawler actually read. It costs nothing to open and nothing to open again. The work that usually means four tabs and a spreadsheet is one screen.

Free to openAssembled, not generatedMeasured and inferred, labelled
Yes
Does it prevent inaccurate, repetitive or weak programmatic pages?

This is what most of the machinery is for. Preflight reads every row before a penny is spent and flags the thin ones, the ones nearly identical to each other, and the addresses that clash with pages you already have. After writing, the brand kit is verified by counting, internal links are checked against pages that exist, and a page with a blocking issue refuses approval outright.

Preflight before spendingNear-duplicate detectionLinks verified after writingBlocking issues refuse approval
Yes
Does it make managing hundreds of pages practical?

345 pages and 1,300 queries is the shape it was built against. Filter and sort the sheet, rewrite one field across every row at once, pause a run and resume without paying twice, and paste the table again when your data moves so only the rows that changed are rewritten. The mapping view went from 1,340 kB to 55 kB specifically so a site that size stays usable.

Resync on changed dataBulk field rewritePause and resumeBuilt against 345 pages
How it compares

Most tools watch your site. This one reads your data.

Not a criticism of hosted platforms, which do several things this cannot. A different trade: you give up the integrations and keep the data, the arithmetic and the bill.

 Hosted platformSearchbench
Where your site data livesTheir serverYour machine
Cost to read and checkMonthly, either wayNothing
Every figure traceable to a row✗✓
Analysis works with no internet✗✓
Connects to Search Console directly✓✗
Scheduled, unattended runs✓✗
Rank tracking over time✓✗
Backlink index✓✗
Writes the pages, not just the list✗✓
Hundreds of pages from one table✗✓
A spending ceiling you set✗✓
Seats, tiers and account limitsYesNone

Analysis runs offline because no model is involved; crawling your pages and writing with one both need a connection. A hosted platform is the better answer if you need scheduling, rank tracking or a backlink index. This is the better answer if you want to see the arithmetic and keep the data where it is.

Questions

The things worth asking

Does it connect to Search Console?

No. You export the CSVs and drop them in, which takes about a minute. That is the trade for keeping the data on your own machine.

What does it cost to run?

Reading, checking and mapping cost nothing at all, because no model is involved in any of it. Writing pages goes through your own API key, with the running total on screen and a ceiling you set.

Will it get me ranking?

It tells you where you already rank and what is costing you clicks there. No tool can honestly promise the rest, and one that does is selling you something.

What does it need installed?

Python 3.11 or newer. No database, no node_modules, no build step. Unzip it, run one command, and open the page it gives you.

Does it publish for me?

No. Everything written waits in a library until you have read it. A page with a blocking issue refuses approval outright, and what you approve exports as Markdown or Word.

Does it work in other languages?

The arithmetic does. The word matching is English only, so query-to-page mapping will be weaker elsewhere and is worth checking by hand.

Get started

It runs on your laptop.

Unzip it, run one command, and open the page it gives you. Your exports, your machine, no account to open.