# Bulk SEO & Broken Link Checker - DigestSEO (`digestseo/bulk-seo-broken-link-checker-digestseo`) Actor

Audit up to 1,000 public webpage URLs for technical SEO metadata issues, JSON-LD errors, basic accessibility signals, and broken links (up to 20 sampled links per page). Export structured results with HTTP statuses, redirects, and categorized findings. No external API key required.

- **URL**: https://apify.com/digestseo/bulk-seo-broken-link-checker-digestseo.md
- **Developed by:** [DigestSEO](https://apify.com/digestseo) (community)
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 2 bookmarks
- **User rating**: No ratings yet

## Pricing

$1.00 / 1,000 completed page audits

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

## Bulk SEO & Broken Link Checker — DigestSEO

Audit a list of public webpages for technical SEO issues, markup-based accessibility signals, JSON-LD errors, and selected broken-link observations. Get a clear page-by-page dataset **plus a downloadable issue report**. No external SEO API key is required.

**Best for:** bulk pre-launch checks, website migration QA, content audits, and checking a specific list of URLs. Submit **1–1,000 public HTTP/HTTPS page URLs** per run. You pay for completed page audits under the current Apify Store event pricing; incomplete/skipped/failed pages do not trigger the `page-audit` event.

### Quick start

1. Open the Actor on Apify and go to **Input**.
2. Add public webpage URLs in the **Webpage URLs** field (or use **JSON**).
3. Keep **Check links** enabled to inspect selected links. Choose **Maximum links per page** between 1 and 20 (default: 10).
4. Run the Actor. For large inputs, configure enough time in **Run options** (for example, 3,600 seconds for a thousand-page test) and choose a spending limit you're comfortable with.
5. Open **Output** for page results and issue exports. Inspect **Run summary** and **Continuation** to see what was actually processed.

#### Example input

```json
{
  "urls": [
    "https://digestseo.com/",
    "https://digestseo.com/keyword-research/"
  ],
  "checkLinks": true,
  "maxLinksPerPage": 10
}
```

The Actor accepts an explicit URL list. It **does not discover a sitemap, crawl an entire domain, or follow links to add new pages**. Link checks inspect **up to 20 selected candidates per page**, giving main content links priority; they are not exhaustive.

### What gets checked

- **Technical SEO metadata:** titles, meta descriptions, canonicals, index directives, headings, and other checks supported by the pinned validator.
- **Basic accessibility signals:** markup-level observations such as missing accessible image names. **Not a complete WCAG audit.**
- **JSON-LD markup:** syntax and document-shape issues. **Not a full Schema.org or rich-result eligibility test.**
- **Selected link responses:** reachable, confirmed not-found, redirected, restricted, rate-limited, transient, unreachable, and skipped-by-policy. A `HEAD` 404/410 is confirmed using bounded `GET` before being treated as broken.

### Download your results

| Output | What it contains |
| --- | --- |
| **Page audit results** (default dataset) | One result per unique submitted URL, including status, findings, link observations, and coverage flags. |
| **Actionable issues (CSV)** — `ISSUES.csv` | Spreadsheet-friendly rows with affected page, issue severity/code, linked target when applicable, observed HTTP status, and an action to review. |
| **Actionable issues (JSON)** — `ISSUES.json` | The same prioritized issues as structured data, plus omission counts and coverage/truncation disclosures. |
| **Run summary** — `SUMMARY.json` | Completed, failed, skipped, partial and unprocessed page counts; limits, stop reason and charged-event count. |
| **Continue unfinished audit** — `CONTINUATION.json` | Always states whether work remains. When incomplete it contains an `input` object with *only unprocessed URLs*. |
| **Next batch input** — `NEXT_BATCH_INPUT.json` | Available in **Storage → Key-value store** only when URLs remain; copy its entire JSON into a new run's input. |

The issue export includes reported page/markup findings, relevant link observations and execution errors. **Access restrictions, redirects, timeouts and robots-skipped links are not automatically broken links.** Recommendations are review prompts, not changes made to your website. Error-level issues appear first in the export. If result sizes are capped, `ISSUES.json` explicitly reports omitted rows and affected pages; consult the original dataset for detail.

#### When your run stops early

A large input may stop at the run's time or event-spending limit without failing. Open **Storage → Key-value store → NEXT_BATCH_INPUT.json** and paste that JSON into a **new** run. It preserves `checkLinks` and `maxLinksPerPage` and contains only URLs that never received a persisted page result. Already processed pages, including partial or failed outcomes, are not repeated automatically. Review `SUMMARY.json` to understand why processing stopped.

If you **resurrect** the same run instead, its checkpoint protects already-persisted results and charge replay. Never remove or edit that run's checkpoint manually. A completed run removes any stale `NEXT_BATCH_INPUT.json`.

### Interpret findings correctly

- **Not found:** bounded checks returned 404/410; verify before updating your source link.
- **Redirect:** the linked URL redirects; its final destination was not followed or validated.
- **Restricted / rate-limited / unreachable / transient:** access or availability couldn't be confirmed. Do **not** automatically delete those links.
- **Skipped:** the target was excluded by network/robots policy; no HTTP link probe was made.
- **Metadata length notices:** editorial hints, not ranking penalties or a requirement to extend text to a fixed length.

#### Important limits and privacy

Public webpages only. No login, cookies, private-network access, browser/JavaScript rendering, screenshot capture, authenticated crawling, proxy bypass, AI-generated fixes, SERP/keyword/rank/backlink data, or SEO ranking guarantees. Remote robots.txt and public-network restrictions are respected.

The Actor does not store full fetched HTML documents, credentials, cookies, or screenshots and does not send results to DigestSEO. Individual findings may contain short evidence snippets from page markup. Run inputs and results are stored by Apify according to your account's platform retention and data settings.

For support and additional tools visit [DigestSEO](https://digestseo.com/) or [DigestSEO support](https://digestseo.com/support/).

*Developer and security implementation details are maintained separately from the customer-facing Store README.*

# Actor input Schema

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

List of 1-1,000 public HTTP/HTTPS webpage URLs. A large batch may stop early at its spending or runtime limit; review SUMMARY.json for unprocessed pages. Fragments are removed and duplicate normalized URLs are ignored.

## `checkLinks` (type: `boolean`):

Run bounded public link/status observations after the page audit. Robots.txt restrictions are respected before probes.

## `maxLinksPerPage` (type: `integer`):

Maximum number of unique eligible link candidates considered per page. Used only when link checks are enabled.

## Actor input object example

```json
{
  "urls": [
    "https://digestseo.com/"
  ],
  "checkLinks": true,
  "maxLinksPerPage": 10
}
```

# Actor output Schema

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

Default dataset containing one bounded outcome per deduplicated requested URL that reached result persistence.

## `summary` (type: `string`):

SUMMARY.json with counts, coverage, effective limits, billing event count, and stop reason.

## `issuesCsv` (type: `string`):

Downloadable severity-prioritized page and link findings, with suggested review actions; rows are capped and coverage limitations are in ISSUES.json.

## `issuesJson` (type: `string`):

Machine-readable issue rows, counts, and truncation/coverage notes.

## `continuation` (type: `string`):

Always-present status and next-batch input. If URLs remain, copy input or use NEXT_BATCH_INPUT.json from the key-value store.

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

// Run the Actor and wait for it to finish
const run = await client.actor("digestseo/bulk-seo-broken-link-checker-digestseo").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://digestseo.com/"] }

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

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,digestseo/bulk-seo-broken-link-checker-digestseo"
        }
    }
}
```

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/YedctgxL6k26NyRx0/builds/SiVXasG8QS9Va1zQ6/openapi.json
