# Anchor Text & Link Extractor (`plainpipe/anchor-text-extractor`) Actor

Extract every link from the pages you name, with anchor text, nofollow/sponsored/ugc flags and internal or external. Use when you need a page's outbound links.

- **URL**: https://apify.com/plainpipe/anchor-text-extractor.md
- **Developed by:** [Plain Pipe](https://apify.com/plainpipe) (community)
- **Categories:** SEO tools, Developer tools
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$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?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Anchor Text & Link Extractor

Extract every link from the pages you name: absolute URL, anchor text or image alt, nofollow/sponsored/ugc flags, target, internal or external, and domain.

### What it does

Give it a list of page URLs. It fetches each page once (robots.txt respected) and returns one row per link found on that page: the absolute link URL, the cleaned anchor text, the rel flags (nofollow, sponsored, ugc), the target attribute, whether the link stays on the same domain, the link's domain and its position on the page. For image links the anchor text is the image's alt text and the row is marked `"anchorType": "image"`.

It only reads the pages you list. It does not crawl a site and it is not a database of links pointing to a site from elsewhere.

### Use cases

- Review internal linking and anchor text on your own or a client's key pages.
- Check which outbound links carry `nofollow`, `sponsored` or `ugc` before publishing.
- Find image links with empty alt text (`anchorType: "image"` with an empty `anchorText`).
- List every external domain a page links to, for example to review partner and affiliate links.
- Export a page's navigation and footer links to compare two versions of a site.

### How to use it

1. Paste the page URLs into **Page URLs**, one per line.
2. Optionally tick **Only external links**, or untick **Remove duplicates** to keep repeated links.
3. Set **Max results** to cap the number of link rows, and with it the cost.
4. Run the Actor. Results land in the dataset; export them as JSON, CSV or Excel, or read them through the Apify API.

### Input

| Field | What it is | Default | Example |
|---|---|---|---|
| `urls` (required) | Pages to read. `example.com` is read as `https://example.com`. Invalid entries are skipped with a log warning. | none | `["https://example.com/blog/post"]` |
| `onlyExternal` | Return only links to other domains. | `false` | `true` |
| `dedupe` | Within one page, keep each URL + anchor text pair once. | `true` | `false` |
| `maxItems` | Stop after this many link rows in total. | `100` | `2000` |

Example input:

```json
{ "urls": ["https://docs.apify.com"], "onlyExternal": false, "dedupe": true, "maxItems": 20 }
```

### Output

One item per link. Real rows from a run on `https://docs.apify.com`:

```json
[
  {
    "pageUrl": "https://docs.apify.com/",
    "finalPageUrl": "https://docs.apify.com/",
    "position": 3,
    "href": "https://console.apify.com/",
    "anchorText": "Get started",
    "anchorType": "text",
    "rel": null,
    "nofollow": false,
    "sponsored": false,
    "ugc": false,
    "target": null,
    "isInternal": false,
    "linkDomain": "console.apify.com"
  },
  {
    "pageUrl": "https://docs.apify.com/",
    "finalPageUrl": "https://docs.apify.com/",
    "position": 5,
    "href": "https://docs.apify.com/actors",
    "anchorText": "Actors",
    "anchorType": "text",
    "rel": "dofollow",
    "nofollow": false,
    "sponsored": false,
    "ugc": false,
    "target": "_self",
    "isInternal": true,
    "linkDomain": "docs.apify.com"
  }
]
```

| Field | Meaning |
|---|---|
| `href` | The link, resolved against the page (and any `<base href>`), with the `#fragment` removed. |
| `anchorText`, `anchorType` | Visible text; `text`, `image` (alt or svg title) or `empty` (falls back to `aria-label` or `title`). |
| `rel`, `nofollow`, `sponsored`, `ugc` | The raw `rel` value and its three SEO flags. |
| `isInternal`, `linkDomain` | Same host as the page (ignoring `www.`; subdomains count as external) and the link's host. |
| `position` | 1-based index among the web links on the page, counted before filtering and de-duplication. |

### Pricing

Pay per result: **$1.00 per 1,000 results** ($0.001 per result). One result is one link row. You only pay for rows the Actor returns. Set a maximum total charge on the run to cap your spend.

### Limits

- Reads the HTML the server returns. It does not run JavaScript, so links added in the browser are not seen.
- One fetch per URL: it does not follow links or crawl the site.
- Skips `#` anchors, `javascript:`, `mailto:`, `tel:` and other non-web links.
- Pages blocked by robots.txt, non-HTML responses and errors produce no rows and cost nothing. If no page yields a link, the run fails with zero results.

### FAQ

**How much does it cost?** $1.00 per 1,000 link rows. A typical page has 50 to 300 links; use **Max results** to cap the total.

**Does it find backlinks to my site?** No. It lists the links on the pages you give it. It is not a backlink database.

**Why are there fewer rows than links on the page?** **Remove duplicates** is on by default, and **Only external links** drops internal ones. `position` still shows each link's place on the full page.

**Need the list of pages first?** See Plain Pipe's Sitemap URL Extractor. For the on-page tags of the same pages, see Plain Pipe's SEO Page Audit & On-Page Checker.

**Can I run it on a schedule or through the API?** Yes, with Apify Schedules, the Apify API, webhooks and integrations such as Zapier, Make or spreadsheet tools.

### Legal and data

This Actor processes only what you give it or public, non-personal technical data. It does not log in anywhere, does not collect personal data, and does not bypass paywalls, CAPTCHAs or access controls. You are responsible for having the right to process the inputs you provide.

### Support

Open an issue on the Issues tab. Issues are answered within 14 days.

**Use this when:** you need every link on a set of pages with its anchor text and rel flags, for a link audit or outbound-link review.

### Why this one

- $1.00 per 1,000 results: one result is one link row; pages that fail cost nothing.
- Each row has the absolute URL (resolved against `<base href>`, fragment removed), anchor type (text, image alt or empty with `aria-label` fallback), raw `rel` plus nofollow, sponsored and ugc flags, link domain and on-page position.
- Plain HTTP fetch with robots.txt respected: no browser and no proxy needed; the 20-row prefill run finishes in a few seconds.
- The prefilled input is test-run daily.

Built and maintained by Plain Pipe.

# Actor input Schema

## `urls` (type: `array`):

Pages to read, one per line, e.g. https://example.com/blog/post. A bare domain is read as https://. Each page is fetched once and robots.txt is respected. Every link on the page becomes one result row.

## `onlyExternal` (type: `boolean`):

Return only links that point to another domain than the page itself (www. is ignored, other subdomains count as external). Default off.

## `dedupe` (type: `boolean`):

Within one page, return each URL + anchor text pair only once (the first occurrence keeps its position). Default on; turn off to see every repeated link.

## `maxItems` (type: `integer`):

Stop after this many link rows in total, e.g. 2000. You are charged per link row, so this also caps the cost. Default 100.

## Actor input object example

```json
{
  "urls": [
    "https://docs.apify.com"
  ],
  "onlyExternal": false,
  "dedupe": true,
  "maxItems": 20
}
```

# Actor output Schema

## `results` (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 = {
    "urls": [
        "https://docs.apify.com"
    ],
    "maxItems": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("plainpipe/anchor-text-extractor").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 = {
    "urls": ["https://docs.apify.com"],
    "maxItems": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("plainpipe/anchor-text-extractor").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 '{
  "urls": [
    "https://docs.apify.com"
  ],
  "maxItems": 20
}' |
apify call plainpipe/anchor-text-extractor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,plainpipe/anchor-text-extractor"
        }
    }
}
```

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/XHObv7rsqOPnfsvPv/builds/SzURvE4TDdfHD4pSw/openapi.json
