Skip to content
Free open-source tool and custom builds

Topical Proximity Classifier: an LLM content audit tool for pruning decisions

Score how close every article on your site is to the services you sell, then decide what to keep, merge, prune and link. Run the free tool yourself, or have a custom classifier built for your site.

MIT licensed. Runs on your own OpenAI or Anthropic API key.

What is the Topical Proximity Classifier?

The Topical Proximity Classifier is an open-source tool built by Lum Kamishi that uses a large language model to judge how close each article on your website is to the core services or products you want to rank for. It reads every post against a description of your business and assigns it a relevance band, with a score and a written reason.

Core

The page directly covers a service you sell, a primary product or a primary entity your site needs to compete.

Adjacent

The page covers a supporting concept, answering an informational question that naturally routes readers toward a core page.

Distant

The page covers a subject that adds nothing to your main offering and dilutes the site's focus.

Topical proximity classifier vs keyword clustering

Keyword clustering and proximity classification solve different problems. Clustering maps search demand before you write. Proximity classification audits what you have already published.

MethodWhat it measuresInput it needsDecision it supportsBlind spot
Keyword clusteringShared URLs in search resultsA query list and SERP dataBrief planning and outlinesIgnores how existing pages relate to the business
TF-IDF content scoringTerm frequency against top competitorsOne target keyword and a draftOn-page keyword optimizationMisses relevance across different URLs
Embedding cosine similarityDistance between text vectorsText and an embedding modelRaw semantic similarityNo business or buyer-intent context
Topical proximity classifierAlignment to the services you sellPost content, a business description and a services listPruning, merging and internal linkingNeeds a careful business description and a human check on borderline posts

The output

The classifier appends seven columns to your audit CSV. Sort by tier for the keep-or-prune pass, and use the other six as the audit trail behind each call.

ColumnWhat it tells youPossible values
serviceWhich service the post supportsA service name, or NONE
service_alignmentHow directly it supports that service0.0 to 1.0
post_typeFormat and role of the postSERVICE_INTENT, PRODUCT_REVIEW, COMPARISON, INSTALLATION_GUIDE, EDUCATIONAL, DESIGN_INSPIRATION, DRIFT, OFF_TOPIC
commercial_intentThe reader's distance from a purchaseHIGH, MEDIUM, LOW
tierThe pruning bucketCORE_SERVICE, CORE_PRODUCT, ADJACENT_COMPARISON, ADJACENT_INFORMATIONAL, PERIPHERAL, OFF_TOPIC
reasoningWhy the post got this callTwo to three sentences
overriddenWhether the second pass changed the callTrue or False

How does the classifier work?

The model handles the judgment and a fixed rule set assigns the tier, so you get the model's reading of each post together with repeatable buckets.

1. It learns the business from two files

config.yaml describes what the business does, where it operates and what it doesn't offer. services.csv lists each service with a short descriptor. Both go into every prompt, so each post is judged against this business's catalog, not a general idea of the topic.

2. It reads the body as well as the title

When the audit CSV has no Content column, the script fetches each URL and extracts the main text with trafilatura, up to a set character limit. A title alone gives too little to judge from: "SunPower Maxeon Panels Review" could be a 3,000-word technical breakdown or a 400-word affiliate post.

3. It classifies each post with structured output

Each post reaches the model with the business context and six worked examples that anchor the hardest boundaries, such as product review against service intent and drift against off-topic. The model reasons first, then returns five fields: reasoning, service, service_alignment, post_type and commercial_intent. The schema is enforced through strict response_format on OpenAI and a tool_use input schema on Anthropic, so the model can't answer in free text or add fields of its own.

4. Fixed rules turn the fields into a tier

The script maps post_type, service_alignment and commercial_intent to a tier with fixed rules, so identical fields always produce an identical tier. A SERVICE_INTENT post with high or medium commercial intent lands in CORE_SERVICE, and a post that supports none of the services lands in OFF_TOPIC whatever its score.

if post_type == "OFF_TOPIC" or service == "NONE":  OFF_TOPIC
if post_type == "SERVICE_INTENT" and intent in (HIGH, MEDIUM):  CORE_SERVICE
if post_type == "PRODUCT_REVIEW" and alignment >= 0.5:  CORE_PRODUCT
if post_type == "COMPARISON" and alignment >= 0.5:  ADJACENT_COMPARISON
if post_type in (EDUCATIONAL, INSTALLATION_GUIDE) and alignment >= 0.4:  ADJACENT_INFORMATIONAL
if post_type == "DESIGN_INSPIRATION":  ADJACENT_INFORMATIONAL if alignment >= 0.4 else PERIPHERAL
if post_type == "DRIFT" or alignment < 0.3:  PERIPHERAL
# anything left:  ADJACENT_INFORMATIONAL

5. A second pass rechecks the borderline calls

Every post with an alignment between 0.35 and 0.65 goes back to the model for an independent re-check, which either confirms the first call or replaces it. The overridden column records which happened. The band covers that range because single-pass calls in the middle of the scale are the ones most likely to be wrong.

Example output

Illustrative output for a residential solar installer selling installation, battery backup, EV chargers and maintenance. An embedding audit would score these posts as close neighbors, because all six share solar vocabulary.

PostPost typeAlignment, 0 to 1IntentTierAction
How Much Does Solar Installation Cost in 2025?SERVICE_INTENT0.95HIGHCORE_SERVICEProtect and refresh
Tesla Powerwall 3 Review: Is It Worth It?PRODUCT_REVIEW0.70MEDIUMCORE_PRODUCTKeep and refresh
Monocrystalline vs Polycrystalline Solar PanelsCOMPARISON0.65MEDIUMADJACENT_COMPARISONHuman review
How Solar Panels Actually Work: The ScienceEDUCATIONAL0.45LOWADJACENT_INFORMATIONALHuman review
10 Beautiful Solar Roof DesignsDESIGN_INSPIRATION0.35LOWPERIPHERALRemove if it earns nothing
Smart Thermostats for Energy SavingsDRIFT0.10LOWOFF_TOPICRemoval candidate

Only the top two rows are safe to leave alone, and the two adjacent rows need a person to read them.

For a post on Powerwall installation cost, the reasoning reads: the post covers labor, permits and panel integration, which are core to the battery backup service, and the reader is a homeowner deciding whether to buy, so a quote request is the likely next step.

How to run the Topical Proximity Classifier

You need Python 3.10 or newer and an API key from OpenAI or Anthropic. Install into a virtual environment so the tool's dependencies stay out of your system Python.

1. Install

git clone https://github.com/lumkamishi/topical-proximity.git
cd topical-proximity
python3 -m venv .venv
source .venv/bin/activate        # Windows: .venv\Scripts\activate
pip install -e .

2. Set your API key

export OPENAI_API_KEY="sk-..."
# or
export ANTHROPIC_API_KEY="sk-ant-..."
# Windows PowerShell: $env:OPENAI_API_KEY="sk-..."

3. Describe the business in config.yaml

Write the description the way you would brief a strategist, and state what the business doesn't sell as plainly as what it does, because exclusions drive the DRIFT and OFF_TOPIC calls. A solar installer that leaves out "we don't sell backyard solar lights" can see "Best Backyard Solar Lights" filed as a core product post.

4. List the services and the posts

services.csv holds one row per service, and audit.csv holds one row per post. URL is required, while Title, H1 and Meta Description are recommended. If a Screaming Frog crawl already gave you the body text, add it as a Content column and the fetch step is skipped.

5. Run it

topical-proximity \
  --config config.yaml \
  --services services.csv \
  --audit audit.csv \
  --output topical_proximity.csv

Model choice and cost per 500 posts

Provider and model are switched with two lines in config.yaml. These estimates from the project README include the verification pass:

ProviderModelCost per 500 posts, USDRuntime, minutes
OpenAIgpt-5.43 to 55 to 15
Anthropicclaude-opus-4-78 to 1210 to 20
Anthropicclaude-sonnet-4-63 to 55 to 15
Anthropicclaude-haiku-4-51 to 25 to 10

Haiku suits a first pass on a large blog, while Sonnet or gpt-5.4 give better reasoning text for a client deliverable. Setting fetch_content: false and verify_alignment_range: [0, 0] roughly halves the cost, although accuracy drops with both.

Turning tiers into prune and link decisions

The classifier measures topical fit only. Before any removal, join its output with twelve months of Search Console clicks, GA4 key events and referring domains.

  • CORE_SERVICE and CORE_PRODUCT: keep, refresh the outdated ones, and never remove one without a review.
  • ADJACENT_COMPARISON and ADJACENT_INFORMATIONAL: read the reasoning column, then decide by hand.
  • PERIPHERAL and OFF_TOPIC: remove those with no clicks, conversions or links first, and redirect linked ones to the closest relevant page.

The tiers also give you an internal linking plan. Adjacent posts should link up to the core page for the service named in the service column, and links into removed posts should be taken out so crawl paths lead only to pages you keep. Allow two to three months before judging the result, and check again after the next Google core update.

Topical authority builds through that tighter focus. Topical map tools plan which pages to create, and this classifier decides which existing pages still belong in the map. The full method is in my content pruning guide.

What a custom topical proximity classifier costs

A custom classifier build is a fixed-scope technical project to clean up content debt and resolve cannibalization across a large URL inventory, built around your own topic map.

OptionPrice, USDWhat you getBest for
Free audit$0An initial assessment of content debt and indexation problemsChecking the baseline structural issues
Hands-on teardown$997A manual diagnostic of the site architecture with graded findingsSites that need an action plan now
Custom topical proximity classifier build$1,479The full classification pipeline, a topic map, a pruning roadmap and the codeDomains with hundreds of URLs that need a systematic cleanup
Typical agency engagement$5,000+A monthly retainer covering mixed SEO tasksOngoing execution across off-page and content

What a custom build delivers

  • Topic map file: a structured taxonomy of your core commercial subjects, the concepts that support them, and the topics you explicitly don't cover.
  • Per-URL proximity scores: every crawled URL with its alignment score, relevance band and primary entity label.
  • Cannibalization report: the URLs that target the same topic and compete against each other in Google.
  • Pruning roadmap: a spreadsheet listing which URLs to delete, which to redirect and which to rewrite.
  • Internal link plan: source URLs, target URLs and vetted anchor text for each link.

For a home renovation client, we built a three-layer Python pipeline that combined search-value scoring, keyword cannibalization detection and GPT-based topical proximity classification, and produced a YAML topic map and a content pruning roadmap.

Every finding is prioritized by impact and effort, with high-impact cleanup listed first so your editors and engineers can start on the structural problems straight away. You also get the full pipeline code at delivery, so your team can rerun it against new pages and keep editorial work inside your strategic scope.

Honest answers about the Topical Proximity Classifier

Who is it for?

SEO consultants running client blog audits, in-house SEO leads at content-heavy businesses, and agencies selling fixed-scope audits who need keep-or-prune calls across hundreds of posts.

Which sites does it suit?

The taxonomy assumes a service or product business, such as an installer, law firm, contractor or SaaS company with a service layer. Affiliate sites and pure publishers aren't covered yet, and pull requests with taxonomies for them are welcome.

How accurate is it?

Accuracy depends on the business description and the services list. Test it on 20 to 30 posts you already have a firm view on, read every reasoning column, and tighten the description until the calls match yours.

Is my content sent anywhere?

Each post's text goes only to the OpenAI or Anthropic API you configure, under that provider's API terms.

Does it use traffic or backlink data?

No. It reads content and business context only, so pair it with Search Console and a backlink tool before deciding anything.

Does it replace human review?

No. It handles the mechanical sorting, which leaves your reading time for the adjacent and borderline rows.

How is the custom build different from the free tool?

The free tool classifies posts against a services list you write yourself. A custom build adds a topic map built for your site, cannibalization detection, a pruning roadmap and an internal link plan, all prioritized by impact and effort.

Do I keep the code from a custom build?

Yes. You receive the full pipeline code at delivery, so your team can rerun it whenever new pages are published.

What do you need from me for a custom build?

Read access to Search Console, a crawl export of the site, and a clear list of what the business sells and doesn't sell. That list becomes the starting point for the topic map.

Hire me to run the content prune

My content audit scores every page on traffic, topical fit and quality, then hands you a keep, merge and remove list with a reason for each call. It draws on 11+ years of technical SEO on large sites.

See the content audit