# Page Link Extractor: all links, anchor text, nofollow (`examsens.ai/page-link-extractor`) Actor

Lists every link on the pages you give it, internal and external, with anchor text, rel values, nofollow, sponsored and ugc flags, target and image link alt text. Many pages in one run, one row per link or one row per page.

- **URL**: https://apify.com/examsens.ai/page-link-extractor.md
- **Developed by:** [Ed Rayyis](https://apify.com/examsens.ai) (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

from $7.00 / 1,000 page processeds

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

## Page Link Extractor: all links, anchor text, nofollow

Paste in a list of page URLs and you get back every link on each page. Each link comes with its anchor text, its rel values and a flag for internal or external.

Each page is fetched once with redirects followed, and the HTML is read as the server sends it. Scripts are not run, so a link that JavaScript adds after the page loads won't show up.

### What you get for each link

- `linkUrl`, the absolute URL, resolved against the page address or its `<base href>`, with the raw `href` kept beside it
- `anchorText`. For a link wrapped around an image with no text, the image's alt text is used, and `isImageLink` and `imageAlt` are set.
- `linkType`: `internal` when the link's host matches the page's host (a leading `www.` is ignored), `external` otherwise, and `other` for mailto, tel and similar links when you ask for them
- `rel` as a list, plus `nofollow`, `sponsored` and `ugc` as true or false
- `target`, the `title` attribute, the link host and scheme, whether it came from an `<a>` or an image map `<area>`, and its position on the page

Google lets a site mark a link with `sponsored`, `ugc` or `nofollow` and accepts several values in one attribute, separated by spaces or commas \[1]. Both forms are read here.

Every row also carries the page it came from: `pageUrl`, `finalPageUrl` after redirects, the HTTP `status`, and that page's totals for links, internal, external, nofollow and unique URLs.

### Input

- Page URLs (required). An address without `http://` or `https://` is read as `https://`.
- Links to return: all, internal only or external only.
- Output format. One row per link is the default and suits a spreadsheet. One row per page puts the links in a nested `links` list.
- One row per unique URL on a page, off by default. When on, repeated links to the same URL merge into one row with an `occurrences` count.
- Count subdomains as internal, off by default. With it on, `blog.example.com` counts as internal on `example.com`.
- Remove #fragments from link URLs (on), skip links to a spot on the same page such as `#top` (on), include mailto and tel links (off).
- Timeout per page (20 seconds), pages read at once (10) and the user agent.

`javascript:` and `data:` links are always left out.

### Output

The dataset has the rows described above. The Overview view shows one line per link and the Pages view shows the page totals. A page that answers 400 or above, isn't HTML, can't be reached or isn't a valid URL gets one row with `ok` false and the reason in `error`, and the run goes on to the next page. A page that has no links of the types you picked gets one row with `linkUrl` empty and a note.

The key value store holds an `OUTPUT` record with pages processed, pages failed, links found and whether the run stopped at your spending limit.

### Pricing

Pay per event: one event per page processed, whatever the number of links on it. Failed pages aren't charged. When the run reaches the maximum charge you set, it stops before reading the next page, and every row in the dataset belongs to a page that was paid for.

### Scope and conduct

It reads only the pages you list. It doesn't follow the links it finds, log in or submit anything. Addresses that resolve to private or local networks are refused.

### Support

Questions and problems: the Issues tab on this page. Replies are written by an AI assistant on behalf of the developer and reviewed by the developer.

### References

\[1] Google Search Central, Qualify your outbound links to Google, sections "rel values" and "Multiple values". https://developers.google.com/search/docs/crawling-indexing/qualify-outbound-links

# Actor input Schema

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

Pages to read, one per line. An address without http:// or https:// is read as https://. Redirects are followed.

## `linkTypes` (type: `string`):

Internal means the link's host matches the page's host, ignoring a leading www.

## `outputFormat` (type: `string`):

Both cost the same: one event per page processed.

## `uniqueLinksOnly` (type: `boolean`):

Merge repeated links to the same URL on a page into one row with an occurrences count.

## `subdomainsAreInternal` (type: `boolean`):

Treat links to subdomains of the page's host (and its parent host) as internal.

## `removeFragments` (type: `boolean`):

Return https://site.com/page for https://site.com/page#section. The raw href is always kept.

## `skipSamePageAnchors` (type: `boolean`):

Leave out links such as #top that point back to the page being read.

## `includeNonHttpLinks` (type: `boolean`):

Return mailto:, tel:, ftp: and similar links as type other. javascript: and data: links are always left out.

## `timeoutSecs` (type: `integer`):

How long to wait for each page.

## `maxConcurrency` (type: `integer`):

How many pages are read at the same time.

## `userAgent` (type: `string`):

The User-Agent header sent with every request. Leave empty to send Mozilla/5.0 (compatible; PageLinkExtractor/1.0).

## Actor input object example

```json
{
  "urls": [
    "https://www.python.org/",
    "https://en.wikipedia.org/wiki/Hyperlink"
  ],
  "linkTypes": "all",
  "outputFormat": "links",
  "uniqueLinksOnly": false,
  "subdomainsAreInternal": false,
  "removeFragments": true,
  "skipSamePageAnchors": true,
  "includeNonHttpLinks": false,
  "timeoutSecs": 20,
  "maxConcurrency": 10
}
```

# 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://www.python.org/",
        "https://en.wikipedia.org/wiki/Hyperlink"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("examsens.ai/page-link-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://www.python.org/",
        "https://en.wikipedia.org/wiki/Hyperlink",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("examsens.ai/page-link-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://www.python.org/",
    "https://en.wikipedia.org/wiki/Hyperlink"
  ]
}' |
apify call examsens.ai/page-link-extractor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,examsens.ai/page-link-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/zyMhJG0F8R5jeY3qD/builds/u4Ltiii9bOlQesoJi/openapi.json
