raptor link crawl.json
raptor link crawl.json --json --top 50Internal linking is the part of technical SEO most often left unexamined, because it is invisible
in any single page's markup — it only exists across a whole crawl. raptor link builds that graph
and reports what it shows.
It reads a crawl.json and performs no network I/O, so it is cheap to re-run with different
--top values.
How the graph is built
Nodes are the crawled pages, keyed by normalized URL. A page that was never crawled is not a
node — so a link pointing outside the crawl budget, or to an uncrawled URL, contributes no edge.
This matters when reading the output: a small --max-pages produces a graph of the pages you
fetched, not of the site.
Edges are internal <a href> links, with three deliberate rules:
- Destinations are resolved through redirects. A page is recorded at the URL that finally
served it, so an
hrefnaming a URL that 301s matches no node by string. Those edges are resolved to the page that served them rather than dropped. When they were dropped, the destination showed in-degree 0 — a false orphan — the source showed out-degree 0, and the component count,avg_out_degreeand every PageRank value were computed over the wrong graph. - Repeated links from one page count once. A header nav linking
/pricingfrom every page contributes one edge per page, not one per occurrence, so a site-wide nav does not drown out editorial links. - Self-links are ignored. A page linking to itself is not internal authority.
What each number means
PageRank
Raptor computes classic PageRank over the internal graph: damping 0.85, 40 iterations, rank from dangling nodes (pages with no outbound internal links) redistributed uniformly.
Read it as relative internal authority — which pages your own site's link structure pushes importance toward. It is not Google's PageRank, has no access to external links, and is only meaningful within one crawl. Comparing a PageRank value between two crawls of different sizes is meaningless; comparing the ranking within one crawl is the point.
The useful question it answers: are the pages I most want to rank actually the pages my site
links to most? A commercial site whose top five pages by PageRank are /privacy, /terms,
/careers, /blog and /about has its internal authority pointed at pages that will never earn
revenue.
Orphan pages
Pages with no inbound internal links from anywhere else in the crawl. The seed is excluded by
identity (resolved through its redirect, so a site whose / 301s to /en/ does not report its own
homepage as an orphan).
An orphan is reachable only if something outside the crawl links to it — a sitemap, an ad, an external site. Google can still find it, but it inherits no internal authority and is usually either forgotten or a mistake.
Two caveats before acting:
- A page is only an orphan relative to the crawl. If
--max-pagescut the crawl short, pages whose only inbound links live on uncrawled pages will look orphaned. Check the crawl reached the whole site before believing an orphan list. - Links injected by JavaScript are invisible to the default backend. On a JS-heavy site, run
--backend playwrightbefore treating orphans as real.
orphan_page_count carries the true total; orphan_pages is bounded by --top.
Hubs
Pages with the highest out-degree — the ones linking out the most. Usually navigation, category and index pages. A hub with high out-degree and low PageRank is a page distributing authority it does not have; a hub with both is doing real structural work.
Silos
Pages grouped by first path segment — /blog/x and /blog/y are both in the blog silo.
This is a crude proxy for site sections and is presented as such: it reflects URL structure, not
semantics or navigation. It is useful for spotting that 80% of your pages sit under one section,
or that a section you consider important has three pages in it.
silo_count carries the true number of distinct silos; the distribution list is bounded by
--top.
Components
Weakly connected components, computed with edges treated as undirected. A healthy site is normally one component: everything is reachable from everything else if you ignore link direction.
More than one component means the crawl found groups of pages with no internal links between them at all. That is usually a genuine structural problem — a section reachable only from a sitemap, a migrated subtree nobody linked back into — or an artefact of a truncated crawl.
total_edges and avg_out_degree
Total internal links (after the dedup and redirect-resolution rules above) and the mean per page.
Their main use is as a sanity check: if avg_out_degree is near zero on a site you know has
navigation, the crawl did not see the links — almost always because they are JS-injected.
Reading the output honestly
The graph describes the crawl, not the site. Every number above is conditioned on which pages
were fetched, whether links were rendered, and whether redirects resolved. Raptor bounds the lists
and reports true totals so a truncated list is visible as truncated, but it cannot tell you that
your crawl budget was too small. Check pages_crawled against what you expect the site to have
before drawing structural conclusions.