Internal Link Graph & Orphan-Page Analyzer
Pricing
from $1.00 / 1,000 pages
Internal Link Graph & Orphan-Page Analyzer
Build an internal-link graph and identify sitemap orphan candidates.
Pricing
from $1.00 / 1,000 pages
Rating
0.0
(0)
Developer
Danial Maqbool
Maintained by CommunityActor stats
0
Bookmarked
3
Total users
2
Monthly active users
17 days ago
Last modified
Categories
Share
Build an internal-link graph and identify sitemap orphan candidates.
Example.com inputs are placeholders, not verified compatible content. Replace them with your permitted source. Run npm run smoke for an account-free deterministic example. JSON Feed, RSS and table examples use the separately recorded small live-test sources; they do not imply compatibility with every site.
What it does
Produces page records for seo and information-architecture teams. Values are extracted deterministically from public pages; unavailable fields remain null.
Common use cases
Build an internal-link graph and identify sitemap orphan candidates. Use the resulting dataset in scheduled tasks, API pipelines, spreadsheets, or agent workflows.
Input
| Field | Type | Description |
|---|---|---|
| startUrl | string | Public HTTP(S) website or sitemap URL. |
| maxPages | integer | Maximum pages scheduled in this bounded run. |
| renderJavaScript | boolean | Opt into Chromium for JavaScript content. More expensive; no access-control bypass. |
| respectRobotsTxt | boolean | Honor robots rules and supported crawl delays. Unavailable rules fail closed. |
| maxDepth | integer | Maximum followed-link depth. Explicit seeds start at zero. |
| includePatterns | array | Allowed absolute-URL patterns using * as wildcard. |
| excludePatterns | array | Excluded absolute-URL patterns using * as wildcard. |
| sitemapUrls | array | Sitemap Urls. See README for extraction behavior. |
| concurrency | integer | Maximum concurrent page handlers. Per-origin delays still apply. |
| requestDelayMs | integer | Minimum spacing between request starts to the same origin. |
| timeoutSecs | integer | Maximum individual request duration in seconds. |
| retries | integer | Retries for page handlers. Access blocks are not retried. |
| proxyConfiguration | object | Optional Apify or public custom proxy. Direct connections are the default. |
Output
| Field | Type |
|---|---|
| url | string or null |
| title | string or null |
| statusCode | number or null |
| depth | number or null |
| inboundLinkCount | number or null |
| outboundInternalLinkCount | number or null |
| outboundExternalLinkCount | number or null |
| discoveredFrom | string or null |
| canonical | string or null |
| sitemapPresence | boolean or null |
| orphanCandidate | boolean or null |
| lowInternalLinks | boolean or null |
| deadEnd | boolean or null |
| excessiveDepth | boolean or null |
| brokenInternalTargets | array or null |
| edgesTruncated | boolean or null |
| scopeComplete | boolean or null |
Results are in the default Dataset. RUN_SUMMARY, FAILURES and DIAGNOSTICS are available in the default key-value store. Diagnostics are not billed entity events. JSON, CSV and spreadsheet exports use Apify Dataset.
Example
Replace example.com with a public source relevant to this Actor. Plain example.com has no products, jobs or opportunities; an empty feed there is expected. Run the included local smoke test for a deterministic working example.
{"startUrl": "https://example.com/","maxPages": 1,"renderJavaScript": false}
Example output
The following records come from synthetic local fixtures, not a live website or claimed customer data.
[{"url": "https://fixture.example/index","title": "Fixture catalog","statusCode": 200,"depth": 0,"inboundLinkCount": 0,"outboundInternalLinkCount": 6,"outboundExternalLinkCount": 0,"discoveredFrom": null,"canonical": "https://fixture.example/index","sitemapPresence": true,"orphanCandidate": false,"lowInternalLinks": false,"deadEnd": false,"excessiveDepth": false,"brokenInternalTargets": [],"edgesTruncated": false,"scopeComplete": false}]
Pricing model
One page event per visible result record. Event prices are configured in Apify Console, never in extraction code. The SDK enforces the run's maxTotalChargeUsd. Failed pages and diagnostic-only messages are not billed. Do not configure an additional automatic default-dataset-item event.
How it works
Crawlee BasicCrawler manages bounded requests and retries. Cheerio parses HTTP HTML. A DNS-pinned transport validates every redirect and checks robots rules. Chromium is opt-in and its page requests pass through the same transport. No external paid API is required.
Limits
- Orphans are candidates within observed crawl coverage, never an unconditional site-wide claim.
- Edges are capped at 1,000 per page and 100,000 per run.
- Public GET/HEAD content only; no login, form submission, CAPTCHA solving or browser stealth.
- Responses are capped at 2 MiB decoded. Browser mode caps requests per page and blocks media, fonts, downloads, service workers and WebSockets.
- Missing/blocked robots rules fail closed. Robots crawl delays above 60 seconds are not supported.
- Start at 512 MB for static runs; use 2 GB for browser runs and measure representative targets.
Responsible use
Use only public content you are authorized to access. Respect source terms, licenses and applicable law. No private-account extraction, personal-data enrichment, credential input or access-control bypass is provided. Cookie values are never returned.
Local development
Requires Node.js 24 LTS. All commands below run from this Actor directory.
npm ci --ignore-scriptsnpm run input:example# Edit storage/key_value_stores/default/INPUT.json with your public URLs.npm run start:devnpm testnpm run typechecknpm run lintnpm run validatenpm run buildnpm startnpm run smokenpx apify rundocker build -t internal-link-graph-analyzer .
Browser testing: set PLAYWRIGHT_BROWSERS_PATH to an Actor-local .cache/browsers directory, then run npx playwright install chromium. Docker installs its own matching browser.
Deployment
Deploy only after validating the Docker build and a representative target. Deployment is not performed by installation or tests.
npx apify loginnpx apify push
Then configure the exact event in Console, set pricing and spending limits, test a private run, complete PUBLICATION_CHECKLIST.md, and deliberately enable Store visibility.
API usage
Replace YOUR_USERNAME with your Apify account name. Keep the token in an environment variable rather than input JSON.
$curl -X POST "https://api.apify.com/v2/acts/YOUR_USERNAME~internal-link-graph-analyzer/runs?maxTotalChargeUsd=1" -H "Authorization: Bearer $APIFY_TOKEN" -H "Content-Type: application/json" --data-binary @examples/basic-input.json
Monitor the returned run ID and read its default Dataset. Store/API names and price configuration are account-side settings.
Verified platform example
The saved Console input performs a bounded HTTP run with one page and at most two result records. The source is a public example/test page. The result demonstrates the audit or extraction interface.
Use the saved default input in Apify Console, or the repository examples/basic-input.json. Replace its source with your own permitted URL and keep limits small for the first run.
The following unmodified record was returned by a successful Apify platform run on 2026-09-30:
[{"url": "https://example.com/","title": "Example Domain","statusCode": 200,"depth": 0,"inboundLinkCount": 0,"outboundInternalLinkCount": 0,"outboundExternalLinkCount": 1,"discoveredFrom": null,"canonical": "https://example.com/","sitemapPresence": null,"orphanCandidate": false,"lowInternalLinks": false,"deadEnd": true,"excessiveDepth": false,"brokenInternalTargets": [],"edgesTruncated": false,"scopeComplete": false}]
Introductory pricing: $1.00 per 1,000 page results, plus Apify platform usage. The standard Actor start event costs $0.00005 per GB (minimum one event). Empty output and duplicate records incur no primary result event; the start fee and any platform usage still apply. The SDK enforces the event spending limit; platform usage is billed separately.