Fetch any page and analyze its on-page anchor text. It classifies every <a href> into one of 7 anchor types, scores the mix against safe ranges, flags over-optimization patterns Google penalizes, and breaks links down by scope (internal/subdomain/external) and follow status.
This analyzes the anchors on a page you point it at, not a site's inbound backlink profile. It's a fast way to sanity-check the internal-linking and outbound-anchor pattern of a page you control or are auditing.
Built and maintained by Mojo Links, a link-building and semantic SEO agency. Hosted, no-install version: mojolinks.com/tools/anchor-text-checker.
Each type has an independent safe range (they're per-type guidance, so they do not sum to 100%):
| Type | Safe range | Example |
|---|---|---|
| Branded | 30-50% | Mojo Links |
| Exact Match | 2-5% | link building service |
| Partial Match | 10-15% | best link building for agencies |
| Generic | 15-25% | click here, read more |
| Naked URL | 10-15% | mojolinks.com |
| Brand + Keyword | 5-10% | Mojo Links link building |
| LSI / Semantic | 5-15% | backlink acquisition |
Requires Node.js 18+.
# run without installing
npx anchor-text-checker https://example.com/post
# or install globally for the `anchor-check` command
npm install -g anchor-text-checker# defaults: brand inferred from the domain, no target keywords
anchor-check https://example.com/post
# supply a brand and target keywords for accurate exact/partial buckets
anchor-check example.com --brand "Acme" --keywords "widgets, blue widgets"
# machine-readable output
anchor-check https://example.com --json > report.json$ anchor-check https://example.com --brand "Acme" --keywords "widgets"
Verdict: OVER-OPTIMIZED
This profile shows over-optimization signals. Exact-match anchors are above
the safe range, the pattern Google penalizes most.
TYPE COUNT YOURS SAFE IDEAL STATUS
----------------------------------------------------------
Branded 8 22% 30-50% 29% v
Exact Match 9 25% 2-5% 3% ^
...
Issues (2):
[CRITICAL] Exact-match anchors above the safe range
Exact-match anchors are 25% of the profile, above the 2-5% safe range...
| Flag | Description |
|---|---|
--brand <name> |
Brand name for the branded / brand+keyword buckets. Defaults to the domain (example.com -> Example). |
--keywords <list> |
Comma-separated target keywords for the exact / partial buckets. |
--json |
Print the full result object as JSON. |
-h, --help |
Show help. |
The scoring engine is pure and network-free; the fetch/extract layer is separate:
import { analyzeAnchors, deriveBrand } from 'anchor-text-checker'
import { toSafeUrl, fetchHtml, extractLinks } from 'anchor-text-checker/src/fetch.js'
const url = await toSafeUrl('example.com')
const { html, finalUrl } = await fetchHtml(url)
const links = extractLinks(html, finalUrl)
const result = analyzeAnchors(links, { url: finalUrl.toString(), ...deriveBrand(finalUrl.hostname), keywords: [] })The fetcher follows redirects manually and re-checks every hop against an SSRF guard that rejects hostnames resolving to private, loopback, link-local, CGNAT, or cloud-metadata ranges. It caps the body at 2 MB and times out at 8 seconds. Only http/https pages are fetched.
- Anchor classification is heuristic. Without
--keywords, exact/partial buckets can't be detected, the tool will still classify branded, naked, generic, and LSI. - Scope uses naive last-two-labels domain matching (no full public-suffix list), which is fine for typical domains.
- It reads server-rendered HTML; links injected by client-side JavaScript after load won't be seen.
MIT.