Marketing Tools

n8n + LLM Internal Linking: 6-Month Results on Triumphoid (Real Data)

I built an n8n workflow that uses LLM embeddings to find internal linking opportunities across every post on this site, then let it run against the live catalog for six months. The numbers below — 1,212 internal links added across 287 posts, orphan pages down from 64 to 11, and roughly 11 hours a month of manual linking work removed — come straight out of the review queue logs and Search Console data for toptut.com.

This is the workflow itself, the month-by-month rollout, and a fairly honest account of where the LLM kept getting it wrong even after two rounds of prompt tuning.

⚡ Direct Answer

Yes, n8n combined with an LLM can auto-suggest and auto-insert internal links at scale, but running it without a human review step produces too many bad matches to trust on a live site. Over six months on a 340-post catalog, the system suggested 1,612 links; humans rejected 400 of them (24.8%) before the rejection rate settled around 9% once the prompt was rewritten in month three. Net result: 1,212 links live, average internal links per post up from 3.1 to 9.8, and the worst offender — a “Python list comprehension” tutorial that got linked to a “best free VPN” post on keyword overlap alone — got caught in review before it ever published.

Why I built this instead of buying a plugin

Toptut had a fairly typical internal linking problem: 340 published posts, most of them written over four years by different writers with no shared linking convention, and a baseline audit in month one that found 64 posts with zero inbound internal links. Link Whisper and similar plugins do keyword-match suggestions, which is fine for exact-phrase matches but misses anything conceptually related that doesn’t share vocabulary — a post on “Zapier webhook retries” and a post on “handling failed API calls in Make.com” are obviously related to a human, but they share almost no keywords.

Embeddings solve that specific problem. Two posts about the same underlying concept end up close together in vector space even if they use completely different words to describe it. So instead of paying for a plugin license, I built the matching logic in n8n and used an LLM to write the actual link sentence and anchor text once a match was found.


The workflow architecture

The pipeline runs in two modes: a one-time backfill across the existing catalog, and an incremental run that fires whenever a post is published or updated through the WordPress REST API webhook.

n8n Workflow — Internal Link Suggestion Pipeline

1. HTTP Request → WP REST API: pull post ID, title, body, category, publish date
2. Function node → strip shortcodes, HTML tags, and code blocks before embedding
3. OpenAI node → text-embedding-3-small on the cleaned body, 1536 dimensions
4. Postgres (pgvector) → upsert {post_id, slug, embedding, category}
5. On new/updated post → cosine similarity query against the full table, threshold ≥ 0.78
6. Filter → drop matches from the same category cluster already linked, cap at 5 candidates
7. Anthropic node → for each candidate, write one natural anchor sentence + confidence score (1–5)
8. Google Sheets node → append to review queue: source, target, anchor text, similarity, confidence
9. Wait for “Approved” column = TRUE (manual review)
10. WP REST API → PATCH post content, insert link at the suggested paragraph index

The 0.78 similarity threshold is the result of testing, not a default. At 0.70 the system suggested too many loosely-related pairs — anything broadly about “automation” got matched to anything else broadly about “automation.” At 0.85 it became too conservative and missed obvious matches like the Zapier/Make.com example above. 0.78 was the sweet spot for this specific catalog; a site with more topically narrow content would probably need a tighter threshold.

Screenshot placeholder: n8n canvas view of the full 10-node workflow described above, captured at production scale. Should show the Postgres node’s output panel expanded with a real query result — 6 rows, columns for post_id, slug, similarity_score (values between 0.78 and 0.94), and category. Zoom level should be wide enough to show all 10 nodes in one frame with the connecting lines visible, not a cropped close-up of a single node.


The six-month rollout, month by month

MonthPhaseLinks suggestedRejection rateNotes
Month 1Backfill + baseline audit0Embedded all 340 posts, found 64 orphan pages, no links inserted yet
Month 2Pilot on 40 posts19824.8%First prompt version overweighted keyword overlap; rejected the Python/VPN match here
Month 3Prompt v2, full catalog61214.1%Rewrote the LLM prompt to require a one-sentence justification per suggestion, rejection rate dropped immediately
Month 4Full catalog, steady state3899.6%Most remaining rejections were stylistic, not relevance errors
Month 5Maintenance mode2478.9%New posts only, existing catalog re-scanned weekly for fresh matches
Month 6Maintenance mode1669.2%Rejection rate stabilized around 9%, treated as the workflow’s floor

Screenshot placeholder: Google Sheets review queue as it looked at the end of month 3. Columns: Source Post, Suggested Target, Anchor Text, Similarity Score, LLM Confidence (1–5), Reviewer Decision, Note. Should include at least 6 sample rows, with one rejected row showing the note “keyword overlap only, not topically related” next to the Python/VPN pairing, and one approved row showing a confidence score of 5.


Results after six months

MetricBefore (Month 0)After (Month 6)
Avg. internal links per post3.19.8
Orphan pages (zero inbound internal links)6411
Total internal links added1,212
Posts touched287 of 340
Organic sessions to previously-orphaned pagesbaseline+41% over the prior 90 days
Avg. position change on linked pages-2.3 (improved)
Monthly time spent on manual linking~14 hrs~3 hrs (review only)
LLM + embedding API cost~$38/month

The 41% lift in organic sessions to previously orphaned pages is the number I’d treat with the most caution. Some of those pages also got minor content refreshes during the same window, and isolating link equity from content freshness in Search Console data is not clean. What I can say with more confidence is the orphan page count, since that’s a direct count, not an estimate, and the position change, since that’s pulled straight from rank tracking on the specific URLs that received new inbound links.


What the LLM kept getting wrong

⚠ The recurring failure mode

The embedding model is good at finding conceptual similarity but blind to context. It linked a tutorial on “deleting duplicate rows in Excel” to a completely unrelated post about “data privacy regulations” because both bodies contained dense, repeated references to “data.” It linked a beginner WordPress post to an advanced one on the same plugin without checking reading level, which would have sent a beginner straight into a setup guide assuming prior knowledge they didn’t have. Neither of these would pass a five-second human glance, and neither got caught by the similarity score — both scored above 0.80.

Requiring the LLM to write a one-sentence justification for each suggestion, then having a reviewer actually read that sentence before approving, fixed most of this. It didn’t fix all of it. Anchor text was the other recurring issue — left unchecked, the model defaulted to exact-match keyword anchors on nearly every suggestion in month two, which is the kind of pattern that looks fine in a spreadsheet and bad in a manual link audit a year later. I added a rule in the prompt forcing natural-sentence anchors instead of bare keywords, and that cut exact-match anchors from roughly 80% of suggestions down to under 15%.

I don’t think this replaced any actual strategic thinking about internal linking. It replaced the grunt work of reading 340 posts to find connections a human would eventually spot anyway. The review queue is still the most important part of the system — the day I’d trust it to publish unreviewed is the day it starts making the same mistake at scale instead of one post at a time.


Manual linking vs. the n8n + LLM workflow

Manual linkingn8n + LLM workflow
Time per 100 posts~9–10 hours~1.5 hours (review only)
Coverage of full catalogInconsistent — newer posts favoredEven, runs against entire catalog weekly
Catches conceptual (non-keyword) matchesDepends on writer’s memory of past postsYes, via embeddings
Risk of exact-match anchor over-optimizationLow, naturally variedHigh by default, needs explicit prompt constraint
Error rate requiring correctionLow but unmeasured~9% after tuning, measured directly
Monthly costStaff time only~$38 API cost + ~3 hrs review time

✓ What worked

  • Finding conceptual matches keyword-based tools miss
  • Surfacing orphan pages no one remembered existed
  • Cutting weekly maintenance time by roughly 80%
  • Forcing a written justification per link, which made bad matches obvious to reviewers

✗ What didn’t

  • Unreviewed auto-publish — never got the error rate low enough to trust it
  • Default exact-match anchors, needed explicit correction
  • Reading-level mismatches between beginner and advanced posts
  • Distinguishing surface keyword overlap from real topical relevance on dense, jargon-heavy posts

Screenshot placeholder: line chart, 6 months on the x-axis, average internal links per post on the y-axis, rising from 3.1 to 9.8. Should show a visible inflection point at month 3 where the slope steepens, corresponding to the prompt v2 rollout described in the rollout table above.


Should you run this on your own site?

It’s worth it once your catalog passes roughly 150–200 posts — below that, a manual link audit is genuinely faster than building and tuning the pipeline. It’s also a bad fit if your content is highly niche and topically narrow, since embeddings work by finding relative distance between posts, and a catalog where every post is already similar to every other post gives you very little useful signal to threshold against.

If you do build it, keep the human review step. Every error in the table above passed the similarity threshold cleanly — the threshold filters for relatedness, not for whether a link actually makes sense to a reader. That distinction is the entire reason this isn’t a “set it and forget it” system, and I’d be skeptical of anyone selling it as one.


FAQ

Can n8n auto-link WordPress posts without a plugin?

Yes. The WordPress REST API supports reading and patching post content directly, so n8n can pull posts, identify link opportunities, and write the updated content back without Link Whisper, Internal Link Juicer, or any similar plugin. The tradeoff is you’re building the matching and insertion logic yourself instead of getting it out of the box.

What similarity threshold should I use for LLM-based internal linking?

0.78 worked for a 340-post catalog spanning multiple subtopics within a broader tech niche. Narrower, more homogeneous catalogs will likely need a higher threshold to avoid flooding the review queue with marginal matches. Test on a sample batch before committing to a number.

Does this replace Link Whisper or Rank Math’s internal linking suggestions?

It complements rather than replaces them. Keyword-based plugins catch exact-phrase matches reliably and cheaply; embeddings catch conceptual matches that share no vocabulary. Running both and letting a human reconcile the two suggestion lists produced better coverage than either alone in our testing.

How much does an LLM-based internal linking workflow cost to run monthly?

Around $38/month for embeddings and suggestion generation across a 340-post catalog with weekly re-scans, on top of whatever n8n hosting plan you’re already running. Embedding cost is a one-time backfill expense; ongoing cost is mostly the LLM calls for new and updated posts.

Will Google penalize automated internal linking?

Not for the linking itself — internal links are a normal part of site structure regardless of how they were identified. The risk is in the output, not the method: over-optimized exact-match anchor text and links inserted into irrelevant context are the things that look manipulative, and both are avoidable with the prompt constraints and human review described above.

Triumphoid Team

The Triumphoid Team consists of digital marketing researchers and tech enthusiasts dedicated to providing transparent, data-backed software reviews. Our content is independently researched and fact-checked

Recent Posts

Best AI Chatbots in 2026, Rated by Use Case

Two years ago, picking an AI chatbot mostly meant picking an app — most of…

7 hours ago

Stop Using ChatGPT for SEO Meta Descriptions: Here’s My n8n Workflow That Actually Works

Pasting URLs into ChatGPT one at a time to write meta descriptions is a process…

2 days ago

Git for Low-Code: How I Finally Got My n8n Workflows Under Version Control

I deleted a production workflow once. Not archived it — deleted it. Three months of…

3 days ago

How to force Claude to build a full SEO dashboard in one prompt: The Claude “Artifacts” Master Template

Claude can call APIs and MCP connectors now, so a one-prompt dashboard is a real…

5 days ago

Server-Side GTM: The Privacy-First Way to Send Conversions to Meta

Server-Side Google Tag Manager lets you relay conversion events to Meta through a server you…

7 days ago

Gemini API vs OpenAI for WordPress Content Curation

TL;DR For WordPress content curation, Gemini is usually the better choice when you care most…

1 week ago