Internal Links API

Automatically add internal links to your content based on keywords you configure. No need to read this if you are using WordPress It works out of the box.

Overview

Internal links are essential for SEO and user navigation. With the Internal Links API, you can:

  • Define keyword-to-URL mappings in your MassBlogger dashboard
  • Fetch these mappings via API when rendering your content
  • Automatically replace keywords with links in your blog posts
  • Maintain consistent internal linking across all your content

Endpoint

GET/api/internal-links

Returns all internal links configured for your website.

Query Parameters

apiKeyRequiredYour website API key

Example Request

GET https://www.massblogger.com/api/internal-links?apiKey=YOUR_API_KEY

Response Format

The API returns an array of keyword-URL pairs:

contact us/contact
pricing/pricing
best practices/blog/best-practices
getting started/docs/getting-started
keywordstringThe keyword to search for in your content
urlstringThe URL to link to when the keyword is found

Usage Examples

Next.js (App Router)

// lib/internal-links.js
const API_KEY = process.env.MASSBLOGGER_API_KEY;
const API_URL = 'https://www.massblogger.com';

export async function getInternalLinks() {
  const res = await fetch(
    `${API_URL}/api/internal-links?apiKey=${API_KEY}`,
    { next: { revalidate: 3600 } } // Cache for 1 hour
  );
  return res.json();
}

export function applyInternalLinks(content, links, currentSlug) {
  let result = content;
  
  links.forEach(({ keyword, url }) => {
    if (!keyword || !url) return;
    // Skip links that point to the current page
    if (currentSlug && url.endsWith('/' + currentSlug)) return;

    const esc = keyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
    let found = false;
    result = result.replace(
      // Match headings, existing links, HTML tags, or the keyword
      new RegExp('(<h[1-6][^>]*>[\\s\\S]*?</h[1-6]>)|(<a[^>]*>[\\s\\S]*?</a>)|(<[^>]+>)|(\\b' + esc + '\\b)', 'gi'),
      (m, h, a, t, kw) => {
        if (h || a || t) return m; // Skip headings, links, tags
        if (!found && kw) {
          found = true;
          return '<a href="' + url + '" class="internal-link">' + kw + '</a>';
        }
        return m;
      }
    );
  });
  
  return result;
}

Usage in a blog post page:

// app/blog/[slug]/page.js
import { getInternalLinks, applyInternalLinks } from '@/lib/internal-links';

export default async function BlogPost({ params }) {
  const { slug } = await params;
  const [post, links] = await Promise.all([
    getPost(slug),
    getInternalLinks()
  ]);
  
  const contentWithLinks = applyInternalLinks(post.content, links, slug);
  
  return (
    <article>
      <h1>{post.title}</h1>
      <div dangerouslySetInnerHTML={{ __html: contentWithLinks }} />
    </article>
  );
}

React (Client-side)

import { useState, useEffect } from 'react';

function useInternalLinks(apiKey) {
  const [links, setLinks] = useState([]);
  
  useEffect(() => {
    fetch('https://www.massblogger.com/api/internal-links?apiKey=' + apiKey)
      .then(res => res.json())
      .then(setLinks)
      .catch(console.error);
  }, [apiKey]);
  
  return links;
}

function applyInternalLinks(content, links, currentSlug) {
  let result = content;
  
  links.forEach(({ keyword, url }) => {
    if (!keyword || !url) return;
    if (currentSlug && url.endsWith('/' + currentSlug)) return;

    const esc = keyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
    let found = false;
    result = result.replace(
      new RegExp('(<h[1-6][^>]*>[\\s\\S]*?</h[1-6]>)|(<a[^>]*>[\\s\\S]*?</a>)|(<[^>]+>)|(\\b' + esc + '\\b)', 'gi'),
      (m, h, a, t, kw) => {
        if (h || a || t) return m;
        if (!found && kw) { found = true; return '<a href="' + url + '">' + kw + '</a>'; }
        return m;
      }
    );
  });
  
  return result;
}

// Usage in component
function BlogPost({ post }) {
  const links = useInternalLinks('your_api_key');
  const content = applyInternalLinks(post.content, links, post.slug);
  
  return <div dangerouslySetInnerHTML={{ __html: content }} />;
}

Vanilla JavaScript

const API_KEY = 'your_api_key';

async function fetchInternalLinks() {
  const response = await fetch(
    'https://www.massblogger.com/api/internal-links?apiKey=' + API_KEY
  );
  return response.json();
}

function applyInternalLinks(content, links, currentSlug) {
  let result = content;
  
  links.forEach(({ keyword, url }) => {
    if (!keyword || !url) return;
    if (currentSlug && url.endsWith('/' + currentSlug)) return;

    const esc = keyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
    let found = false;
    result = result.replace(
      new RegExp('(<h[1-6][^>]*>[\\s\\S]*?</h[1-6]>)|(<a[^>]*>[\\s\\S]*?</a>)|(<[^>]+>)|(\\b' + esc + '\\b)', 'gi'),
      function(m, h, a, t, kw) {
        if (h || a || t) return m;
        if (!found && kw) { found = true; return '<a href="' + url + '">' + kw + '</a>'; }
        return m;
      }
    );
  });
  
  return result;
}

// Usage
document.addEventListener('DOMContentLoaded', async () => {
  const links = await fetchInternalLinks();
  const article = document.querySelector('.article-content');
  // Extract current slug from URL path
  const currentSlug = window.location.pathname.split('/').pop();
  
  if (article) {
    article.innerHTML = applyInternalLinks(article.innerHTML, links, currentSlug);
  }
});

PHP

<?php
$api_key = 'your_api_key';

function getInternalLinks($api_key) {
    $url = "https://www.massblogger.com/api/internal-links?apiKey=" . $api_key;
    $response = file_get_contents($url);
    return json_decode($response, true);
}

function applyInternalLinks($content, $links, $current_slug = '') {
    foreach ($links as $link) {
        $keyword = $link['keyword'];
        $url = $link['url'];
        
        if (empty($keyword) || empty($url)) continue;
        // Skip links that point to the current page
        if ($current_slug && str_ends_with($url, '/' . $current_slug)) continue;
        
        $esc = preg_quote($keyword, '/');
        // Skip matches inside headings, existing links, or HTML tags
        $pattern = '/(<h[1-6][^>]*>[\\s\\S]*?<\\/h[1-6]>)|(<a[^>]*>[\\s\\S]*?<\\/a>)|(<[^>]+>)|(\\b' . $esc . '\\b)/i';
        
        $found = false;
        $content = preg_replace_callback($pattern, function($m) use ($url, &$found) {
            if (!empty($m[1]) || !empty($m[2]) || !empty($m[3])) return $m[0];
            if (!$found && !empty($m[4])) {
                $found = true;
                return '<a href="' . htmlspecialchars($url) . '">' . $m[4] . '</a>';
            }
            return $m[0];
        }, $content);
    }
    
    return $content;
}

// Usage
$links = getInternalLinks($api_key);
$post_content = get_the_content(); // Your content source
$current_slug = basename(get_permalink()); // Current page slug
$content_with_links = applyInternalLinks($post_content, $links, $current_slug);

echo $content_with_links;
?>

Best Practices

  • Cache responses: Internal links don't change often. Cache the API response for at least 1 hour to reduce API calls.
  • Limit replacements: Only replace the first occurrence of each keyword to avoid over-linking, which can hurt SEO and readability.
  • Use whole word matching: Use word boundaries in your regex to avoid matching partial words.
  • Process server-side: For SEO benefits, apply internal links on the server before sending HTML to the client.
  • Avoid nested links: Be careful not to insert links inside existing anchor tags. Consider parsing HTML properly for complex content.
  • Skip self-links: Never link a page to itself. Pass the current slug and skip any internal link whose URL matches the current page.
  • Skip headings: Never insert links inside heading tags (h1–h6). Internal links belong in paragraph text only.
  • Order by length: Process longer keywords first to avoid partial matches (e.g., "getting started guide" before "getting started").

Need help? Contact us at [email protected]