# Internal Link Opportunity Finder (`ib4ngz/internal-link-opportunity-finder`) Actor

Crawl websites and find explainable, contextual internal linking opportunities.

- **URL**: https://apify.com/ib4ngz/internal-link-opportunity-finder.md
- **Developed by:** [Iqbal R](https://apify.com/ib4ngz) (community)
- **Categories:**
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 page analyzeds

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

### What does Internal Link Opportunity Finder do?

**Internal Link Opportunity Finder automatically crawls your website and finds high-value internal linking opportunities between related pages.** It returns recommended source and target URLs, suggested anchor text, a matching content snippet, priority, score, explanation, and relevant page metrics without requiring a prebuilt keyword-to-URL map.

Try it with [apify.com](https://apify.com). Results are available through Apify Console, API, schedules, webhooks, integrations, and downloadable datasets.

### Why use Internal Link Opportunity Finder?

Strong internal links help visitors discover useful content and make important pages easier for search engines to find. On larger websites, manually reviewing every possible source and target page is slow and inconsistent.

Use this Actor to:

- find contextual links between related articles, services, products, landing pages, and documentation;
- discover important pages with few or no inbound links in the analyzed crawl;
- identify natural phrases that can become useful anchor text;
- prioritize recommendations using content relevance and link metrics;
- support content refreshes, website migrations, editorial audits, and SEO workflows;
- export recommendations to spreadsheets, databases, n8n, Make, Zapier, or other tools.

### How to use Internal Link Opportunity Finder

1. Open the Actor in Apify Console.
2. Add one or more website URLs in the **Input** tab.
3. Set the maximum pages and crawl depth.
4. Choose grouped output for editorial review or flat output for automation.
5. Click **Start**.
6. Review the ranked recommendations in the **Output** tab.
7. Confirm that each suggested link is useful to readers before publishing it.

For best results, start with a focused website section and increase the page limit after reviewing the first run.

### Input

A minimal input:

```json
{
    "startUrls": [{ "url": "https://apify.com" }]
}
```

A more targeted configuration:

```json
{
    "startUrls": [{ "url": "https://apify.com" }],
    "maxPagesPerSite": 100,
    "maxCrawlDepth": 10,
    "includeSubdomains": false,
    "respectRobotsTxt": true,
    "minimumOpportunityScore": 0.6,
    "maxOpportunitiesPerPage": 10,
    "detectOrphanPages": true,
    "minimumWordCount": 100,
    "outputMode": "grouped",
    "preferredTargetUrls": ["https://apify.com/store/*"],
    "excludeUrlPatterns": ["https://apify.com/account/*"],
    "proxyConfiguration": {
        "useApifyProxy": false
    }
}
```

#### Input fields

| Field                     | Description                                        | Default   |
| ------------------------- | -------------------------------------------------- | --------- |
| `startUrls`               | Websites or pages where analysis starts            | Required  |
| `maxPagesPerSite`         | Maximum HTML pages crawled per website             | `100`     |
| `maxCrawlDepth`           | Maximum link distance from a discovered entry page | `10`      |
| `includeSubdomains`       | Include child subdomains in the same analysis      | `false`   |
| `respectRobotsTxt`        | Apply applicable robots.txt crawl rules            | `true`    |
| `minimumOpportunityScore` | Minimum score included in results                  | `0.6`     |
| `maxOpportunitiesPerPage` | Maximum recommendations for each source page       | `10`      |
| `detectOrphanPages`       | Detect eligible pages with no inbound links found  | `true`    |
| `minimumWordCount`        | Minimum page word count used for recommendations   | `100`     |
| `outputMode`              | Return `grouped` or `flat` results                 | `grouped` |
| `includeUrlPatterns`      | Crawl only URLs matching these wildcard patterns   | Empty     |
| `excludeUrlPatterns`      | Skip URLs matching these wildcard patterns         | Empty     |
| `targetUrlPatterns`       | Restrict recommendation targets                    | Empty     |
| `preferredTargetUrls`     | Give strategic target pages additional priority    | Empty     |
| `ignoredTargetUrls`       | Never recommend matching pages as targets          | Empty     |
| `proxyConfiguration`      | Apify Proxy or custom proxy settings               | Direct    |

URL pattern fields support `*` as a wildcard.

### Output

The default grouped output creates one dataset item per source page:

```json
{
    "site": "apify.com",
    "sourceUrl": "https://apify.com/blog/web-scraping",
    "source": {
        "title": "Web scraping guide",
        "pageType": "article"
    },
    "sourceMetrics": {
        "inboundLinks": 8,
        "outboundLinks": 14,
        "crawlDepth": 2,
        "importanceScore": 0.76
    },
    "opportunityCount": 1,
    "opportunities": [
        {
            "targetUrl": "https://apify.com/store",
            "targetTitle": "Apify Store",
            "targetPageType": "landing-page",
            "type": "contextual_link",
            "suggestedAnchorTexts": ["ready-made web scrapers"],
            "context": {
                "text": "You can start quickly with ready-made web scrapers.",
                "matchedPhrase": "ready-made web scrapers"
            },
            "score": 0.88,
            "priority": "critical",
            "reason": "Strong topical relevance and a natural anchor phrase exists in the source content.",
            "targetMetrics": {
                "inboundLinks": 3,
                "isOrphan": false,
                "isUnderlinked": true,
                "importanceScore": 0.91
            }
        }
    ]
}
```

Flat output creates one dataset item per recommendation, making it convenient for automation and spreadsheet exports.

The key-value store also contains a summary for each analyzed website and an aggregate run summary. You can download the dataset in various formats such as JSON, HTML, CSV, or Excel.

### Data table

| Field                  | Description                                                  |
| ---------------------- | ------------------------------------------------------------ |
| `site`                 | Website associated with the recommendation                   |
| `sourceUrl`            | Page where a link could be added                             |
| `targetUrl`            | Recommended internal destination                             |
| `targetTitle`          | Title of the recommended destination                         |
| `type`                 | Contextual, missing, orphan, underlinked, or hub opportunity |
| `suggestedAnchorTexts` | Suggested wording for the link                               |
| `context`              | Source sentence and matching phrase                          |
| `score`                | Opportunity score from 0 to 1                                |
| `priority`             | Critical, high, medium, or low                               |
| `reason`               | Explanation of why the link is recommended                   |
| `sourceMetrics`        | Link count, depth, and importance of the source              |
| `targetMetrics`        | Link coverage and importance of the target                   |

### How much does it cost to analyze internal links?

This Actor uses **pay-per-event pricing**. You pay once for each unique HTML page successfully included in the analysis. Failed, rejected, duplicate, and over-budget pages are not charged. Apify platform usage is billed separately.

Prices start at **$5.00 per 1,000 pages analyzed**. The event charge for a 100-page crawl starts at **$0.50**, plus platform usage. You can set a maximum charge for every run; the Actor stops cleanly and returns the paid results collected so far when that limit is reached.

Start with 50-100 pages to confirm your URL scope, then increase `maxPagesPerSite` for a complete audit.

### Tips and advanced options

- Add commercial, evergreen, or high-conversion pages to `preferredTargetUrls`.
- Use `targetUrlPatterns` when recommendations should point only to a specific section.
- Exclude account pages, calendars, search results, and faceted navigation.
- Raise `minimumOpportunityScore` for a shorter, higher-confidence shortlist.
- Use grouped output for editorial review and flat output for downstream automation.
- Keep robots.txt enabled and choose crawl limits appropriate for the target website.
- Analyze unrelated subdomains separately unless they share one content strategy.

The Actor checks robots.txt and discovers sitemap URLs before crawling eligible internal HTML pages. Sitemap coverage helps surface pages that may not be reachable through the links found during the run.

### FAQ, disclaimers, and support

#### What does an orphan-page result mean?

It means no inbound internal links were found for that page in the analyzed crawl. It does not claim that a search engine considers the page orphaned.

#### Does the Actor change my website?

No. It only reads public pages and returns recommendations. It does not publish or edit links.

#### Does every recommendation need to be implemented?

No. Review relevance, reader value, placement, and anchor wording before making a change. The score is a prioritization aid, not a guarantee of SEO performance.

#### What are the limitations?

Pages and links that require browser-side JavaScript may not be visible. PDF content is not analyzed. Password-protected pages, blocked pages, and content outside the configured limits are excluded. Results reflect only the pages successfully observed during the run.

#### Is web crawling legal?

You are responsible for complying with applicable laws, website terms, robots directives, privacy requirements, and rate limits. Only process websites and data you are authorized to access.

For help, bug reports, or feature requests, use the Actor's **Issues** tab. Custom scoring, extraction rules, and workflow integrations are also available as custom solutions.

# Actor input Schema

## `startUrls` (type: `array`):

One or more website URLs. URLs from the same hostname share one site analysis.

## `maxPagesPerSite` (type: `integer`):

Maximum number of HTML pages crawled for each website.

## `maxCrawlDepth` (type: `integer`):

Maximum internal-link distance from a start or sitemap URL.

## `includeSubdomains` (type: `boolean`):

Allow pages on child subdomains of each start hostname.

## `respectRobotsTxt` (type: `boolean`):

Apply the matching robots.txt allow/disallow rules during the page crawl.

## `minimumOpportunityScore` (type: `number`):

Only emit opportunities at or above this normalized 0–1 score.

## `maxOpportunitiesPerPage` (type: `integer`):

Limits recommendations returned for each source page.

## `detectOrphanPages` (type: `boolean`):

Flag sitemap/start pages with no inbound links found in the analyzed crawl.

## `minimumWordCount` (type: `integer`):

Minimum cleaned-content word count for opportunity source and target pages.

## `outputMode` (type: `string`):

Grouped produces one item per source page; flat produces one item per opportunity.

## `includeUrlPatterns` (type: `array`):

Optional wildcard URL patterns that pages must match.

## `excludeUrlPatterns` (type: `array`):

Optional wildcard URL patterns excluded from crawling.

## `targetUrlPatterns` (type: `array`):

Optional wildcard patterns restricting recommendation target pages.

## `preferredTargetUrls` (type: `array`):

Wildcard patterns for strategic pages that receive an importance boost.

## `ignoredTargetUrls` (type: `array`):

Wildcard patterns for pages that must never be recommended as targets.

## `proxyConfiguration` (type: `object`):

Standard Apify Proxy or custom proxy settings.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://apify.com"
    }
  ],
  "maxPagesPerSite": 100,
  "maxCrawlDepth": 10,
  "includeSubdomains": false,
  "respectRobotsTxt": true,
  "minimumOpportunityScore": 0.6,
  "maxOpportunitiesPerPage": 10,
  "detectOrphanPages": true,
  "minimumWordCount": 100,
  "outputMode": "grouped",
  "includeUrlPatterns": [],
  "excludeUrlPatterns": [],
  "targetUrlPatterns": [],
  "preferredTargetUrls": [],
  "ignoredTargetUrls": [],
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `results` (type: `string`):

No description

## `runSummary` (type: `string`):

No description

## `siteSummaries` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "startUrls": [
        {
            "url": "https://apify.com"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("ib4ngz/internal-link-opportunity-finder").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "startUrls": [{ "url": "https://apify.com" }] }

# Run the Actor and wait for it to finish
run = client.actor("ib4ngz/internal-link-opportunity-finder").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "startUrls": [
    {
      "url": "https://apify.com"
    }
  ]
}' |
apify call ib4ngz/internal-link-opportunity-finder --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,ib4ngz/internal-link-opportunity-finder"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/Qk1gn25XucmYNgqTa/builds/iCgIFozkHejWIIGKw/openapi.json
