# Website SEO Audit: On-Page Checker, Broken Links & Site Crawl (`larkmoor/website-seo-audit`) Actor

On-page SEO audit for any list of URLs or a whole site: score 0-100 per page plus every issue (title, meta description, H1, canonical, noindex, hreflang, structured data, Open Graph, alt text, thin content, speed, broken links). Polite crawl, robots.txt respected. $20 per 1,000 pages.

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

## Pricing

from $17.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.
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 Checker, Broken Links & Site Crawl

Run an on-page SEO audit on any list of pages, or crawl a whole site, and get one row per page with a **score from 0 to 100**, **every issue found with its severity** (error, warning, notice) and the raw facts behind each one: title and its length, meta description and its length, H1 and H2 headings, canonical URL, meta robots and X-Robots-Tag (indexable or not), hreflang alternates, JSON-LD structured data types, Open Graph and Twitter card tags, images without alt text, word count, internal, external and nofollow links, **broken links**, HTTP status, redirects, HTTPS, server response time and HTML size.

No browser and no login: the actor reads each page's HTML directly, follows internal links politely and respects robots.txt.

### What you can use it for

- **Agencies and freelancers**: audit a prospect's site in a minute and send the issue list as a CSV or sheet with your pitch
- **Site owners**: a weekly scheduled run catches the page that lost its title, the template that dropped its H1, or the redirect that started returning 404
- **Content teams**: find thin pages, missing meta descriptions and images without alt text across the whole blog
- **Developers**: check a staging site before launch — noindex left on, canonicals pointing to the wrong host, missing viewport tag
- **Lead generation**: score hundreds of local business homepages and contact the ones that need help

### Why this one

- **$0.02 per page**, half the price of the most used SEO audit actor in the Store, $0.001 per run start
- **Score + issue codes**: each page gets a 0–100 score and stable issue codes (`title_long`, `h1_missing`, `noindex`, `broken_links`, …) that are easy to filter, count and chart
- **Broken link checker built in**: turn on `checkBrokenLinks` and every link on the page is requested; failing ones are listed with their status
- **Site crawl**: give a homepage, turn on `crawl`, set `maxPagesPerSite` — the actor walks internal links on the same host (assets, PDFs and feeds skipped)
- **Polite and honest**: robots.txt respected by default, blocked or bot-check pages are flagged instead of being scored as empty
- **256 MB memory**, about one second per page without link checks

### Input

| Field | What it does |
|---|---|
| `urls` | Pages or bare domains to audit |
| `crawl` | Follow internal links from each start URL |
| `maxPagesPerSite` | Page cap per start URL when crawling |
| `checkBrokenLinks` | Request every link on each page and list broken ones |
| `maxLinkChecksPerPage` | Cap on link checks per page |
| `respectRobotsTxt` | Skip pages disallowed by robots.txt (default on) |
| `maxResults` | Cap on pages for the whole run |

Example:

```json
{
  "urls": ["https://www.example-store.com"],
  "crawl": true,
  "maxPagesPerSite": 200,
  "checkBrokenLinks": true,
  "maxResults": 200
}
```

### Output

```json
{
  "url": "https://www.python.org/",
  "site": "python.org",
  "score": 88,
  "errors": 0,
  "warnings": 1,
  "issueCodes": ["title_short", "meta_description_short", "h1_multiple", "canonical_missing"],
  "issues": [{"severity": "warning", "code": "title_short", "message": "Title is 21 characters; 30-60 is typical."}],
  "statusCode": 200,
  "finalUrl": "https://www.python.org/",
  "redirected": false,
  "https": true,
  "responseTimeMs": 50,
  "pageSizeKb": 51.8,
  "title": "Welcome to Python.org",
  "titleLength": 21,
  "metaDescription": "The official home of the Python Programming Language",
  "metaDescriptionLength": 52,
  "canonical": null,
  "indexable": true,
  "h1": ["Functions Defined", "Compound Data Types"],
  "h1Count": 5,
  "h2Count": 9,
  "lang": "en",
  "viewport": true,
  "hreflang": null,
  "openGraph": {"type": "website", "site_name": "Python.org", "title": "Welcome to Python.org"},
  "structuredDataTypes": ["WebSite"],
  "wordCount": 1039,
  "images": 1,
  "imagesWithoutAlt": 0,
  "internalLinks": 64,
  "externalLinks": 64,
  "linksChecked": 15,
  "brokenLinks": null
}
```

#### The checks

Errors (−12 each): unreachable page, HTTP 4xx/5xx, bot-check or block page, missing title, missing H1, not HTTPS, broken links.
Warnings (−6 each): title shorter than 25 or longer than 60 characters, missing meta description, noindex, missing viewport, images without alt, server response over 2.5 s.
Notices (−2 each): meta description under 70 or over 160 characters, several H1s, missing or foreign canonical, missing lang, incomplete Open Graph, no JSON-LD, thin content (under 250 words), HTML over 1.5 MB, no favicon, invalid JSON-LD.

### How much does it cost?

**$0.02 per page audited** on the Free plan plus **$0.001 per run**, with automatic discounts on paid Apify plans. Platform compute is included; broken-link checks cost nothing extra.

Worked examples:

- **One prospect's homepage** before a sales call: **$0.021**
- **A 200-page site** crawled with broken-link checks, weekly: **$4 a week**
- **500 local business homepages** scored for outreach: **$10**

### FAQ

**Is this legal?**
Yes. It reads public web pages the way a search engine does, identifies itself, respects robots.txt by default and keeps a gentle pace. Audit sites you own, sites of clients who asked, or public pages for research. No login-walled content and no personal data are collected.

**Does it render JavaScript?**
No. It audits the HTML the server sends, which is what most search engine checks start from. Single-page apps that build all their content in the browser will show low word counts; that is a real SEO finding.

**How is the score calculated?**
100 minus 12 per error, 6 per warning and 2 per notice, never below 0. The issue list is the real output; the score makes pages easy to sort.

**Can I check only the links?**
Our Bulk URL Status Checker checks plain URL lists for status codes and redirects at a lower price.

**Why was a page skipped?**
robots.txt disallowed it (turn `respectRobotsTxt` off only for your own site), it was already audited, or it is an asset (image, PDF, script).

### Integrations

Works with Google Sheets, Slack, Zapier, Make, Airtable, webhooks, the Apify API and the Python and JavaScript clients. Download as JSON, CSV, Excel, XML or HTML. Schedule it weekly and send the rows with errors to Slack.

### Changelog

- **2026-10-22** — First release: 0–100 score and 27 issue codes per page, broken-link checker, same-site crawl with robots.txt, structured data, Open Graph, hreflang and speed facts.

# Actor input Schema

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

Pages to audit (https://site.com/page) or bare domains (site.com). With crawl on, each one is a start page and the actor follows internal links on the same site.

## `crawl` (type: `boolean`):

Follow internal links from each start URL and audit those pages too (same host, robots.txt respected).

## `maxPagesPerSite` (type: `integer`):

With crawl on: stop after this many pages per start URL.

## `checkBrokenLinks` (type: `boolean`):

Request every link on each page (HEAD, then GET when needed) and list the ones that answer 4xx/5xx or fail. Slower.

## `maxLinkChecksPerPage` (type: `integer`):

With broken-link checking on: check at most this many links per page (internal first).

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

Skip pages the site's robots.txt disallows. Leave on unless you own the site.

## `maxResults` (type: `integer`):

Hard cap on pages audited in the whole run (you pay per page).

## Actor input object example

```json
{
  "urls": [
    "https://www.python.org"
  ],
  "crawl": true,
  "maxPagesPerSite": 10,
  "checkBrokenLinks": false,
  "maxLinkChecksPerPage": 50,
  "respectRobotsTxt": true,
  "maxResults": 10
}
```

# Actor output Schema

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

All results as JSON items in the run's default dataset (also downloadable as CSV/Excel from the dataset).

# 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"
    ],
    "crawl": true,
    "maxPagesPerSite": 10,
    "maxResults": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("larkmoor/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://www.python.org"],
    "crawl": True,
    "maxPagesPerSite": 10,
    "maxResults": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("larkmoor/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://www.python.org"
  ],
  "crawl": true,
  "maxPagesPerSite": 10,
  "maxResults": 10
}' |
apify call larkmoor/website-seo-audit --silent --output-dataset

```

## MCP server setup

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