# Website SEO Audit Tool - On-Page SEO Checker, Meta & Speed (`santhej/website-seo-audit`) Actor

Instant on-page SEO audit for any list of URLs: title & meta tags, H1s, word count, internal/external links, load time, on-page score & a prioritized list of SEO issues. Bulk-check pages. Clean JSON/CSV for audits & reports. No API keys.

- **URL**: https://apify.com/santhej/website-seo-audit.md
- **Developed by:** [Santhej Kallada](https://apify.com/santhej) (community)
- **Categories:** SEO tools, Developer tools, MCP servers
- **Features:** Limited permissions
- **Stats:** 45 total users, 15 monthly users, 92.4% runs succeeded, 1 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $4.00 / 1,000 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.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## Website SEO Audit — On-Page Analyzer for Title, Meta, Links, Speed & Issues

**Audit any page's on-page SEO in seconds — or crawl a whole site. Get the title, meta description, headings, link counts, word count, load time, an on-page score and a list of detected SEO issues — for one URL, a list of URLs, or an entire website.**

### What does Website SEO Audit do?

It runs a technical on-page SEO check in one of two modes:

- **Instant** — paste a list of page URLs (up to 1,000) and get one audit row per URL.
- **Crawl** — give one starting URL or domain and a page cap (up to 50). The Actor crawls the site and returns a **site-summary** row (site score, pages with issues, broken links and resources, duplicate titles and descriptions, 4xx/5xx counts) followed by an audit row for every crawled page, worst pages first.

Use it for site audits, client reports, migrations and pre-publish QA.

### What you get per page

| Field | Description |
|---|---|
| `onpage_score` | On-page health score, 0–100 |
| `status_code`, `is_redirect`, `is_https` | HTTP status, redirect flag, HTTPS flag |
| `title`, `title_length` | Title tag and its length |
| `description`, `description_length` | Meta description and its length |
| `h1`, `h1_count` | First H1 and number of H1 tags |
| `word_count` | Words of visible text |
| `internal_links`, `external_links` | Link counts |
| `broken_links` | `1` if the page links to a broken URL, `0` if not |
| `images_count`, `images_without_alt` | Image count; `images_without_alt` is `0` when every image has alt text, otherwise `null` and the `no_image_alt` issue is raised |
| `canonical` | Canonical URL |
| `has_structured_data` | Whether the page carries schema.org / microdata markup |
| `duplicate_title`, `duplicate_description` | Whether another crawled page shares the title / description (meaningful in crawl mode) |
| `load_time_ms`, `dom_complete_ms` | Total load time and DOM-complete time, in ms |
| `ttfb_ms` | Time to first byte — measured when **Render JavaScript** is on, otherwise `null` |
| `issues`, `issue_count` | Detected problems, e.g. `no_title`, `title_too_long`, `title_too_short`, `no_description`, `no_h1_tag`, `no_image_alt`, `high_loading_time`, `is_redirect`, `is_4xx_code`, `is_5xx_code`, `is_broken`, `low_content_rate`, `has_render_blocking_resources`, `duplicate_meta_tags`, `deprecated_html_tags` |

`largest_contentful_paint_ms` and `cumulative_layout_shift` are part of the row but are `null`: they require full browser rendering, which this Actor does not perform.

### What you get for the whole site (crawl mode)

The first row of a crawl has `"type": "site-summary"`: `pages_crawled`, `onpage_score`, `avg_page_score`, `pages_with_issues`, `broken_links`, `broken_resources`, `links_internal`, `links_external`, `duplicate_title` and `duplicate_description` (number of pages affected), `count_4xx`, `count_4xx_pages`, `count_5xx_pages` and `no_image_alt` (pages with images missing alt text).

### Use cases

- **SEO audits** — bulk-check a whole site or a content batch.
- **Client reports** — export a clean issue list per page.
- **Pre-publish QA** — catch missing meta, missing H1s and broken pages before they go live.
- **Migrations** — verify titles, canonicals and status codes after a move.
- **Competitor benchmarking** — compare your landing pages with competitors' on the same checks.

### How to use

1. Pick a **Mode**: `instant` (a list of URLs) or `crawl` (one site).
2. Instant: paste your **Page URLs** (with `https://`). Crawl: set **Site to crawl** and **Max pages to crawl**.
3. Optionally turn on **Render JavaScript** for JavaScript-heavy sites (slower; also measures TTFB).
4. Click **Start**, then export JSON, CSV or Excel, or pull the dataset via the Apify API.

#### Example input — instant

```json
{ "mode": "instant", "urls": ["https://www.notion.com/pricing", "https://slack.com/pricing"], "enableJavaScript": false }
```

#### Example input — crawl

```json
{ "mode": "crawl", "target": "https://basecamp.com", "maxCrawlPages": 25 }
```

#### Input configuration

| Field | Type | Default | Description |
|---|---|---|---|
| `mode` | string | `instant` | `instant` audits the listed URLs; `crawl` crawls one site. |
| `urls` | array of strings | — | Page URLs to audit in instant mode, up to 1,000. |
| `target` | string | — | Domain or URL to crawl in crawl mode. |
| `maxCrawlPages` | integer | `25` | Pages to crawl in crawl mode, 1–50. |
| `enableJavaScript` | boolean | `false` | Render pages with JavaScript before auditing. |

#### Example output

A real instant-mode row:

```json
{
  "type": "page",
  "url": "https://www.notion.com/pricing",
  "status_code": 200,
  "onpage_score": 97,
  "title": "Notion Pricing Plans: Free, Plus, Business, & Enterprise.",
  "title_length": 57,
  "description": "Learn more about our different pricing plans. We support everyone, from free personal accounts to enterprise businesses.",
  "description_length": 120,
  "h1": "One tool to run your company.",
  "h1_count": 1,
  "internal_links": 87,
  "external_links": 10,
  "images_count": 10,
  "images_without_alt": null,
  "word_count": 3430,
  "is_https": true,
  "canonical": "https://www.notion.com/pricing",
  "has_structured_data": false,
  "broken_links": 0,
  "duplicate_title": false,
  "duplicate_description": false,
  "is_redirect": false,
  "ttfb_ms": null,
  "dom_complete_ms": 38,
  "largest_contentful_paint_ms": null,
  "cumulative_layout_shift": null,
  "load_time_ms": 38,
  "issues": ["no_image_alt", "low_content_rate", "has_render_blocking_resources", "no_image_title"],
  "issue_count": 4
}
```

A real crawl-mode `site-summary` row:

```json
{
  "type": "site-summary",
  "target": "https://basecamp.com",
  "domain": "basecamp.com",
  "crawl_progress": "finished",
  "pages_crawled": 10,
  "onpage_score": 98,
  "avg_page_score": 98,
  "pages_with_issues": 10,
  "broken_links": 0,
  "broken_resources": 0,
  "links_external": 75,
  "links_internal": 424,
  "duplicate_title": 0,
  "duplicate_description": 0,
  "count_4xx": 0,
  "count_4xx_pages": 0,
  "count_5xx_pages": 0,
  "no_image_alt": 1
}
```

### How much does a website SEO audit cost?

Pay per page. No monthly fee.

| Event | Price | When it is charged |
|---|---|---|
| Actor start | $0.001 | Once per run |
| Page audit | **$0.005** | Per URL audited in instant mode |
| Crawl page | **$0.004** | Per page returned by a crawl (the site-summary row is free) |

A URL that could not be audited is still returned (with an `issues` entry such as `audit_failed`) so every URL is accounted for, but it is not charged.

- Instant audit of 100 URLs: $0.001 + 100 × $0.005 = **$0.501**.
- Crawl of 25 pages: $0.001 + 25 × $0.004 = **$0.101**.
- Crawl of 50 pages: **$0.201**.

### FAQ

**Can it crawl a whole site?** Yes — use `crawl` mode with a starting URL and a page cap of up to 50. Use `instant` mode to audit a specific list of URLs.

**Can it render JavaScript?** Yes — turn on **Render JavaScript** for single-page apps and JavaScript-heavy sites. It is slower, and it is also what enables the `ttfb_ms` measurement.

**Does it measure Core Web Vitals (LCP, CLS)?** No. Those need full browser rendering, so `largest_contentful_paint_ms` and `cumulative_layout_shift` are always `null`. You do get total load time, DOM-complete time and, with JavaScript rendering, time to first byte.

**What issues does it detect?** Missing or too long/short titles, missing descriptions, missing H1, images without alt text, slow load, redirects, 4xx/5xx errors, broken pages, thin content, render-blocking resources, duplicate meta tags and more — see the `issues` field above.

**Why is `images_without_alt` null on some pages?** The audit reports *whether* any image lacks alt text, not how many. When at least one does, the page gets the `no_image_alt` issue and the count is left `null` rather than guessed.

**Can I use it with AI agents and automation tools?** Yes. The output is clean JSON, ready for n8n, Make, Zapier and MCP-based agents.

***

*Tags: seo audit, on-page seo, site audit, technical seo, meta tags checker, seo analyzer, page speed, seo issues, website audit, seo crawler.*

# Actor input Schema

## `mode` (type: `string`):

instant = audit the exact URLs you provide. crawl = crawl a whole site from one starting domain/URL.

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

Full page URLs to audit (include https://). One per line. Up to 1,000 per run. Used in instant mode.

## `target` (type: `string`):

Starting domain or URL to crawl, e.g. https://example.com. Used in crawl mode.

## `maxCrawlPages` (type: `integer`):

Maximum number of pages to crawl in crawl mode. Capped at 50.

## `enableJavaScript` (type: `boolean`):

Render pages with JavaScript before auditing (slower, but accurate for JS-heavy sites).

## Actor input object example

```json
{
  "mode": "instant",
  "urls": [
    "https://apify.com",
    "https://www.python.org"
  ],
  "target": "https://apify.com",
  "maxCrawlPages": 25,
  "enableJavaScript": false
}
```

# Actor output Schema

## `dataset` (type: `string`):

On-page audit per URL with issues.

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

Pages audited and total issues.

# 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://apify.com",
        "https://www.python.org"
    ],
    "target": "https://apify.com"
};

// Run the Actor and wait for it to finish
const run = await client.actor("santhej/website-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 = {
    "urls": [
        "https://apify.com",
        "https://www.python.org",
    ],
    "target": "https://apify.com",
}

# Run the Actor and wait for it to finish
run = client.actor("santhej/website-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 '{
  "urls": [
    "https://apify.com",
    "https://www.python.org"
  ],
  "target": "https://apify.com"
}' |
apify call santhej/website-seo-audit --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,santhej/website-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/d3e2jctvKMocftD9z/builds/Fhe55PnfUF5y9A8u9/openapi.json
