# Ultimate SEO Audit & Website Analyzer (`badr_xba/ultimate-seo-audit`) Actor

Renders every page in Chromium and audits meta tags, headings, content, technical SEO, structured data, lab performance, links, images, accessibility (axe-core), mobile, hreflang and AI-search visibility. Traceable scores, site action plan, run-to-run history, HTML/PDF/CSV reports.

- **URL**: https://apify.com/badr_xba/ultimate-seo-audit.md
- **Developed by:** [Badr Bouargan](https://apify.com/badr_xba) (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

from $10.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

## Ultimate SEO Audit & Website Analyzer

![Ultimate SEO Audit: visual report of a real run, key figures and charts](https://api.apify.com/v2/key-value-stores/R1P4OnWyoV0LnGBin/records/ultimate-seo-audit-overview.png)

Audit one website or compare several. Every page is rendered in a real Chromium browser, checked by twelve modules, scored with traceable points, and summarised in an action plan, an interactive HTML report, a PDF, a CSV of issues and a run-to-run history.

**What you get per run**

| Output | Where | What it contains |
|---|---|---|
| Page records | Dataset (`recordType: "page"`) | Score, band, 9 category scores, every lost point, issues with explanation + fix + example, and the raw data of each module |
| Site summaries | Dataset (`recordType: "site-summary"`) | Site score, action plan, top issues, duplicates, broken links/images, non-indexable pages, coverage, robots/sitemaps, AI-bot permissions, history diff |
| `REPORT` | Key-value store, `text/html` | Self-contained interactive report: overview with gauges, action plan, page explorer (search, filter, sort, pagination, page detail), issues, broken links, image gallery, schema inspector, accessibility, performance, crawl & AI, multi-site comparison, history, JSON/CSV export. While the crawl runs it shows live progress (pages processed / total, refreshed at most once a minute); an **Errors & warnings** panel groups pages and inputs that were not audited by code; a **Data quality & coverage** panel shows audited / failed / skipped pages and how many pages each check category could score |
| `REPORT_PDF` | Key-value store, `application/pdf` | Printable summary per site |
| `ISSUES` | Key-value store, `text/csv` | One row per issue per page (and site-wide issues) with recommendation |
| `RUN_SUMMARY` | Key-value store, `application/json` | Totals, per-site comparison, timing, outputs |
| `FAILED_INPUTS` | Key-value store, `application/json` | Invalid inputs, robots-blocked start URLs, failed and turned-away pages (none of them charged) |

### What is checked

- **Meta**: title and description length in characters and estimated pixels (Arial metrics), duplicates across the site, several titles/descriptions, charset, `lang`, Open Graph, Twitter card (with Open Graph fallback), head elements that ended up in `<body>`.
- **Headings**: outline, missing/empty/multiple H1, skipped levels, H1 vs title overlap.
- **Content**: words (Unicode aware, CJK per character), paragraphs, sentences, average sentence length, reading ease for EN/DE/FR/ES/IT/NL/PT, top keywords and phrases, keyword stuffing, placeholder text, exact duplicates (SHA-1) and near duplicates (SimHash of distinct 5-word shingles).
- **Technical**: status, HTTPS, TLS certificate, http->https redirect, redirect chains and loops, robots.txt (RFC 9309), sitemaps (index, gzip, text), indexability (meta robots, googlebot, `X-Robots-Tag`), canonical (HTML + Link header, multiple, relative, outside head, target broken/redirecting/noindex, chains), soft 404 (error wording, or same content as a random missing URL), JavaScript dependence (server HTML vs rendered DOM), mixed content, compression, URL length, pagination targets, orphan pages (sitemap vs links, only when the crawl covered the whole site).
- **International**: hreflang from the HTML, the HTTP `Link` header and the XML sitemap; invalid codes (validated with the runtime's Unicode data, e.g. `en-UK` -> `en-GB`), relative URLs, missing self-reference, conflicts between sources, x-default, broken targets, missing return links.
- **Structured data**: JSON-LD (including `@graph`), microdata and RDFa; parse errors; required properties for Product, Breadcrumb, Event, Recipe, Job posting, Video, Local business, Software app, FAQ, Q\&A, Review, Course, Dataset.
- **Performance (lab)**: LCP, CLS, TBT, FCP, TTFB, DOMContentLoaded, load, transfer weight and requests by type.
- **Links**: internal/external, nofollow, empty anchors, too many links, insecure internal links, and **every unique link** checked once per run (HEAD, then GET when HEAD fails), redirects.
- **Images**: alt coverage, decorative images, broken images, real weight (browser network data, else Content-Length, else a bounded download), format, dimensions attributes, images much larger than displayed.
- **Accessibility**: axe-core 4.11 with WCAG 2.0/2.1/2.2 A and AA rules plus best practices; "needs review" items listed separately.
- **Mobile**: viewport, zoom blocking, horizontal overflow, tap targets (WCAG 2.5.8 size + spacing test) and small text, measured at a 375 px viewport.
- **AI search visibility**: robots.txt permissions for search bots (OAI-SearchBot, Claude-SearchBot, PerplexityBot...), training bots (GPTBot, ClaudeBot, CCBot, meta-externalagent...), control tokens (Google-Extended, Applebot-Extended) and user-triggered fetchers, per start URL and as a share of the audited URLs; `/llms.txt` presence (informational).

### Honest limits

- **Lab, not field data.** Performance comes from one unthrottled desktop load in headless Chromium close to the crawler. Real-user Core Web Vitals (CrUX) can be very different, especially on phones. The Core Web Vitals thresholds are used only as guidance.
- **Accessibility**: automated rules catch only part of the problems. Results are never a conformance claim.
- **robots.txt permission is not access.** A bot allowed by robots.txt can still be blocked by a firewall or CDN, and user-triggered fetchers may not read robots.txt.
- **Rich results**: having the required properties does not guarantee that a search engine shows a rich result.
- **Pixel widths** of titles/descriptions are estimates.
- **Broken vs unverified**: hosts that refuse automated checks (401/403/429/999, timeouts) are reported as "unverified", never as broken.
- **Turned away**: bot challenges, CAPTCHAs, login walls and 401/403/429 answers are reported as "turned away". They are never bypassed, never scored and never charged.
- **Orphan pages** are only reported when the crawl reached every page (no page/depth limit hit, not start-URLs-only, not seeded from the sitemap). Otherwise the report says why the check was not possible.
- **Mobile checks** re-lay out the page at 375 px without touch emulation.

### Scores (formulas)

- Every issue type has a severity and points (critical 30, high 15, medium 8, low 3, info 0; a few documented exceptions such as accessibility impacts 15/8/4/1, best-practice-only axe rules count half).
- **Category score** (per page) = `max(0, 100 - sum of the points of the issues in that category)`. Categories: technical, on-page, content, performance, accessibility, internal linking & links, images, mobile, schema.
- A category that was not measured (module off, metric unavailable, page without images, axe error) is **left out**, never counted as 0.
- **Page score** = rounded mean of the scored categories **except links** (links mostly reflect other websites). Pages that are not indexable (4xx/5xx, noindex) are capped at **39**.
- **Bands**: good 80+, needs improvement 60-79, poor 40-59, critical below 40.
- **Site category score** = mean of the page category scores minus the points of site-wide issues in that category (floor 0). **Site score** = mean of the site category scores except links.
- Every deduction is stored in `deductions` (page) and `siteDeductions` (site) with the issue id, rule and points.
- **Priority** in the action plan: critical severity -> critical; high -> high; medium -> medium (long-term when the fix effort is high); low -> long-term.

### Input

| Field | Default | Notes |
|---|---|---|
| `startUrls` | demo: books.toscrape.com | One report per website; several websites are compared |
| `crawlPages` | `true` | Off = audit only the start URLs |
| `maxPages` | 5 | Per website; this is what you pay for |
| `maxDepth` | 5 | Clicks from a start URL |
| `seedFromSitemap` | `false` | Queue sitemap URLs too |
| `includeSubdomains` | `false` | |
| `includeUrlPatterns` / `excludeUrlPatterns` | `[]` | Glob (`/blog/**`, `**?sort=*`) or `/regex/` |
| `respectRobotsTxt` | `true` | Crawl-delay honoured (max 10 s) |
| `maxConcurrency` | 3 | Pages rendered in parallel |
| `pageTimeoutSecs` | 45 | |
| `checkExternalLinks` | `true` | |
| `maxLinkChecksPerSite` | 1000 | Unique URLs checked per website (0 = none) |
| `maxImagesPerPage` | 100 | |
| `soft404Probe` | `true` | One request to a random missing URL per website |
| `auditMeta` ... `auditAiVisibility` | `true` | 12 module switches |
| `generateHtmlReport`, `generatePdfReport` | `true` | |
| `pageScreenshots` | `true` | Small above-the-fold JPEG per audited page (desktop, plus phone when the mobile check runs) taken from the render the audit already does; stored as `SHOT-...` records in the key-value store, shown in the page explorer and page detail (max 3 per page). Not charged. |
| `useHistory`, `historyStoreName` | `true`, `seo-audit-history` | Named key-value store with the last 10 runs per website |

Empty input runs a quick 5-page demo audit. Invalid values fall back to defaults and are listed in `FAILED_INPUTS`.

#### Examples

```json
{ "startUrls": [{ "url": "https://www.example.com/" }], "maxPages": 50 }
```

```json
{ "startUrls": [{ "url": "https://shop-a.example/" }, { "url": "https://shop-b.example/" }], "maxPages": 20, "excludeUrlPatterns": ["/cart/**", "**?sort=*"] }
```

Ready-made inputs are in `tasks/` (quick audit, full site, technical, Core Web Vitals lab, accessibility, AI search visibility, multi-site comparison, deep audit).

### History & regressions

With `useHistory` on, each run stores a compact snapshot per website (score, category scores, page scores, issue keys) in the named store. The next run reports the score change, new issues, fixed issues (only for pages audited in both runs), regressed and improved pages (5+ points), and flags a regression. Schedule the Actor to monitor a site.

### Pricing

Pay per event: **page-audited** = one rendered and analysed page with a 2xx/3xx answer. Not charged: robots-blocked, excluded, duplicate, file, failed, error (4xx/5xx) and turned-away URLs, site summaries and reports. Plus a tiny $0.00005 start fee per run (one event per GB of memory). Price per 1,000 audited pages by Apify plan: **Free $20** ($0.02 per page), **Starter $16**, **Scale $13**, **Business and above $10** ($0.01 per page). Free-plan examples: 20 pages = $0.40, 100 pages = $2, 500 pages = $10. Apify platform usage (compute) is included in this price. A run stops cleanly when the maximum cost per run is reached; pages after the limit are neither audited nor charged.

### FAQ

**Why does my score differ from PageSpeed Insights?** PSI uses throttled mobile lab runs and real-user data; this Actor measures one unthrottled desktop load. Use the values to compare pages and runs, not as field data.

**Why is a link "unverified"?** The target refused automated requests (for example 403 or 429). It may work fine in a browser.

**Does it log in or solve CAPTCHAs?** No. Those pages are reported as turned away.

**Why are some categories "n/a"?** They were not measured (module switched off, no images on the page, metric unavailable). They do not lower the score.

**How much memory?** Each parallel page needs roughly 300-500 MB; 4 GB is enough for the default 3 parallel pages.

### What the results look like

![Ultimate SEO Audit: result cards from a real run](https://api.apify.com/v2/key-value-stores/R1P4OnWyoV0LnGBin/records/ultimate-seo-audit-results.png)

*Screenshots from a real platform run.*

### Feedback and reviews

If this Actor saved you time, a short review on its [Store page](https://apify.com/badr_xba/ultimate-seo-audit/reviews) helps other users find it. If something did not work as expected, please open an issue on the [Issues tab](https://apify.com/badr_xba/ultimate-seo-audit/issues) with your run ID: we read every issue.

# Changelog

This Actor's version history is a separate document: https://apify.com/badr_xba/ultimate-seo-audit/changelog.md

# Actor input Schema

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

One or more URLs. Each website (host) gets its own report; several websites are compared side by side. Leave empty for a quick demo audit of books.toscrape.com.

## `crawlPages` (type: `boolean`):

On: follow internal links from the start URLs (up to the page limit). Off: audit only the start URLs.

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

Pages audited (and charged) per website. Robots-blocked, excluded, duplicate, failed, file and turned-away URLs are never charged.

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

How many clicks away from a start URL the crawler may go (0 = start URLs only).

## `seedFromSitemap` (type: `boolean`):

Also queue the URLs listed in the sitemap(s), up to the page limit. Sitemaps are always read for orphan-page and hreflang checks.

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

Treat blog.example.com and shop.example.com as part of example.com.

## `includeUrlPatterns` (type: `array`):

Optional glob (e.g. /blog/\*\*) or /regular expression/. When set, only matching URLs are audited.

## `excludeUrlPatterns` (type: `array`):

Glob (e.g. /cart/\*\*, \*\*?sort=\*) or /regular expression/. Matching URLs are never audited.

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

Recommended. Only switch off for your own site.

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

Pages rendered at the same time. Each needs about 300-500 MB of memory. Requests to each host are also spaced politely.

## `pageTimeoutSecs` (type: `integer`):

How long to wait for one page to load before it is reported as failed (not charged).

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

Check links to other websites too (each unique URL once per run).

## `maxLinkChecksPerSite` (type: `integer`):

Unique URLs (links, images, canonical and hreflang targets) checked per website. 0 = no link checks.

## `maxImagesPerPage` (type: `integer`):

How many images per page are checked (size, format, alt text). 0 = skip image checks.

## `soft404Probe` (type: `boolean`):

Request one random non-existent URL per website to learn what a missing page looks like.

## `auditMeta` (type: `boolean`):

Title, meta description, Open Graph, Twitter cards, canonical, robots meta, charset.

## `auditHeadings` (type: `boolean`):

Heading tree: missing or multiple H1, skipped levels, H1 vs title.

## `auditContent` (type: `boolean`):

Word count, sentences, reading ease, top keywords, duplicate and near-duplicate content.

## `auditTechnical` (type: `boolean`):

Status codes, HTTPS, redirects, indexability, robots.txt, sitemap, soft-404, JavaScript dependence.

## `auditSchema` (type: `boolean`):

JSON-LD, microdata and RDFa: parse errors and required properties for rich results.

## `auditPerformance` (type: `boolean`):

Lab timings from one page load (LCP, CLS, FCP, TTFB, TBT). Approximate, not real-user data.

## `auditLinks` (type: `boolean`):

Internal and external links, nofollow, redirects and broken links (checked once per run).

## `auditImages` (type: `boolean`):

Alt text, dimensions, file size, format and broken images.

## `auditAccessibility` (type: `boolean`):

Automated WCAG 2.x A/AA checks with axe-core. Does not prove conformance.

## `auditMobile` (type: `boolean`):

Viewport, layout at 375 px, horizontal scrolling, small tap targets and text.

## `auditInternational` (type: `boolean`):

hreflang from HTML, HTTP headers and sitemaps: codes, return links, x-default.

## `auditAiVisibility` (type: `boolean`):

Which AI search and training bots robots.txt allows, plus llms.txt (information only).

## `generateHtmlReport` (type: `boolean`):

Save a visual HTML report (REPORT) in the key-value store. While the crawl runs, REPORT is updated at most once a minute and shows the progress.

## `pageScreenshots` (type: `boolean`):

Save a small above-the-fold JPEG of each audited page (desktop, plus phone when the mobile check runs) from the browser render the audit already does, and show it in the report. No extra charge; the images are stored in the run's key-value store.

## `generatePdfReport` (type: `boolean`):

Save a PDF version of the report (REPORT_PDF). Adds a few seconds.

## `useHistory` (type: `boolean`):

Keep a snapshot per website in a named key-value store and report new, fixed and regressed issues on the next run.

## `historyStoreName` (type: `string`):

Named key-value store used for history (letters, digits and -).

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://books.toscrape.com/"
    }
  ],
  "crawlPages": true,
  "maxPages": 5,
  "maxDepth": 5,
  "seedFromSitemap": false,
  "includeSubdomains": false,
  "includeUrlPatterns": [],
  "excludeUrlPatterns": [],
  "respectRobotsTxt": true,
  "maxConcurrency": 3,
  "pageTimeoutSecs": 45,
  "checkExternalLinks": true,
  "maxLinkChecksPerSite": 1000,
  "maxImagesPerPage": 100,
  "soft404Probe": true,
  "auditMeta": true,
  "auditHeadings": true,
  "auditContent": true,
  "auditTechnical": true,
  "auditSchema": true,
  "auditPerformance": true,
  "auditLinks": true,
  "auditImages": true,
  "auditAccessibility": true,
  "auditMobile": true,
  "auditInternational": true,
  "auditAiVisibility": true,
  "generateHtmlReport": true,
  "pageScreenshots": true,
  "generatePdfReport": true,
  "useHistory": true,
  "historyStoreName": "seo-audit-history"
}
```

# Actor output Schema

## `report` (type: `string`):

Interactive report with scores, action plan, page explorer and exports.

## `reportPdf` (type: `string`):

Printable summary.

## `overview` (type: `string`):

Score, band, status and issue counts per page.

## `issues` (type: `string`):

Every issue with recommendation.

## `allFields` (type: `string`):

Page and site-summary records as JSON, CSV or Excel.

## `runSummary` (type: `string`):

Totals, comparison, timing.

## `failedInputs` (type: `string`):

What was not audited and why.

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

// Run the Actor and wait for it to finish
const run = await client.actor("badr_xba/ultimate-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": [{ "url": "https://books.toscrape.com/" }] }

# Run the Actor and wait for it to finish
run = client.actor("badr_xba/ultimate-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": [
    {
      "url": "https://books.toscrape.com/"
    }
  ]
}' |
apify call badr_xba/ultimate-seo-audit --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,badr_xba/ultimate-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/04ijTZXIrAK6fYYTg/builds/h8CUhJvUfcdsSkQn7/openapi.json
