A solo GEO practice doesn’t come with an agency’s back-office tooling built in. There’s no team maintaining a rank tracker, no shared license for an AI-visibility platform, no in-house dashboard stitching a client’s numbers together at the end of the month. That tooling either gets bought, often from a vendor built for a different job, or it gets built. I built it: a set of Python command-line tools that now runs every stage of a client engagement, from the first page review through the monthly retainer report. Here’s what’s actually in it, the one dependency I let in, and the bugs that only surfaced once real data hit it.
Why Build Instead of Buy
The generic version of most of these tools already exists. It’s just aimed at the wrong target. A standard technical-SEO grader checks title length, viewport meta tags, HTTPS: useful, but not what Small Factory 5 sells. The five-lever framework (Citability, Conversational Alignment, Authority Signals, Factual Density, Structured Clarity) needs a tool that reads a page the way an AI answer engine does, not the way an old-school crawler does. Nothing off the shelf scores that.
The AI-citation tracker follows the same logic. Nothing commercially available scores the mention-versus-citation distinction the Citability lever actually needs scored, weighted toward citation because being the cited source is the stronger signal. So that got built the same way, from scratch, instead of bolted on from a tool designed for a different question.
The Discipline: Real Math, Real Verification, Real Money
The same design decision repeats across every piece of this toolkit. Anything actually measurable, a heading hierarchy, a Lighthouse score, a click count from Search Console, gets computed in plain Python. AI only gets called for the part that’s genuinely judgment: does this copy read as citable, does a fix actually recur across three differently worded page reviews, do this month’s numbers add up to a coherent story. Every one of those prompts says explicitly: never invent a number that isn’t in the data you were just handed.
I built this with an AI coding assistant, and it got held to the same rule I sell in every audit: don’t trust a thing because it compiled, test it against the real API before calling it done. None of this runs free, either. Every tool defaults to a dry run and prints its plan and an estimated cost before anything runs; nothing spends real money without an explicit flag to confirm it.
What’s Actually in the Toolkit
Seven pieces, each covering a different stage of a client engagement.
Page grader. Runs the core five-lever review. Structural checks (heading hierarchy, JSON-LD validity, image alt coverage) are plain HTML parsing with no API call and no cost. The actual judgment calls, whether the Citability and Conversational Alignment levers hold up, are one AI call per page. Re-reviewing a page that’s been reviewed before automatically becomes a re-grade, with a real score delta and a shipped/partial/not-shipped status per fix, checked against the live page as it stands now.
Rank tracker. Pulls real Google positions from DataForSEO’s SERP API, checked to a depth of 100 results instead of the default 10, so a page moving from position 45 to 22 shows up as real progress instead of “not found” on both ends.
PageSpeed integration. Adds real Core Web Vitals, averaged across three runs rather than trusted from one, since a single Lighthouse run has enough natural variance to make a one-off number misleading in either direction.
AI Visibility Tracker. Produces the Citability Index, a 0-100 score weighted 60/40 toward being cited as a source over just being mentioned by name, run three times per prompt per platform because a single AI answer isn’t a stable measurement.
Search Console traffic. Pulls real clicks, impressions, and average position straight from Google’s API, the other half of “rank and traffic monitoring” so a retainer report isn’t just reporting rank in a vacuum.
Cross-page executive summary. Reads every individual page review already on file for a client and finds what’s actually a pattern across the site, with an explicit instruction not to manufacture a site-wide pattern out of something that only shows up once.
Monthly report assembler. Reads what the other six tools already produced and writes one narrative describing what actually happened that month, instead of three sections stapled together. If a data source isn’t available yet, it says so plainly rather than quietly working around the gap.
The One Dependency
Every one of these tools is standard-library Python, no requests, no pandas, with a single deliberate exception: google-auth, needed only for the Search Console integration. Real automated access to that API means signing a JWT with RSA-SHA256, which the standard library genuinely can’t do. It’s used for exactly that one job; the actual API calls still go through plain urllib, same as everything else here. (Installing it on this machine’s older Python needed one extra step, pinning an older cryptography version before google-auth would install cleanly, now written down in requirements.txt so it’s not a rediscovery next time.)
Two Bugs Only Real Data Found
Neither of these was hypothetical. Both showed up because a tool got run against a real API with real credentials before being called done, not because a test suite predicted them.
DataForSEO’s “still processing” status wasn’t the code I expected going in. The real async queue returns status 40601 or 40602 while a rank check is still pending, not the status code that shows up in the docs for a different context entirely. Assuming the wrong code would have meant the tool reporting “done” on a task that hadn’t actually run yet.
The monthly report also managed to eat its own table. The report assembler splits an underlying markdown file into sections wherever a line reads as a bare ---. The Citability Index table has its own header-separator row that also contains ---, so the first version of that splitter cut the table off right after its header, every time, silently. The fix was splitting only on lines that are exactly ---, not any line that merely contains it. Caught the same way as the DataForSEO issue: by rendering the actual output and reading it, not by trusting that the function returned without an error.
FAQ
Why standard library only, with just one exception?
Fewer dependencies means less that can silently break on someone else’s release schedule, and less to audit for a practice that runs on one person’s time. google-auth got let in because hand-rolling RSA-SHA256 JWT signing isn’t a reasonable thing to do for one API integration.
Why test against real APIs instead of mocking them? Because both bugs above were invisible to a mock. A mock returns what you expect it to return; a real API returns what it actually returns, including a status code the docs didn’t mention. Passing mocked tests has never once meant an integration actually worked here.
Does any of this replace a human review before something ships to a client? No. Every generated report says plainly that it’s an automated first pass. The tooling produces the scorecard and the draft fixes; a human still reads the output before it reaches a client.
Written by David Cox, GEO consultant at Small Factory 5. The same standard applied to every client page review, check the actual rendered result, not just that the code ran without an error, is what this toolkit gets held to as well.
Want this applied to your site?
Send me a page and I'll tell you what's actually wrong with it — no sales call required.
Get in touch