# SEO Site Auditor: Score, Broken Links & Prioritised Fixes (`luke-main/seo-site-auditor`) Actor

Fast, low-cost technical SEO audit of your website: 0-100 site score, top 10 prioritised fixes, broken links, titles, meta descriptions, H1s, canonicals, noindex, alt text, JSON-LD and Open Graph. Clean JSON for AI agents.

- **URL**: https://apify.com/luke-main/seo-site-auditor.md
- **Developed by:** [Luke Main](https://apify.com/luke-main) (community)
- **Categories:** SEO tools, Developer tools
- **Features:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$7.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 Site Auditor: Score, Broken Links & Prioritised Fixes

Run a **technical SEO audit of your website** in minutes and get back exactly what to fix first: a **0-100 site score**, a **top 10 prioritised fix list** (with how-to-fix advice and example URLs), and a clean per-page JSON report covering status codes, titles, meta descriptions, H1s, canonicals, noindex, **broken internal and external links**, missing image alt text, page size, response time, **JSON-LD structured data** and **Open Graph** tags.

**US$7 per 1,000 pages audited.** The site summary, top fixes and link checks are free. No proxy, no headless browser, no monthly subscription.

> Built with AI and maintained by Luke Main (Apify: luke-main). Please audit sites you own or manage. The crawler identifies itself (`SEOSiteAuditor` user agent), respects robots.txt by default and stays on your site.

### Why this auditor

- **Fix list, not a data dump.** The first dataset row is a site summary with a score, a grade and the 10 fixes that move the score most, ranked by severity and how many pages they affect. Each fix has a plain-English `howToFix` and example URLs.
- **Built for AI agents and automations.** Stable field names, one row type per record (`summary` or `page`), machine-readable issue codes (`missing_h1`, `broken_internal_links`, ...) and a ready-made Markdown `REPORT` an LLM can read in one go. Works well as a tool for Claude, ChatGPT, LangChain, n8n, Make and Zapier.
- **Fast and cheap.** Lightweight HTTP crawling (Crawlee CheerioCrawler). A 50-page audit typically finishes in under a minute and costs about US$0.08.
- **Honest scoring.** The scoring formula is published below, so you can explain a score to a client.

### What it checks

| Area | Per-page fields | Issue codes |
|---|---|---|
| HTTP | `statusCode`, `redirected`, `finalUrl`, `responseTimeMs`, `pageSizeKb` | `http_error`, `fetch_failed`, `redirected`, `slow_response`, `large_page` |
| Title & meta | `title`, `titleLength`, `metaDescription`, `metaDescriptionLength` | `missing_title`, `title_too_long` (>60), `title_too_short` (<30), `missing_meta_description`, `meta_description_too_long` (>160), `meta_description_too_short` (<70), `duplicate_title`, `duplicate_meta_description` |
| Headings & content | `h1Count`, `h1`, `wordCount`, `lang`, `hasViewport` | `missing_h1`, `multiple_h1`, `thin_content`, `missing_lang`, `missing_viewport` |
| Indexing | `canonical`, `canonicalIsSelf`, `noindex`, `nofollow`, `hreflang` | `noindex`, `missing_canonical`, `canonical_mismatch` |
| Links | `internalLinks`, `externalLinks`, `brokenInternalLinks[]`, `brokenExternalLinks[]` | `broken_internal_links`, `broken_external_links` |
| Images | `imagesTotal`, `imagesMissingAlt`, `imagesMissingAltSample` | `images_missing_alt` |
| Structured data & social | `jsonLdTypes`, `openGraph`, `twitterCard` | `no_structured_data`, `invalid_json_ld`, `missing_open_graph` |
| Site | `robotsAndSitemaps` (summary row) | `no_robots_txt`, `no_sitemap` |

A link counts as broken on 404, 410, any 5xx or a connection failure. 401/403/429 responses (logins and bot protection on sites like LinkedIn) are not reported as broken.

### Input

| Field | Default | What it does |
|---|---|---|
| `startUrls` | (required) | Your home page or a section, e.g. `https://yoursite.com`. |
| `maxPages` | 50 | Hard cap on pages audited. Also caps your cost. |
| `crawlScope` | `same-hostname` | `same-hostname` stays on your host (www and non-www count as one); `same-domain` adds subdomains. |
| `respectRobotsTxt` | true | Skips URLs disallowed for `*`. |
| `checkExternalLinks` | true | Checks outbound links too. |
| `maxLinkChecks` | 500 | Max link URLs checked (most-linked first). Free. |
| `maxConcurrency` | 5 | Parallel requests to your server. |
| `slowResponseMs` | 2000 | Threshold for `slow_response`. |
| `includePageIssuesOnly` | false | Output only pages with critical/high issues (all pages are still audited and charged). |

```json
{
  "startUrls": [{ "url": "https://yoursite.com" }],
  "maxPages": 200,
  "checkExternalLinks": true
}
```

### Output

**Row 1: site summary** (also saved as `SUMMARY` in the key-value store):

```json
{
  "type": "summary",
  "siteScore": 81, "grade": "B", "pagesAudited": 50,
  "issuesBySeverity": { "critical": 2, "high": 9, "medium": 31, "low": 64 },
  "topFixes": [
    { "rank": 1, "code": "broken_internal_links", "severity": "critical",
      "issue": "Page links to broken internal URLs",
      "howToFix": "Update or remove links to internal URLs that return 4xx/5xx, or redirect those URLs.",
      "affectedPages": 6, "affectedPct": 12, "exampleUrls": ["https://yoursite.com/blog/old-post"] }
  ],
  "brokenLinks": 4, "duplicateTitles": 3, "avgResponseTimeMs": 412,
  "robotsAndSitemaps": { "yoursite.com": { "robotsTxt": true, "sitemapFound": true, "sitemaps": ["https://yoursite.com/sitemap.xml"] } }
}
```

**Rows 2+: one row per page** (`type: "page"`) with every field in the table above, an `issues` array (`code`, `severity`, `message`) and a `pageScore`.

**`REPORT`** (key-value store): a short Markdown report with the score and top 10 fixes, ready to paste into an email, a ticket or an LLM prompt.

Dataset views: *Pages*, *Issues per page*, *Site summary & top fixes*. Export as JSON, CSV, Excel or HTML.

### Scoring

- Page score = 100 minus 25 per critical, 10 per high, 5 per medium and 2 per low issue (minimum 0).
- Site score = average page score minus a small penalty for a missing sitemap (3) or robots.txt (1).
- Grades: A 90+, B 80+, C 70+, D 55+, F below 55.
- Top fixes are ranked by severity weight x (0.3 + 0.7 x share of pages affected), so a medium issue on every page can outrank a high issue on one page.

### Pricing

Pay per event: **US$0.007 per page audited (US$7 per 1,000 pages)**. The summary row, top fixes, Markdown report, robots/sitemap checks and link checks are free. Set a max charge on the run and the crawl stops at what it allows. Apify platform usage is included in the price.

### Integrations: put audits on autopilot

- **Weekly SEO health check.** Create an Apify Schedule (e.g. Monday 7 am) and add the email or Slack integration on the Integrations tab to get the score and top fixes every week.
- **Google Sheets.** Add the Google Sheets integration to log each run's pages, or just the summary row, and chart your score over time.
- **Zapier, Make and n8n.** Use the Apify app with the trigger "Actor run finished", then "Get dataset items" (or fetch the `SUMMARY` record) to open tickets in Jira, Linear, Trello or Notion for each top fix.
- **Webhooks.** Add a webhook on `ACTOR.RUN.SUCCEEDED` to call your CI or deploy pipeline and fail a release when the score drops or broken links appear.
- **AI agents and MCP.** Call the Actor from Apify's MCP server or the API from Claude, ChatGPT, Cursor or your own agent, and give the model the `REPORT` record. Issue codes and `howToFix` text make it easy for an agent to draft fixes or explain them to a client.
- **API.** `POST https://api.apify.com/v2/acts/luke-main~seo-site-auditor/run-sync-get-dataset-items?token=YOUR_TOKEN` with the input JSON returns the summary and pages in one call for small sites.

### Use cases

- Agencies and freelancers: a quick pre-sales or monthly technical SEO audit with a client-ready fix list.
- Developers: catch broken links, missing titles and noindex mistakes before or after a deploy.
- Site owners: a free-of-subscription SEO checker you run when you need it.
- AI agents: a reliable "audit this site" tool that returns structured JSON.

### Limits and notes

- HTML is fetched over plain HTTP, without running JavaScript. Sites that render all content client-side (some single-page apps) may show missing titles or links even though browsers see them. Server-rendered and static sites, WordPress, Shopify, Webflow, Next.js/Nuxt with SSR and Docusaurus are fine.
- No Core Web Vitals or Lighthouse scores: `responseTimeMs` is the time to download the full HTML from Apify's servers (US), not a field-data metric.
- No backlink, keyword ranking or traffic data.
- Only `text/html` pages are audited; images, PDFs and other files are skipped and not charged.
- If a site blocks datacenter IPs, pages may show `fetch_failed` or HTTP 403.

### FAQ

**Is it free to try?** Apify's free plan credit covers thousands of audited pages.

**Will it hammer my server?** No. Default concurrency is 5 with a 240 requests/minute ceiling, and you can lower it.

**Can I audit a competitor?** The tool reads only public pages and respects robots.txt, but it is designed for your own sites. Please follow each site's terms.

**Who made this?** Luke Main, built with AI assistance. Feedback and feature requests are welcome in the Issues tab.

# Actor input Schema

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

Home page or section of the website you want to audit, e.g. https://yoursite.com. Please audit sites you own or manage. The crawler follows links on the same site from here.

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

Hard cap on pages crawled and audited. This also caps your cost (you pay per audited page).

## `crawlScope` (type: `string`):

same-hostname: only the exact host of the start URL (www and non-www count as one). same-domain: also subdomains such as blog.yoursite.com.

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

Skip URLs disallowed for all user agents (\*) in robots.txt. Recommended.

## `checkExternalLinks` (type: `boolean`):

Also check outbound links for 404/410/5xx/connection errors (HEAD, then GET if HEAD is refused).

## `maxLinkChecks` (type: `integer`):

Max internal-but-not-crawled plus external URLs to check. Links found on the most pages are checked first. Link checks are not charged.

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

Parallel page requests to your site. Keep it low for small servers.

## `slowResponseMs` (type: `integer`):

Pages taking longer than this to return full HTML get a 'slow_response' issue.

## `includePageIssuesOnly` (type: `boolean`):

Keeps the dataset short. The summary row and top fixes still cover every audited page, and every audited page is still charged.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://crawlee.dev"
    }
  ],
  "maxPages": 50,
  "crawlScope": "same-hostname",
  "respectRobotsTxt": true,
  "checkExternalLinks": true,
  "maxLinkChecks": 500,
  "maxConcurrency": 5,
  "slowResponseMs": 2000,
  "includePageIssuesOnly": false
}
```

# Actor output Schema

## `pages` (type: `string`):

No description

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

No description

## `report` (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 = {
    "startUrls": [
        {
            "url": "https://crawlee.dev"
        }
    ],
    "maxPages": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("luke-main/seo-site-auditor").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": [{ "url": "https://crawlee.dev" }],
    "maxPages": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("luke-main/seo-site-auditor").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": [
    {
      "url": "https://crawlee.dev"
    }
  ],
  "maxPages": 50
}' |
apify call luke-main/seo-site-auditor --silent --output-dataset

```

## MCP server setup

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

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/qL0jSh2Rpbow5o0Tg/builds/1mjjwunA2mN37QXbo/openapi.json
