# Website Link Crawler & Broken Link Checker (`aerodynamic_tripod/website-link-crawler`) Actor

Crawl websites and audit internal and external links. Find broken links, redirects, HTTP status codes, anchor text, follow/nofollow links, canonical URLs, and crawl depth for SEO audits and website maintenance.

- **URL**: https://apify.com/aerodynamic\_tripod/website-link-crawler.md
- **Developed by:** [SearchShark Tools](https://apify.com/aerodynamic_tripod) (community)
- **Categories:**
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## 🔗 Website Link Crawler

**Website Link Crawler** lets you crawl websites and extract **structured SEO link data** from every page.

Find **broken links**, analyze internal linking, detect redirects, inspect anchor text, identify follow/nofollow links, review crawl depth, and export clean source → target link relationships **without manually checking pages one by one**.

- 🔎 **Discover website links:** extract internal and external links from crawled pages
- 🚨 **Find broken links:** detect URLs returning HTTP errors or request failures
- ↪️ **Detect redirects:** capture redirect status and destination URLs
- 🧭 **Analyze internal linking:** preserve the exact page where every link was found
- 🏷 **Inspect anchor text:** extract the visible text attached to each link
- 🔗 **Check follow status:** distinguish normal follow links from `nofollow` links
- 📏 **Track crawl depth:** understand how deep each source page is from the starting URL
- 📄 **Inspect page metadata:** collect page titles and canonical URLs
- 🧹 **Get cleaner results:** normalize fragments and remove duplicate target links on the same source page
- ⚡ **Automate SEO audits:** export structured results to Apify Dataset, API, integrations, or downstream applications

***

### 📦 What data does Website Link Crawler extract?

| | |
| --- | --- |
| 📄 **Source URL** | 📝 **Source page title** |
| 🔖 **Canonical URL** | 🎯 **Target URL** |
| 🏷 **Anchor text** | 🔗 **Internal / external classification** |
| 👁 **Follow / nofollow status** | 📏 **Crawl depth** |
| 🌐 **HTTP status code** | 🚨 **Broken-link status** |
| ↪️ **Redirect status** | 🎯 **Redirect destination** |
| ⚠️ **Request error information** | 🧹 **Normalized target URL** |

> **Unavailable values are returned as `null`.** The Actor does not invent metadata that the website does not expose.

***

### 🚀 Features

Website Link Crawler is designed for **technical SEO audits**, **website maintenance**, **site migrations**, and **link analysis**.

- **Multiple start URLs:** crawl one or more websites or sections in a single Actor run
- **Configurable crawl depth:** control how many internal-link levels the crawler follows
- **Configurable page limit:** prevent unexpectedly large crawls
- **Internal link discovery:** capture links pointing to the same hostname
- **External link discovery:** capture links pointing to other hostnames
- **External-link safety:** external links are analyzed but are not recursively crawled
- **HTTP status checking:** inspect the response status of discovered targets
- **Broken-link detection:** mark HTTP errors and unreachable targets
- **Redirect detection:** identify HTTP redirects without automatically hiding the original response
- **Redirect destination extraction:** preserve the URL returned in the redirect `Location` header
- **Follow / nofollow classification**
- **Anchor-text extraction**
- **Source page title extraction**
- **Canonical URL extraction**
- **Crawl-depth tracking**
- **Fragment normalization:** URLs such as `/about` and `/about#team` are treated as the same HTTP resource
- **Per-page duplicate removal:** repeated target links on the same source page are not emitted repeatedly
- **Same-hostname recursive crawling**
- **API-friendly output:** every Dataset row follows a predictable source → target relationship
- **HTTP-first architecture:** normal crawling does not require a browser

***

## ⬇️ Input

You can configure Website Link Crawler directly from the **Apify Console** or send the input programmatically through the **Apify API**.

***

### 🔗 Start URLs

Add one or more URLs where crawling should begin.

Example:

```text
https://example.com
https://example.org
```

Each starting URL begins at:

```text
crawlDepth: 0
```

The Actor can then follow internal links depending on the configured maximum crawl depth.

***

### 📏 Max Crawl Depth

Controls how many levels of internal links the crawler follows.

```text
Minimum: 0
Default: 2
Maximum: 20
```

#### Depth 0

```text
https://example.com
```

Only the starting page is crawled.

Links found on that page are still analyzed and written to the Dataset, but the linked pages are not recursively crawled.

***

#### Depth 1

Example:

```text
Start:
https://example.com

Links to:
https://example.com/about
https://example.com/services
```

The Actor crawls:

```text
Depth 0 → Homepage
Depth 1 → About
Depth 1 → Services
```

but does not continue to depth 2.

***

#### Depth 2

Example:

```text
Homepage
   ↓
Services
   ↓
Plumbing
```

becomes:

```text
Homepage → depth 0
Services → depth 1
Plumbing → depth 2
```

The crawler stops adding deeper requests after the configured limit is reached.

***

### 🔢 Maximum Pages to Crawl

Limits how many pages the crawler may process during the Actor run.

```text
Minimum: 1
Default: 100
Maximum: 100000
```

For example:

```json
{
  "maxRequestsPerCrawl": 500
}
```

allows the crawler to process up to 500 pages.

The requested number is a **maximum**, not a guarantee.

A crawl may naturally finish earlier when no new internal pages remain.

***

### 🧪 Example input

```json
{
  "startUrls": [
    {
      "url": "https://example.com"
    }
  ],
  "maxCrawlDepth": 2,
  "maxRequestsPerCrawl": 100
}
```

***

## ⬆️ Output

Results are stored in the Actor's default **Apify Dataset**.

You can access the results from:

```text
Output
Storage
Apify API
Integrations
```

Each Dataset row represents:

```text
one discovered target URL
+
the source page where it was found
```

***

### 📊 Example result

```json
{
  "sourceUrl": "https://example.com/",
  "sourceTitle": "Example Company",
  "canonicalUrl": "https://example.com/",
  "targetUrl": "https://example.com/services",
  "anchorText": "Our Services",
  "linkType": "internal",
  "followType": "follow",
  "crawlDepth": 0,
  "statusCode": 200,
  "isBroken": false,
  "isRedirect": false,
  "redirectUrl": null,
  "statusError": null
}
```

***

### 📄 Source URL

The `sourceUrl` field tells you exactly where a link was discovered.

Example:

```text
sourceUrl:
https://example.com/services

targetUrl:
https://example.com/contact
```

This preserves the relationship:

```text
/services → /contact
```

instead of returning only a flat list of URLs.

This is useful for technical SEO because you can identify **which page contains the problematic link**.

***

### 📝 Source page title

Each result includes:

```text
sourceTitle
```

when the source page contains an HTML `<title>` element.

Example:

```json
{
  "sourceUrl": "https://example.com/services",
  "sourceTitle": "Home Services | Example Company"
}
```

This makes large Dataset exports easier to understand without manually opening each source page.

***

### 🔖 Canonical URL

When the source page contains:

```html
<link rel="canonical" href="https://example.com/preferred-page">
```

the Actor returns:

```json
{
  "canonicalUrl": "https://example.com/preferred-page"
}
```

Relative canonical URLs are resolved against the source page.

Example:

```html
<link rel="canonical" href="/services">
```

becomes:

```text
https://example.com/services
```

If no canonical tag is available:

```json
{
  "canonicalUrl": null
}
```

***

## 🔗 Internal and external links

Every target is classified as either:

```text
internal
external
```

***

### 🏠 Internal links

A link is considered internal when its hostname matches the source page hostname.

Example:

```text
Source:
https://example.com/

Target:
https://example.com/about
```

Output:

```json
{
  "linkType": "internal"
}
```

***

### 🌍 External links

Example:

```text
Source:
https://example.com/

Target:
https://wikipedia.org/
```

Output:

```json
{
  "linkType": "external"
}
```

External links are still:

```text
extracted
status checked
classified
stored
```

but the crawler does **not recursively crawl the external website**.

This prevents a normal website audit from accidentally expanding across the entire web.

***

## 🏷 Anchor text

The Actor extracts the visible text inside each link.

Example HTML:

```html
<a href="/pricing">View our pricing</a>
```

Output:

```json
{
  "anchorText": "View our pricing"
}
```

This can help with:

```text
Internal anchor-text audits
SEO optimization
Navigation analysis
Generic anchor detection
Site migration reviews
```

***

## 👁 Follow and nofollow links

Website Link Crawler checks the link's `rel` attribute.

Normal link:

```html
<a href="/about">About</a>
```

Output:

```json
{
  "followType": "follow"
}
```

Nofollow link:

```html
<a href="/partner" rel="nofollow">Partner</a>
```

Output:

```json
{
  "followType": "nofollow"
}
```

A link containing multiple `rel` values is still recognized.

Example:

```html
<a href="/partner" rel="nofollow sponsored">
```

returns:

```text
nofollow
```

***

## 🚨 Broken-link detection

One of the main uses of Website Link Crawler is finding links that no longer work.

The Actor checks each discovered target URL and returns:

```text
statusCode
isBroken
statusError
```

***

### ✅ Working URL

Example:

```json
{
  "statusCode": 200,
  "isBroken": false,
  "statusError": null
}
```

***

### ❌ Broken URL

Example:

```json
{
  "statusCode": 404,
  "isBroken": true,
  "statusError": null
}
```

HTTP error responses such as:

```text
400
401
403
404
410
429
500
503
```

are represented by their actual response status.

The Actor currently marks responses with status code **400 or higher** as broken.

***

### ⚠️ Request failure

If the target cannot be checked because of a network/request failure:

```json
{
  "statusCode": null,
  "isBroken": true,
  "statusError": "Request error information"
}
```

This distinguishes:

```text
HTTP error response
```

from:

```text
No valid HTTP response received
```

***

## ↪️ Redirect detection

Redirecting links are preserved instead of automatically hiding the original response.

For example:

```text
https://example.com/old-page
```

returns:

```text
301
```

with:

```text
Location: /new-page
```

The Actor can return:

```json
{
  "targetUrl": "https://example.com/old-page",
  "statusCode": 301,
  "isBroken": false,
  "isRedirect": true,
  "redirectUrl": "https://example.com/new-page"
}
```

This is useful because SEO audits often need to identify pages that are **still linking to redirected URLs**.

***

### Why redirects matter

Imagine:

```text
Homepage
   ↓
/old-services
   ↓ 301
/new-services
```

The website technically works, but the internal link is outdated.

Website Link Crawler lets you identify that relationship so the original link can be updated directly to:

```text
/new-services
```

***

## 📏 Crawl-depth tracking

Every result contains:

```text
crawlDepth
```

The value describes the depth of the **source page** where the link was found.

Example:

```text
Homepage
Depth 0

↓ links to

Services
Depth 1

↓ links to

Roofing
Depth 2
```

A link discovered on the Roofing page would therefore contain:

```json
{
  "crawlDepth": 2
}
```

This can help identify:

```text
Deep pages
Site architecture problems
Important content buried too far from the homepage
Navigation complexity
Internal-link opportunities
```

***

## 🧹 URL normalization and duplicate handling

Website pages often contain repeated links.

For example:

```html
<a href="/about">About</a>
<a href="/about">Company</a>
<a href="/about#team">Our Team</a>
```

Website Link Crawler normalizes URL fragments before duplicate detection.

These:

```text
https://example.com/about
https://example.com/about#team
```

are treated as the same HTTP resource for target deduplication.

The crawler keeps only the first occurrence of the normalized target URL **within that source page**.

This avoids:

```text
Duplicate Dataset rows
Duplicate HTTP status requests
Unnecessary processing
Messy exports
```

***

### Important deduplication behavior

Deduplication is performed **per source page**.

This means:

```text
Homepage → /contact
Services → /contact
Blog → /contact
```

remain three useful relationships.

That is intentional.

They represent three different internal links even though they point to the same target.

The Actor does not globally remove these relationships.

***

## 🌐 Same-hostname crawling

Recursive crawling uses a **same-hostname strategy**.

Example:

```text
Start:
https://example.com
```

The crawler may follow:

```text
https://example.com/about
https://example.com/services
https://example.com/blog
```

but it does not recursively begin crawling:

```text
https://facebook.com/...
https://youtube.com/...
https://wikipedia.org/...
```

Those external targets can still appear in the Dataset and have their link status checked.

***

## ⚡ Performance

Website Link Crawler uses an **HTTP-first architecture** based on Crawlee and Cheerio.

It does not launch a browser for normal page crawling.

This generally means:

```text
Lower memory use
Faster startup
Lower browser overhead
Lower compute requirements
Efficient HTML parsing
```

This design is especially useful for crawling ordinary server-rendered websites.

***

## 🛡 Reliability

The Actor is designed so discovered links are processed independently.

A target status check can return:

```text
HTTP status
redirect information
request error
```

without requiring the Dataset to hide the underlying result.

The crawler also includes:

```text
Configurable page limits
Configurable depth limits
Per-page URL deduplication
Protocol filtering
Same-hostname recursion
Invalid URL protection
Request timeout protection
```

Only:

```text
http:
https:
```

links are analyzed as web targets.

Links using protocols such as:

```text
mailto:
tel:
javascript:
```

are currently skipped.

***

## 🔌 API & integrations

Website Link Crawler can be used programmatically through the **Apify API**.

You can connect Dataset results to workflows such as:

- Google Sheets
- Make
- Zapier
- CRMs
- Internal APIs
- Data warehouses
- SEO reporting systems
- Website monitoring workflows
- AI agents
- MCP workflows

Each Actor run stores **structured source → target link relationships in the default Dataset**, making the output straightforward to process with another application.

***

## 💡 Example use cases

### 🚨 Broken-link audit

Run the crawler on your website and filter:

```text
isBroken = true
```

to identify URLs that may need attention.

Useful output:

```text
Source page
Broken target
Anchor text
HTTP status
Crawl depth
```

This tells you both **what is broken** and **where the broken link exists**.

***

### ↪️ Redirect cleanup

Filter:

```text
isRedirect = true
```

to locate links pointing to redirects.

Example:

```text
Source:
https://example.com/

Target:
https://example.com/old-pricing

Redirect:
https://example.com/pricing
```

You can then update the source page to point directly to the final URL.

***

### 🧭 Internal-link audit

Filter:

```text
linkType = internal
```

to analyze relationships between pages on the website.

Use:

```text
sourceUrl
targetUrl
anchorText
crawlDepth
```

to understand the internal linking structure.

***

### 🌍 External-link audit

Filter:

```text
linkType = external
```

to review websites being linked to from your pages.

This can help identify:

```text
Dead external references
Redirecting external links
Old resources
Unexpected outbound links
```

***

### 🏷 Anchor-text analysis

Export:

```text
sourceUrl
targetUrl
anchorText
```

to inspect how internal links are labeled.

Example:

```text
/services → /contact → Contact us
/blog/article → /contact → Get a quote
/pricing → /contact → Start today
```

This can support internal-link and SEO reviews.

***

### 📏 Site-depth analysis

Use:

```text
crawlDepth
```

to find pages and links located deeper within the site's architecture.

For example:

```text
Depth 0 → Homepage
Depth 1 → Main category
Depth 2 → Subcategory
Depth 3 → Individual page
```

Deep pages may be useful candidates for additional internal links.

***

### 🚚 Website migration auditing

Before or after a website migration, crawl the site and inspect:

```text
404 links
301 redirects
Old internal URLs
Canonical URLs
Internal anchor text
Source → target relationships
```

This helps identify links that still point to outdated paths.

***

## ⚠️ Current limitations

The current version focuses on **fast HTTP-based website link analysis**.

It does **not currently perform**:

```text
JavaScript/browser-rendered link discovery
Redirect-chain traversal
Internal-link graph visualization
Site-wide orphan-page detection
XML sitemap comparison
robots.txt auditing
Hreflang auditing
Meta robots analysis
Structured-data auditing
Page-speed analysis
Full page-content extraction
Domain-level link summaries
Site-wide anchor-text summaries
Automatic scheduled historical comparisons
```

These are intentionally separate from the current MVP.

***

### JavaScript-rendered websites

The Actor currently uses:

```text
HTTP
+
Cheerio
```

instead of Playwright or Puppeteer.

If a website creates links only after JavaScript executes in the browser, those links may not appear in the HTML received by the crawler.

For ordinary server-rendered HTML websites, this is not a problem.

Browser-rendered crawling can be introduced later as a separate mode if needed.

***

## ❓ FAQ

### How does Website Link Crawler work?

The Actor starts from the URLs you provide.

It then:

```text
Downloads the source page
Parses HTML
Finds <a href> links
Normalizes target URLs
Removes duplicate targets on that page
Extracts anchor text
Classifies internal/external
Detects follow/nofollow
Checks HTTP status
Detects redirects
Stores source → target relationships
Follows eligible internal links
Tracks crawl depth
Stops at configured limits
```

***

### What is the difference between this and a normal URL crawler?

A normal crawler may return:

```text
https://example.com
https://example.com/about
https://example.com/services
```

Website Link Crawler returns relationships such as:

```text
Homepage
   ↓ "Our services"
/services
HTTP 200
Internal
Follow
Depth 0
```

This makes the output more useful for SEO audits and website maintenance.

***

### Does the Actor find backlinks?

**No.**

This Actor discovers links **on the website being crawled**.

Example:

```text
example.com/page-a
        ↓
example.com/page-b
```

A backlink tool would instead need to discover:

```text
otherwebsite.com
        ↓
example.com
```

Those are different problems.

Website Link Crawler is an **on-site link crawler**, not a global backlink index.

***

### Does it crawl external websites?

**No.**

External links are:

```text
discovered
classified
status checked
stored
```

but they are not recursively crawled.

This prevents an audit from expanding uncontrollably across unrelated websites.

***

### Does it detect broken links?

**Yes.**

The Actor marks:

```text
HTTP status >= 400
```

as broken.

It also marks targets as broken when the status request cannot be completed.

***

### Does a redirect count as a broken link?

**No.**

For example:

```text
301
302
307
308
```

are treated as redirects rather than broken HTTP errors.

A redirecting URL can therefore return:

```json
{
  "isBroken": false,
  "isRedirect": true
}
```

***

### Does it follow redirects?

The current status checker preserves the **original redirect response**.

It returns:

```text
Original status code
Redirect indicator
Redirect destination
```

Example:

```text
301
https://example.com/new-page
```

It does not currently build a full multi-hop redirect chain.

***

### Does it remove duplicate links?

**Yes, within each source page.**

For example:

```text
/about
/about
/about#team
```

are normalized and treated as one target resource on that source page.

But:

```text
Homepage → /about
Services → /about
Blog → /about
```

are all retained because each represents a different source → target relationship.

***

### Does it extract canonical URLs?

**Yes, when present in the source HTML.**

Example:

```html
<link rel="canonical" href="/preferred-page">
```

is resolved to an absolute URL.

If no canonical exists:

```text
canonicalUrl: null
```

***

### Can I control how deeply the website is crawled?

**Yes.**

Use:

```text
maxCrawlDepth
```

Current limits:

```text
Minimum: 0
Default: 2
Maximum: 20
```

***

### Can I limit how many pages are crawled?

**Yes.**

Use:

```text
maxRequestsPerCrawl
```

Current limits:

```text
Minimum: 1
Default: 100
Maximum: 100000
```

***

### Can I crawl multiple websites in one run?

**Yes.**

You can provide multiple:

```text
startUrls
```

in the same Actor input.

Each start request begins at crawl depth 0.

***

### Can I use Website Link Crawler through an API?

Yes.

Every Actor can be started through the **Apify API**, and the resulting Dataset can be consumed programmatically.

This makes Website Link Crawler suitable for:

```text
Automated SEO audits
Website QA pipelines
Migration checks
Reporting systems
Internal tools
Scheduled workflows
```

***

### Can I export the results?

Yes.

Apify Dataset results can be exported in supported formats such as:

```text
JSON
CSV
Excel
XML
HTML
```

***

### Is a browser required?

**Not for the current workflow.**

Website Link Crawler uses an HTTP-first approach with:

```text
Crawlee
Cheerio
```

which avoids launching full browser automation during normal crawls.

***

### Why is `canonicalUrl` sometimes null?

Because not every page contains a canonical tag.

The Actor returns:

```text
null
```

instead of inventing one.

***

### Why is `statusCode` sometimes null?

This means the status request did not receive a normal HTTP response.

The associated:

```text
statusError
```

field may contain additional information about the request failure.

***

## 🧭 Recommended workflow

For a normal SEO website audit, start conservatively.

Example:

```json
{
  "startUrls": [
    {
      "url": "https://example.com"
    }
  ],
  "maxCrawlDepth": 2,
  "maxRequestsPerCrawl": 500
}
```

First inspect:

```text
isBroken
isRedirect
linkType
crawlDepth
canonicalUrl
anchorText
```

Then increase the crawl depth or maximum pages if additional coverage is needed.

For very large websites, avoid immediately using extremely high limits before understanding the site's linking structure.

***

## 💬 Feedback & support

Found a bug or have a feature request?

Use the Actor's **Issues** tab on Apify.

Useful future additions may include:

```text
Redirect-chain detection
Internal-link graph
Pages with very few internal links
Pages with excessive outgoing links
External-domain summaries
Site-wide anchor-text summaries
Sitemap comparison
JavaScript rendering mode
Orphan-page analysis
Historical website audit comparisons
```

***

### 🚀 Website link auditing without manually checking every page

Enter your website URL, choose the crawl depth, run the Actor, and receive **structured source → target link data** ready for **SEO audits, broken-link cleanup, migration analysis, APIs, and automation**.

# Actor input Schema

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

One or more website URLs to start crawling from.

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

Maximum number of internal-link levels to crawl from each start URL. Depth 0 analyzes only the starting page, depth 1 also crawls links found on the starting page, and so on.

## `maxRequestsPerCrawl` (type: `integer`):

Maximum number of website pages the crawler may process during one run.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://example.com"
    }
  ],
  "maxCrawlDepth": 2,
  "maxRequestsPerCrawl": 100
}
```

# Actor output Schema

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

All discovered and analyzed website links.

# 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://example.com"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("aerodynamic_tripod/website-link-crawler").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://example.com" }] }

# Run the Actor and wait for it to finish
run = client.actor("aerodynamic_tripod/website-link-crawler").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://example.com"
    }
  ]
}' |
apify call aerodynamic_tripod/website-link-crawler --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,aerodynamic_tripod/website-link-crawler"
        }
    }
}

```

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/8ZdvFftCVdp6RXByf/builds/ZORdyjIp8anPe36Q1/openapi.json
