The full upstream README, mirrored here for reference. Install config, tool schemas, adoption signals, and an original overview live on the Geo Score listing page.
Can AI engines cite your site? A free 0–100 score from one command. Do they? Check through their APIs with your own keys.
Score (free, no key) → Ask one question (your API keys) → Watch a question set every week (your API keys). Three levels, one tool · the citation half is never added to the score.
Recorded with geo-score 1.1.0 on 2026-09-09. A run with 1.2.0 on 2026-09-27 read 72.
One command. Every check, and what the next tier needs. The report says how long its run took.
--explain adds the evidence)geo_score.py stripe.com, recorded with geo-score 1.5.0 on 2026-10-03 through an HTTPS proxy (the footer's time includes it). The image above is an earlier run, with 1.1.0. From 1.7.0 the report also prints three observations under Outside the score after the checks (robots.txt rules page by page, links in the server HTML, hreflang), which never move the score; this recording predates them. See cli/README.md.
Python 3.8+, standard library only, nothing to install. It reads public URLs and prints a score against a published, versioned rubric — not a black box.
See how 324 well-known sites score → · a quarter of them are unreadable to AI crawlers.
GEO means Generative Engine Optimization — getting cited by ChatGPT, Perplexity, Google AI Overviews, Gemini and Copilot. Nothing to do with geography or maps.
Contents: MCP server · Three levels · What it checks · Leaderboard · Ways to run it · Does AI cite you? · Scope · How accurate is it? · Install and stability · Repository layout · Contributing
geo-score-mcp is a local MCP server (stdio, Python standard library only). It gives an agent the
same three levels as the CLI: score a site for free, ask the AI engines one question, and track a
question set week over week.
The buttons and the lines below run geo-score 1.8.2 from PyPI with uvx, which needs only uv: no git and no build step. They add no keys of their own, but a server started from a shell that exports a provider key (Claude Code passes its environment on) can use it. For a server that cannot spend, add --read-only after mcp.
Claude Code
Standard config (no keys in the file) for any client that reads an mcpServers file:
Claude Desktop does not read your shell's PATH: put the full path that which uvx prints in command.
Without access to PyPI, the release wheel: uvx --from https://github.com/jianruntech/geo-score/releases/download/v1.8.2/geo_score-1.8.2-py3-none-any.whl geo-score-mcp
From source (needs git): uvx --from git+https://github.com/jianruntech/geo-score@v1.8.2 geo-score-mcp
| Tool | Level | Cost | Writes | Hints | Args |
|---|---|---|---|---|---|
score_site | 1 | Free | No | read-only, open-world | url, urls, sample |
ask | 2 | Paid (your keys) | No | non-destructive, open-world | question, engines, brand, domains |
run | 3 | Paid (your keys); dry_run is free | A run file under .geo-score/watch/runs/, next to the config | non-destructive, open-world | dry_run, engines, limit, max_calls, budget_usd |
list_runs | 3 | Free | No | read-only, idempotent | — |
report | 3 | Free | No | read-only, idempotent | run, since, format |
diff | 3 | Free | No | read-only, idempotent | from, to, format |
status | — | Free | No | read-only, idempotent | — |
Resources: geo-score://rubric/v1.1.
Prompts: audit_site (url), check_citations (question, brand), weekly_watch, compare_runs (from, to), explain_check (check_id).
Bold arguments are required. Hints are the tools' MCP annotations: clients may use them to decide what to confirm, and they are hints, not guarantees. Only ask and run can spend money, and only with the provider keys you give the server.
Things to ask your agent:
| Prompt | What it calls |
|---|---|
| Score https://acme.com for AI-search readiness and list the 3 checks with the most points to gain. | score_site with url |
| Dry-run my tracked questions and tell me the planned calls and caps. | run with dry_run: true (level 3, needs a config) |
| Compare my last two watch runs: which engines changed, and is any change outside the noise? | diff (defaults to the latest two runs) |
Keys and config. score_site needs no key. ask and run read the provider keys
(OPENAI_API_KEY, PERPLEXITY_API_KEY, GEMINI_API_KEY, ANTHROPIC_API_KEY, OPENROUTER_API_KEY)
from the server's environment, which is not your terminal's: a desktop app does not see what you
exported in a shell. The level 3 tools also need a geo-score-watch.json. Pass it as
-c /absolute/path/geo-score-watch.json, because a GUI client does not start the server in your
project. From 1.4.0, --read-only gives a server that cannot spend anything.
Setup for each client (Claude Code, Claude Desktop, Codex, Cursor, VS Code, Gemini CLI, Devin Desktop, Zed), the Claude Code plugin, timeouts, security and troubleshooting: guide/mcp.md.
| Level | What it answers | Needs | Command |
|---|---|---|---|
| 1 · Score | Can AI engines reach, parse, trust and cite the site? 0–100 against the open rubric | Nothing: no key, no install | geo_score.py stripe.com |
| 2 · Ask | Right now, for a question you care about, do they cite it? | An API key for any of OpenAI, Perplexity, Gemini, Anthropic, OpenRouter | geo_score.py stripe.com --ask "best payments API for marketplaces" |
| 3 · Watch | How often are you cited, against which competitors and sources, week over week? | Your API keys and a fixed question list | geo_score.py watch run |
Level 1 is the score. Levels 2 and 3 measure the outcome the rubric keeps out of the score on purpose (two scores, never one): they are reported next to the 100 and never summed into it. A site can score 90 and still lose every answer to a competitor with more third-party coverage, and the reverse also happens, which is why you want both.
Levels 2 and 3 need cli/geo_watch.py next to cli/geo_score.py. The package on PyPI has both:
uvx geo-score@1.8.2 example.com runs it without installing, and pipx install geo-score==1.8.2 gives you
a geo-score command. A clone, or both files downloaded, works too. The one-line curl | python3 above
runs level 1.
Classic SEO asks where do I rank. Answer engines don't rank — they retrieve passages, decide whether a source is worth quoting, and cite it. Different question, different failure modes: a site can sit at position 3 on Google and never be quoted. Because engines retrieve passages, this rubric gives Content Citability the most weight. That is a design choice, not a measured effect: the evidence for how much passage shape matters is thin, and the evidence table says where.
Most of what the rubric checks is mechanical and cheap to fix — a robots.txt line, a
JSON-LD block, a date in a template, a paragraph rewritten so it stands on its own. The
hard part is knowing which of them you are missing, and what each one is worth.
21 tiered checks totalling 100 points, plus 4 bonus checks worth up to +6 outside the denominator. Full specification: rubric/v1.1.md · 简体中文
| Pillar | Pts | Asks |
|---|---|---|
| Reachable — gates | 15 | Can a retrieval crawler get the page at all? robots.txt, live reachability across 10 AI user-agents, server-rendered content |
| Understandable | 22 | Can it tell what the page and the company are? Organization + WebSite, llms.txt, sitemap, breadcrumbs, page-type schema |
| Content Citability | 35 | Is there anything here worth quoting? Self-contained answer passages, headings that match how people ask, sourced figures, real bylines, freshness |
| Brand Credibility | 18 | Why should an engine trust it? Knowledge-graph entity, third-party listings, sameAs that resolves, video presence |
| Answer Fit | 10 | Is the content shaped to be lifted into an answer? |
Content Citability carries the most weight by design: answer engines retrieve
passages, not domains. Whether passage shape outweighs domain authority is not
established: the heaviest single check, p2.answer-passages, rests on thin support
(where the evidence is thinnest).
Every scored check is tiered — 2 to 4 tiers, each naming a count out of the 8 sampled pages, so two people scoring the same site agree on the arithmetic. Three checks are gates: score zero on crawler access, live reachability or server-rendered content and the result caps at 40, because until a crawler can reach the content nothing else you change has any effect.
| 0–30 | 31–50 | 51–65 | 66–82 | 83–100 |
|---|---|---|---|---|
| Not started | Early | Growing | Solid | Leading |
Band names describe a stage, not a verdict. External benchmarks put most business sites in the 30–55 range, so a score in the forties is ordinary, not alarming. In this repository's own benchmark, 85% of 324 sites reach Early and 6% reach Leading (where the cuts fall).
A quarter of them are unreadable to AI crawlers. 73 sites have a gate check at zero — an
AI retrieval crawler cannot get the content, so it has nothing of theirs to quote. 20 block AI crawlers by name in
robots.txt, which is an editorial choice and reported as such — amazon.com lands at 17
for exactly this reason. 41 serve a page whose body only exists after JavaScript runs.
Their content is there, a browser sees it, and a crawler gets an empty shell. That group
almost certainly did not choose it. At a further 12 the server refuses crawler user-agents (unverified
probes from the auditor's address).
Median 55 (95% CI 51–58, stats.py). Range 11 to 96. 66 sites were scored on fewer than four pages; without them the median is 59.
| Site | Score | Band |
|---|---|---|
| resend.com | 96 | Leading |
| pulumi.com | 94 | Leading |
| supabase.com | 93 | Leading |
| elevenlabs.io | 91 | Leading |
| lumalabs.ai | 90 | Leading |
| … | ||
| qcloud.com | 12 | Not started |
| mercadolibre.com | 12 | Not started |
| keepa.com | 11 | Not started |
The full table, by sector → · markdown · raw data · CSV · every site's full report · re-run it
Two more findings worth the click. Sites in the five China sectors score 22 points lower than everyone else (95% CI 16–30; median 36 against 58; earlier samples measured with 1.1.0 put the gap between 16 and 23 points). Grouped by the language a site serves instead, the 54 Chinese-language sites score 26 points lower than the 270 others (95% CI 22–32), and within matched sector families the gap runs from -8 to 27 points. Counted check by check in raw points, 69% of the gap sits in checks the CLI reads with a heuristic, so at most that share could be the tool misreading Chinese pages (the breakdown). The two largest per-check differences, question-shaped headings and self-contained answer passages, are heuristics the CLI also read lower than a human on the one Chinese site in the hand-audit comparison, so part of the gap may be the tool reading Chinese pages conservatively. From 1.5.0 the CLI reads each page in its own language and counts Chinese task, explanation and comparison headings; the leaderboard above was measured with 1.5.0, so its figures include that change. And statistics that carry a source and a named, verifiable byline are among the three largest gaps on more than half the sites.
Every number here is reproducible with the command at the top of this page (the benchmark was run with 1.5.0 on 2026-10-02) — and we measured how reproducible. On 2026-10-12, 1.8.0 scored the whole list three times from one place the same day (the third run re-reading the first run's pages): of the 317 sites scored every time, 99% landed within ±5 between the first two runs and 81% scored identically (ICC 0.99, SEM 1.9; REPRODUCIBILITY.md, derived by benchmark/retest.py). An older 1.1.x build, measured on 2026-09-09, put 96% within ±5 and 45% identical. Read one site's score as ±5 rather than as exact; medians are stable. Most of what still moves is the knowledge-graph lookup, which went unanswered on most sites from that vantage, and the gate checks of sites whose bot protection answers a crawler differently from one run to the next. The band, the control experiment and the per-site pairs are in benchmark/REPRODUCIBILITY.md.
For five reference sites we also publish hand-scored audits covering all 21 checks, with the evidence behind each one: examples/audits/v1.1/.
CLI — level 1 is one file with no dependencies, and every report records how long its run took
(elapsed_s). Levels 2 and 3 add cli/geo_watch.py from the same release. With the package from PyPI,
uvx geo-score@1.8.2 (or geo-score, after pipx install geo-score==1.8.2) takes the place of
python3 cli/geo_score.py below, all three levels included.
GitHub Action (level 1) — score on every push, fail the build when it regresses. For level 3 on a schedule, see examples/ci/watch-weekly.yml.
A gate at zero caps the score at 40: a site that would otherwise score 40 or more reads exactly 40 and passes fail-under: 40. fail-on-gate: true fails any capped site.
Claude Code skill — the CLI measures what a static fetch can see. Three checks need off-site search or human judgement, and the skill does those too.
npx skills add runs the open skills CLI. It finds one skill in this
repository, geo-score, and copies the whole repository (about 9 MB) as it is on the default branch, main,
into .claude/skills/geo-score for Claude Code. Without Node.js, or for the release rather than main, clone the
release tag into your own skills folder instead:
git clone --branch v1.8.2 --depth 1 https://github.com/jianruntech/geo-score ~/.claude/skills/geo-score.
The CLI leaves those three checks out of the denominator rather than guessing (and a fourth,
p4.cn-engines, unless the site serves Chinese). Re-scoring
the five published hand audits on the same pages, it matched the auditor's tier on 80% of
84 check pairs (92% where it reads a rule, 69% where it approximates a judgement). On the checks
both scored it read 2.4 points lower on average (per site from 12 lower to 10 higher; mean absolute
difference 8.8). Its full normalised score read 5.2 points lower on average, from 16 lower to 7
higher, because the hand audits also score p3.listings, p3.mentions and p4.question-coverage,
which the CLI leaves out. n=5, all of them calibration sites:
VALIDITY.md lists every disagreement.
MCP server (all three levels) — uvx geo-score@1.8.2 mcp from PyPI (geo-score-mcp after a pipx
install), or python3 cli/geo_score.py mcp from a clone, for Claude Code, Cursor and other agents: see
MCP server.
--ask and watch put the questions your buyers ask to ChatGPT, Perplexity, Gemini and Claude,
through each provider's search-enabled API with your own keys, and record who the answers cite:
you, your competitors, or the third-party pages (forums, review sites) the engines lean on
instead. Keys are read from the environment; geo-score never writes them anywhere.
Level 2 asks each engine each question once and prints the result under the readiness
report (and into the report's citation object with --json). One ask is an anecdote: use it to
see what the engines say today, not to measure a rate.
Level 3 keeps a fixed question list, saves every answer under .geo-score/watch/runs/
(schema), and reports:
| Meaning | |
|---|---|
| cited | The answer links to a URL you own: one of your domains (subdomains included), or a url_prefixes entry such as your Amazon store or GitHub org |
| rank | Your position among the distinct domains the answer cites. Rank 1 means you were the first source |
| mentioned | The answer names you, one of your aliases or your domain. Chinese, Japanese and Korean names match anywhere; all other names match whole words only |
| share of voice | The same two rates for each competitor, over the same answers |
| sources | The third-party domains cited most often: the pages the engines trust in your category |
| answer share | How much of each answer your pages support (supported_share): by characters, by sentences and by position-weighted characters, read from where the provider says each citation sits. Printed beside the cited rate, never merged into it; an answer whose citations carry no position is not measured |
| sources listed and used | Perplexity and Gemini (generate_content) return sources beside the answer and mark the ones the text uses: sources listed and used per answer, used over listed, and the answers that list and use a URL of yours. APIs that return one list are not measured |
| repeat consistency | When a run asks a question more than once on an engine: how far the cited domains agree between asks (Jaccard, the median over questions per engine), and the least consistent questions. Descriptive only; watch diff does not read it |
| gaps | Questions where a competitor is cited and you are not, in any answer |
| searches | The searches the engine actually ran before answering, where the API exposes them (OpenAI, Gemini, Claude). This is the query fan-out, observed rather than guessed. The report counts how many contain a year or a ranking or comparison word, and says so when answers searched but the endpoint returned no query text |
| ledger | Questions asked, API requests, tokens, searches and cost. Every run keeps its own ledger |
What it will not tell you.
diff calls
something a change only when an exact paired test on the questions both runs answered,
Holm-corrected across engines, says so (p < 0.05). Too few shared questions (under 6, or too few
for the number of engines compared) is reported as too few to tell. The method: guide/watch-methodology.md.watch flags them when it runs, keeps
them out of the headline rate, and reports them on their own line with their own count. An
optional branded column in queries.csv corrects the flag by hand.Engines. Pin the model you mean in the config and keep it fixed between runs; diff flags a
run where the model changed. OpenRouter covers hundreds of models with one key.
| Engine id (default) | Provider | Key | Default model | What counts as cited | Searches shown | Cost reported |
|---|---|---|---|---|---|---|
chatgpt-api | OpenAI Responses API + web_search | OPENAI_API_KEY | gpt-6-luna | url_citation annotations | yes | no, set prices |
perplexity-api | Perplexity Sonar | PERPLEXITY_API_KEY | sonar | numbered sources the answer uses | no | yes |
gemini-api | Gemini Interactions API + google_search | GEMINI_API_KEY or GOOGLE_API_KEY | gemini-3.8-flash | url_citation annotations | yes | no, set prices |
claude-api | Anthropic Messages + web_search tool | ANTHROPIC_API_KEY | claude-sonnet-5 | citations on the answer text | yes | no, set prices |
| any id you choose | OpenRouter + web plugin (the model answering over OpenRouter's search, not the vendor's own) | OPENROUTER_API_KEY | openai/gpt-6-luna | url_citation annotations | no | yes |
Caps and cost. max_calls is an exact cap on questions asked in a run, and a plan that
exceeds it refuses to start. budget_usd is checked before every call against the spend so far
plus the most expensive call seen on that engine, so it can be exceeded by at most one call per
engine; with a budget set, engines whose cost cannot be known are left out unless you pass
--allow-unpriced. Every run keeps a ledger of requests, tokens, searches
and cost. Configuration, prices and all commands: cli/README.md.
From an agent. Levels 2 and 3 are also MCP tools, and an agent can only tighten your caps, never loosen them: see MCP server.
Every week. Citation rates move slowly and noisily: run on the same weekday with the same
questions and models, and read diff, not single runs.
examples/ci/watch-weekly.yml does it on a schedule with keys from
repository secrets and commits each run, so the history lives in git. Run files contain your
questions and the full answers: use a private repository if they are confidential.
Run it yourself, or have it run for you. Everything here is MIT; the tool has no paid edition. You pay your model providers directly and the ledger shows what each run used. What needs people rather than an API, Jianrun does as a service:
| Run it yourself (free) | Run by Jianrun | |
|---|---|---|
| Channel | Provider APIs with search | APIs and the consumer apps, sampled by hand each week |
| Engines | OpenAI, Perplexity, Gemini, Anthropic, anything on OpenRouter | ChatGPT with search, Perplexity, Gemini, Google AI Overviews, Copilot; Chinese engines when you sell into China |
| Report | Text, Markdown, CSV, JSON | A weekly report with the AIV dashboard: citation trend, per-engine rates, facts AI gets wrong about you, readiness history |
| When citations drop | Out of scope | We do the fixing |
| Price | Your API bill | AEO delivery system, from US$7,790 per quarter (US$30,590 paid yearly), AIV dashboard included. See pricing |
Five questions people ask, and where this repository answers each one:
| Question | Answer | Record |
|---|---|---|
| Score the same site twice: how far apart? | A same-day retest with 1.8.0 on 2026-10-12: 99% of 317 sites within ±5 and 81% identical (ICC 0.99) | REPRODUCIBILITY.md |
| Can anyone recompute it? | Yes: every check, tier and weight is published, so the same pages give the same score whoever runs it | rubric/v1.1.md |
| Does it match a person reading the same rubric? | 80% of 84 check pairs land in the same tier; only 5 sites, all of them calibration sites | VALIDITY.md |
| Do the weights decide the ranking? | With equal weights, or 300 random perturbations of them, the rank correlation stays at 0.99 or above | SENSITIVITY.md |
| Are higher-scoring sites cited more often by AI? | The preregistered test's first wave (2026-10-12 to 14; 16 categories, 83 sites, 1,280 answers from one engine): no association was detected (T = 0.07, p = 0.51); the design could detect a T at least as large as 0.29. Not detected is not the same as none | RESULTS-wave0.md, PREREGISTRATION.md |
A score you cannot audit is a number someone made up. So the specification is the product, and the tools are implementations of it:
71 (v1.1) is a claim; 71 is not.rubric/v1.1.json with stable check ids, and schema/report.v2.json so results from different implementations are comparable.Implement it in your own stack, disagree with a weight, open a rubric proposal. That is the main thing we want contributions on.
This is the part most tools leave out, so it's stated plainly.
AIV Score measures. It does not fix.
| Not included | Why |
|---|---|
Fix templates — robots.txt, JSON-LD blocks, llms.txt boilerplate | Remediation is where the actual work and judgement live. It is a separate, non-open project |
| Content rewriting — how to shape a passage so it gets quoted | Same |
| Per-engine tactics — what to do differently for Perplexity vs Gemini | Same |
| A remediation roadmap | Same |
Other honest limits:
The weights are opinionated but not invented. The two findings that most shaped them:
p2.sourced-stats (rubric/evidence-v1.1.md).p1.llms-txt).Every check declares what its weight rests on (published research, a vendor's own
documentation, our field observation, or a convention no engine has confirmed using), with
sources and the date they were last verified: rubric/evidence-v1.1.md.
Where the evidence is thin (p2.answer-passages, p2.question-intent, p1.llms-txt), the table
says so. If you have evidence that a weight is wrong,
open a rubric proposal — that is the
main thing we want contributions on.
Deliberately naming what this is not, so you can pick correctly:
| Project | What it does | Relationship |
|---|---|---|
| llms-txt | The llms.txt specification itself | AIV checks for compliance with it |
| yao-geo-skills | 21 categorized GEO skills, execution-oriented | Complementary — they do production, this does measurement |
| GEOFlow | Full GEO operations system for company sites | Much larger scope; AGPL |
If you need remediation and not just a score, those projects overlap with the part this repo deliberately excludes.
curl line above, pinned to a release). For
a geo-score command with all three levels, install the package from PyPI:
pipx install geo-score==1.8.2. To run it without installing: uvx geo-score@1.8.2 example.com
(uvx geo-score@1.8.2 mcp is the MCP server). Neither needs git or a build step.
Without access to PyPI, install the same wheel from the release:
pipx install https://github.com/jianruntech/geo-score/releases/download/v1.8.2/geo_score-1.8.2-py3-none-any.whl.
From source (needs git): pipx install git+https://github.com/jianruntech/geo-score@v1.8.2.
The Claude Code skill: npx skills add jianruntech/geo-score (-g for a global install), or a clone into
~/.claude/skills/geo-score (Ways to run it).
Standard library only, whichever you pick. Release downloads carry a SHA256SUMS file, and the wheel and
sdist on PyPI match it byte for byte (how to check).sameAs profiles, logo,
sitemap), and Wikidata and Wikipedia search; levels 2 and 3
call only the AI providers whose keys you set.score_site connects only to public addresses, and keys never reach a file. Threat model and reporting:
security policy.Why did my score move 4 points? Each run samples up to 8 pages, and the sample changes; in a
same-day retest with 1.8.0, 99% of sites stayed within ±5 (REPRODUCIBILITY.md). To compare before and
after a change, re-score the same pages with --baseline last.json, which also prints what changed,
check by check (--urls-from last.json re-scores them without the comparison).
Why does a check show —? It was not measured, so it left the denominator instead of scoring 0.
More answers: troubleshooting. Every term the README, the reports and the guides
use is defined in the glossary; every flag, exit code and watch command is in the
CLI reference (简体中文).
| Path | What is there |
|---|---|
cli/ | geo_score.py (level 1: one file, no dependencies), geo_watch.py (levels 2 and 3), their tests and the CLI reference |
rubric/ | The rubric (v1.1 and its JSON), the evidence behind each weight, calibration, band analysis and open questions |
benchmark/ | The 324-site leaderboard: results, one raw report per site, the preregistration, reproducibility and validity records |
docs/ | The GitHub Pages copy of the leaderboard, llms.txt and the badge at the top of this page |
guide/ | MCP setup, troubleshooting, the glossary, watch methodology and the stability policy |
reference/ | Background the checks rely on: known AI crawlers, and which sources each platform draws from |
schema/ | JSON Schemas for reports, diffs, errors and watch files |
examples/ | A sample report, the published hand audits and CI workflows |
action.yml, action/ | The GitHub Action |
SKILL.md, .claude-plugin/, gemini-extension.json, mcpb/, server.json, Dockerfile | Packaging for Claude Code, Gemini CLI, MCP clients, the MCP registry and Docker. They stay at the top level because the installers look for them there |
scripts/ | The repository's checks (scripts/check.sh runs all of them) and the release script |
.github/ | Contributing, support, security, code of conduct, the release process, issue forms and the CI workflow |
AGENTS.md | A map of the repository for coding agents (CLAUDE.md points to it) |
Built and maintained by Jianrun Tech (见润科技), Shenzhen. We run GEO and AI-adoption programs for companies in China and abroad: B2B exporters, software companies, professional services, local businesses, education providers, and companies going global, cross-border e-commerce included. The rubric came out of client work and out of optimizing our own products; publishing it is how we'd like AI visibility to be measured consistently, including by people who never become our clients.
Commercial use of this repository is unrestricted under MIT — including inside paid consulting work. You do not need our permission, and there is no separate commercial licence.
The most valuable contribution is evidence about the weights. See the contributing guide. Which issue form takes a wrong score, a disputed leaderboard row or a tool that does not work: getting help.
If you reference the rubric in research or a report, see CITATION.cff.