How We Used Jev to Automate Internal Linking (Real Costs and Results)
We used TypeSafe's Jev decision model to find internal links paragraph by paragraph. The pipeline, the rules, the bugs we hit, and what it costs to run.
TL;DR
- We built an automatic internal linking pipeline around Jev, a decision model from TypeSafe on OpenRouter. It reads every paragraph on a site and decides which page, if any, a reader needs next.
- Jev never writes text. It answers five structured questions per paragraph with probabilities, and plain rules decide which links actually get made.
- A cheap writing model picks the anchor text, copied word for word from the paragraph. Our first version lost two thirds of its anchors to one API setting.
- A full 300-page run now costs about 30 cents. You can run it yourself with the open-source repo or inside the DataWise internal linking tool.
Why is internal linking still worth automating?
Internal links are how search engines and readers find the pages you care about. Google states that “every page you care about should have a link from at least one other page on your site” and that it uses links “to find new pages to crawl” (Google Search Central). Most sites fall short of that on pages nobody has looked at in months.
The work itself is tedious. On a 150-page site, finding every paragraph that mentions a topic you have a full guide on means rereading thousands of paragraphs. Keyword-matching plugins speed that up, but they link on exact phrases, not on whether the link actually helps the reader. That gap is what we wanted to close.
What is Jev, and why use a decision model instead of a chat model?
Jev is a “System One” model from TypeSafe, available on OpenRouter. Instead of generating text, it returns “the selected option, a probability for each option, and a confidence value” for questions you define (OpenRouter Jev documentation). It answers two kinds of question we needed: Choice (which option?) and Noul (does this condition hold?).
That shape fits internal linking far better than a chat model. A chat model asked “should this paragraph link anywhere?” gives you a paragraph of reasoning you then have to parse. Jev gives you a pick and a number, and the number is what lets you set thresholds. It is also cheap: you pay per input token and “output tokens are free” (same source).
How does the pipeline work?
The pipeline has four steps, and only two of them use a language model:
- Crawl. Read the sitemap, fetch up to 300 pages, and keep every paragraph and list item of 25 words or more. Navigation, headers and footers are stripped out.
- Match. An embedding model scores how close each paragraph is in meaning to each page on the site, and keeps the 4 best candidates per paragraph.
- Decide. Jev reads the paragraph plus its 4 candidates and answers five questions. Fixed rules then turn those answers into a ranked list of links.
- Place. A writing model picks the exact words in the paragraph that become the link, and code checks them.
The original version is a Claude Code skill in the jev-internal-links repo. It uses a local sentence-transformers model for matching. When we rebuilt it inside DataWise, we switched matching to BGE-M3 on Cloudflare Workers AI. It is a multilingual embedding model that “can support more than 100 working languages” (BGE-M3 model card), so the tool is not limited to English sites.
Why only 4 candidate pages per paragraph?
Because Jev gets worse with large, irrelevant context. A small, focused shortlist gives it a real choice without noise. Candidates must also clear a similarity floor, and a page is never offered a link to itself or to a page it already links to.
Switching embedding models meant recalibrating that floor. The Python tool used 0.25 with all-MiniLM-L6-v2. BGE-M3 scores run higher, so we tested it on a Spanish course site of about 200 pages on October 2, 2026. At 0.45, BGE-M3 kept 3.96 candidates per paragraph against MiniLM’s 3.98 at 0.25, and 95% of the links Jev actually placed scored 0.518 or above. We shipped 0.45.
What questions does Jev answer about each paragraph?
Each Jev call contains the page title, the paragraph, and the 4 candidate pages with their titles and descriptions. It then answers five questions:
| Question | Type | What Jev is asked |
|---|---|---|
| Best target | Choice | Which page is the most useful next step for this reader? Choose none unless one page clearly deepens this paragraph’s topic. |
| Link warranted | Noul | Does the paragraph raise a topic, tool or claim a reader would want to explore? |
| Anchor available | Noul | Is there a phrase that could become link text without rewriting the sentence? |
| Reader stage | Choice | Is the reader learning, comparing, or ready to act? |
| Commercial | Noul | Is the paragraph promotional rather than educational? |
The “none” option matters most. Telling Jev to choose none unless a page clearly deepens the topic is what stops the tool from linking every paragraph to something.
How do plain rules decide which links get made?
Jev only supplies judgements. No model makes the final call. Every candidate link passes the same three bars:
- Jev is at least 70% confident in the page it picked
- at least 80% sure a link is warranted
- at least 70% sure a natural anchor exists
Links that clear all three get a score. The heaviest weight (1.2) goes to target pages with few inbound links, so orphan and under-linked pages come first. Then come Jev’s confidence (1.0), semantic similarity (0.8), link warranted (0.6) and anchor available (0.4). A link to a money page such as pricing earns a bonus when the reader is ready to act, and promotional paragraphs lose points.
Links are then placed best-first under caps: at most 3 new links per page, at most 8 into any one page, and at least 2 paragraphs between new links. Each extra link to the same target is worth 75% of the one before, which spreads links across the site instead of piling them onto the homepage. Because every number is fixed, each row in the final report can say exactly why a link was made or skipped.
How do you stop anchor text from sounding forced?
You never let the model write the anchor. It picks a span from the existing paragraph, and code rejects it unless it:
- appears word for word in the paragraph
- is 2 to 6 words long
- does not start with a verb or an article
- is not “click here” or “read more”
That matches Google’s advice that good anchor text is “descriptive, reasonably concise, and relevant” (Google Search Central). If no clean phrase exists, the link is dropped rather than forced. In practice that produces anchors like “Fan-out Queries tool” or “service schema”, and rejects ones like “Audit your top content”.
What went wrong in the first version?
Two thirds of the anchors came back empty. The Python tool used Claude Sonnet with a 120-token output limit. Sonnet reasons by default, and it spent the whole budget on hidden reasoning before writing a single word of output. We fixed it by turning reasoning off for that call, since picking a span from a paragraph needs none. Each call also got about 5 times cheaper.
The second lesson came from the cost side. Once anchors worked, the anchor writer was about 80% of a run’s cost. On October 5, 2026 we ran 70 real links from three production runs through seven models, with the same prompt and the same validation:
| Anchor model | Usable anchors | Cost per anchor |
|---|---|---|
| Claude Sonnet 5 | 44 of 70 | $0.00137 |
| DeepSeek V4 Pro | 40 of 70 | $0.00019 |
| Gemini 3.1 Flash Lite | 40 of 70 | $0.00011 |
| GLM 5.3, GPT-5 mini | Rejected the request: reasoning cannot be disabled | n/a |
Reading the disagreements side by side, Sonnet was cautious rather than better. It often returned nothing where the cheaper models found an obvious phrase. We switched DataWise to DeepSeek V4 Pro, which cut the cost of a full run by about 70%.
What did we learn from real runs?
The Python repo’s own benchmark is a 164-page site: 3,182 Jev decisions, 112 links proposed, $0.75 in total (jev-internal-links README). Our DataWise test runs between October 2 and 5, 2026 looked like this:
| Site | Pages read | Links suggested | Cost | Anchor model |
|---|---|---|---|---|
| datawiseseo.com | 30 | 31 | $0.07 | Sonnet 5 |
| airankingskool.com | 165 | 171 | $0.56 | Sonnet 5 |
| A Spanish course site | 199 | 306 | about $0.80 | Sonnet 5 |
| yoast.com | 300 (of 1,262) | 375 | $1.02 | Sonnet 5 |
| datawiseseo.com | 30 | 28 | $0.02 | DeepSeek V4 Pro |
Three patterns showed up on every site. Most suggestions land in the uncertain buckets, and that is a feature: the run pictured above found 15 sure links, 16 that needed review and 51 maybes. Orphan pages surfaced first, as designed. And the weak links that remained came from Jev picking the target, not from the anchor writer. One example: “DataWise settings” linking to our privacy policy at 99%. A human review step is not optional.
How do you run it on your own site?
There are two ways:
- The open-source skill. Clone the jev-internal-links repo and run it with Claude Code and your own OpenRouter key. It outputs a dashboard, a spreadsheet and CSV files.
- Inside DataWise. Open the Internal Links tool, enter your domain, and get the report in your browser with Sure, Needs review and Not sure tabs. Jev and anchor calls bill to your OpenRouter key. A 30-page site takes about 2 minutes.
Either way, add the Sure links first, read the paragraph behind every Needs review link, and treat Not sure as ideas. If your site is new, pair it with the Site Audit to catch broken links and orphan pages. If you are still writing content, the Content Writer adds internal links to new drafts as it writes them.
Watch the walkthrough
FAQ
Do internal links matter for AI search engines too?
Yes. To appear as a supporting link in AI Overviews or AI Mode, Google says a page “must be indexed and eligible to be shown in Google Search with a snippet”, and it lists “making your content easily findable through internal links on your website” among its best practices for AI features (Google Search Central). A page nothing links to is harder to discover for any system that relies on crawling.
How many internal links per page is too many?
Google does not publish a hard limit. The useful test is whether each link helps the reader at that point in the text. Our tool deliberately caps new links at 3 per page per run and keeps them 2 paragraphs apart. That is conservative, so you can run it again later without stacking links.
Should old posts link to new posts, or the other way around?
Both, but old posts linking to new ones is the step most sites skip. New pages start with no inbound links, while older posts already have traffic and authority. Running the tool after publishing a batch of new content is the quickest way to connect the two.
Can Jev rewrite my paragraphs to fit a link?
No, by design. Jev only returns choices and probabilities, and the anchor writer may only pick words that already exist in the paragraph. If a link would need new wording, the tool skips it. You can still add that link by hand from the Not sure tab.
Does the same approach work for languages other than English?
Yes. The matching model supports more than 100 languages, and the anchor prompt tells the writing model to keep the anchor in the paragraph’s language. Our largest early test was a Spanish site, where the tool suggested 306 links across 199 pages.
Nicolas Gorrono
Founder of DataWise SEO and the AI Ranking community. Writing about SEO, AI search, and data-driven optimization.
Put these insights into action
Join the AI Ranking community to get unlimited DataWise access, included with your membership. 7-day risk-free trial.
Join AI Ranking