# SEO Audit Crawler — On-Page SEO Checker for Whole Sites (`actor-forge/seo-audit`) Actor

Crawl a whole website and audit every page for technical and on-page SEO: titles, meta descriptions, H1s, canonicals, noindex, duplicates, broken internal links, hreflang, schema, mixed content, alt text, thin content. Issues + a 0-100 score per page, plus a site summary. $6 per 1,000 pages.

- **URL**: https://apify.com/actor-forge/seo-audit.md
- **Developed by:** [ActorForge](https://apify.com/actor-forge) (community)
- **Categories:** SEO tools, Developer tools, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $6.00 / 1,000 page auditeds

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

## SEO Audit Crawler — On-Page SEO Checker for Whole Sites

Crawl a whole website and audit every page for technical and on-page SEO: titles, meta descriptions, H1s, canonicals, noindex, duplicates, broken internal links, hreflang, schema, mixed content, alt text, thin content. Issues + a 0-100 score per page, plus a site summary. $6 per 1,000 pages.

**Run a full SEO audit of your website, every page, in one click, for a fraction of what other site audit tools charge.** Give it your site's URL and it crawls your pages the way a search engine does (following links and your sitemap, respecting robots.txt) and runs a **technical SEO and on-page SEO check on every page**: HTTP status and **redirect chains**, response time, **indexability** (meta robots and X-Robots-Tag noindex/nofollow, **canonical** pointing to itself, elsewhere or missing, robots.txt), **title** and **meta description** (missing, too long, too short and **duplicated across the site**), **H1** (missing, empty, multiple) and the H2/H3 outline, **word count and thin content**, **images missing alt text** (with their URLs), internal, external and nofollow link counts, **broken internal links** and **links that go through redirects**, **inlinks** per page, **hreflang** (invalid codes, missing self-reference and x-default, alternates that don't link back), **Open Graph and Twitter cards**, **schema.org structured data** (JSON-LD types and invalid blocks), **mixed content**, viewport, lang, charset, HTTPS and URL hygiene (uppercase, underscores, length, query strings). Each page gets a list of **issues with severity (error, warning, notice)** and a plain message, plus a **0-100 SEO score**. The run's summary adds the site-level view: average score, **top issues** by how many pages they hit, **duplicate title and description groups**, the most-linked broken URLs, **orphan pages** (in your sitemap but linked from nowhere) and whether robots.txt and a sitemap exist. A lightweight **Screaming Frog alternative** that runs in the cloud: no install, no browser, export to JSON, CSV or Excel, schedule it weekly and watch your score. **$6 per 1,000 pages audited** ($0.60 for a 100-page site); pages that answer an error are reported for free.

### What data does it return?

Every run outputs clean, schema-validated rows like these:

| url | score | statusCode | indexable | titleLength | metaDescriptionLength | h1Count | wordCount | inlinks | issues |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| https://crawlee.dev/js/docs/guides/motivation | 83 | 200 | true | 68 | 146 | 1 | 8 | 0 | meta-description-duplicate, thin-content, title-too-long, orphan-page, structured-data-missing |
| https://crawlee.dev/js/docs/guides/cheerio-crawler-guide | 89 | 200 | true | 78 | 56 | 1 | 622 | 19 | meta-description-duplicate, title-too-long, meta-description-too-short |
| https://crawlee.dev/js/docs/guides/custom-http-client | 94 | 200 | true | 99 | 66 | 1 | 474 | 19 | title-too-long, meta-description-too-short |
| https://crawlee.dev/js/docs/quick-start | 95 | 200 | true | 69 | 118 | 1 | 1207 | 21 | title-too-long |

### How much does it cost?

Transparent pay-per-event pricing — you only pay for what you get, and the
actor **always respects your run budget**: set a maximum total charge and it
stops cleanly at the cap instead of overrunning.

| Event | Price | Charged | Worked example |
| --- | --- | --- | --- |
| `page-audited` | $0.006 | per HTML page of your site fetched and audited (one dataset row) | 100 pages = $0.60; a 1,000-page site = $6.00. Pages that answer 4xx/5xx or time out are reported free |
| `apify-actor-start` | $0.00005 | per run per GB of memory (platform start fee) | One run at 1 GB = $0.00005 |

### Input options

| Field | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `startUrls` | array | yes | — | Where to start the audit. The crawl follows links on the same site and stays under each URL's path: https://example.com audits the whole site, https://example.com/blog only the blog. |
| `maxPages` | integer | no | `200` | Stop after auditing this many HTML pages. Each audited page is one 'page-audited' event; pages that answer an error are reported for free and don't count. |
| `maxDepth` | integer | no | `20` | How many clicks away from a start URL to crawl. 0 = audit only the start pages. |
| `includeSubdomains` | boolean | no | `false` | Also audit subdomains of the start site (e.g. blog.example.com when starting at example.com). |
| `includeGlobs` | array | no | `[]` | Only audit pages matching one of these, e.g. https://example.com/docs/\*\* (\*\* = anything, \* = anything except /). Replaces the default 'stay under the start path' rule. |
| `excludeGlobs` | array | no | `[]` | Never audit pages matching these, e.g. **/tag/** or \**/page/* (archive and pagination pages). |
| `useSitemap` | boolean | no | `true` | Read sitemap.xml (and sitemaps listed in robots.txt). After the pages reachable by links, sitemap URLs that no page links to are audited too and reported as orphan pages. |
| `respectRobotsTxt` | boolean | no | `true` | Don't audit pages that robots.txt disallows. Turn off to audit them anyway (they are then flagged 'blocked-by-robots-txt', since search engines can't crawl them). |

### Limits & honest notes

- Pages are fetched as static HTML without a browser: fast and cheap, and it covers almost all websites. Content, links and tags that exist only after JavaScript runs (single-page apps that render client-side) are not seen, which is also how many search engine and AI crawlers see your site. Core Web Vitals and other browser performance metrics are not measured.
- Thresholds: titles 30-60 characters, meta descriptions 70-160 characters, thin content under 200 words (counted in the page body without navigation, header, footer, sidebars and scripts), URLs over 115 characters, responses slower than 2 seconds.
- The score starts at 100 and loses 10 points per distinct error, 5 per warning and 1 per notice. Duplicate titles and descriptions are compared across indexable pages only (noindex and canonicalised copies are expected to repeat), case and spacing aside.
- Broken internal links and links to redirects are found from the pages the crawl fetched; internal links beyond your page limit, and external links, are not checked (use a broken link checker for that). Orphan pages are sitemap URLs no audited page links to, so they are only conclusive when the crawl covered the whole site (orphanCheckComplete in the summary).
- Crawling respects robots.txt and stays on your site and under the start URL's path, unless you set include globs. Pages behind a login are not supported. Polite by design: at most 3 requests at a time to your site, and a 429 'Too Many Requests' pauses it for its Retry-After. Pages refused by bot protection are reported as 'fetch-blocked' rows, free.
- Rows are delivered when the crawl finishes, since duplicates, broken links, hreflang return links and inlinks need every page; runs resume after a platform migration without re-charging work already done. Pages larger than 5 MB and non-HTML files are skipped.
- Links to internal network addresses (localhost, private IPs, cloud metadata) are never requested and never charged.
- Respects your run budget: set a maximum total charge and the run stops cleanly at the cap and delivers every page audited so far.

### Related ActorForge actors

- [Broken Link Checker](https://apify.com/actor-forge/broken-link-checker) — every dead link with the pages it is on
- [Sitemap URL Extractor](https://apify.com/actor-forge/sitemap-extractor)
- [Tech Stack Detector](https://apify.com/actor-forge/tech-stack-detector)
- [Website Screenshot API](https://apify.com/actor-forge/website-screenshot)

### Changelog

- **2026-10-19** — Initial release: same-site crawl with sitemaps, robots.txt and globs; 40+ technical and on-page checks per page with severity and a 0-100 score; duplicate titles and descriptions, broken internal links, links to redirects, hreflang return links, inlinks and orphan sitemap pages across the site; site summary with top issues; migration-safe resume.

***

*Built and maintained by **ActorForge**. Reliability first: every actor in the
portfolio is monitored by an automated nightly self-test, and breakages are
typically fixed the same day.*

# Actor input Schema

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

Where to start the audit. The crawl follows links on the same site and stays under each URL's path: https://example.com audits the whole site, https://example.com/blog only the blog.

## `maxPages` (type: `integer`):

Stop after auditing this many HTML pages. Each audited page is one 'page-audited' event; pages that answer an error are reported for free and don't count.

## `maxDepth` (type: `integer`):

How many clicks away from a start URL to crawl. 0 = audit only the start pages.

## `includeSubdomains` (type: `boolean`):

Also audit subdomains of the start site (e.g. blog.example.com when starting at example.com).

## `includeGlobs` (type: `array`):

Only audit pages matching one of these, e.g. https://example.com/docs/\*\* (\*\* = anything, \* = anything except /). Replaces the default 'stay under the start path' rule.

## `excludeGlobs` (type: `array`):

Never audit pages matching these, e.g. **/tag/** or \**/page/* (archive and pagination pages).

## `useSitemap` (type: `boolean`):

Read sitemap.xml (and sitemaps listed in robots.txt). After the pages reachable by links, sitemap URLs that no page links to are audited too and reported as orphan pages.

## `respectRobotsTxt` (type: `boolean`):

Don't audit pages that robots.txt disallows. Turn off to audit them anyway (they are then flagged 'blocked-by-robots-txt', since search engines can't crawl them).

## Actor input object example

```json
{
  "startUrls": [
    "https://crawlee.dev/js/docs/guides"
  ],
  "maxPages": 200,
  "maxDepth": 20,
  "includeSubdomains": false,
  "includeGlobs": [],
  "excludeGlobs": [],
  "useSitemap": true,
  "respectRobotsTxt": true
}
```

# Actor output Schema

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

One row per page: score, issues with severity, indexability, title, meta description, headings, word count, images, links, hreflang, social tags and structured data.

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

Pages audited, average score, top issues, issues by code, duplicate titles and descriptions, broken internal links, orphan sitemap pages, robots.txt and sitemap status.

# 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": [
        "https://crawlee.dev/js/docs/guides"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("actor-forge/seo-audit").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": ["https://crawlee.dev/js/docs/guides"] }

# Run the Actor and wait for it to finish
run = client.actor("actor-forge/seo-audit").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": [
    "https://crawlee.dev/js/docs/guides"
  ]
}' |
apify call actor-forge/seo-audit --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,actor-forge/seo-audit"
        }
    }
}
```

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/L5WGzn0yupy6m9dt8/builds/68tLAUb9gPehogftL/openapi.json
