# Website SEO & Accessibility Auditor (`zsb1978/seo-accessibility-auditor`) Actor

Audit public website pages for SEO metadata, accessibility basics, HTTP errors, and content quality issues.

- **URL**: https://apify.com/zsb1978/seo-accessibility-auditor.md
- **Developed by:** [sky zhong](https://apify.com/zsb1978) (community)
- **Categories:** Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 audited website pages

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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Website SEO & Accessibility Auditor

Audit public website pages for practical SEO and accessibility problems without browser automation, logins, cookies, or external APIs. The Actor follows same-site links, optionally reads XML sitemaps, and writes one transparent result per audited page.

### What you get

Each dataset row includes:

- HTTP status, final URL, content type, response size, and load time
- title, meta description, H1 count, canonical URL, HTML language, robots directives, and indexability
- Open Graph fields and detected JSON-LD types
- missing image `alt` attributes
- buttons and links without detectable accessible names
- form controls without detectable labels
- skipped heading levels
- internal and external link counts
- a 0–100 SEO score, a 0–100 accessibility score, and detailed issue codes

The `OUTPUT` record in the default key-value store contains aggregate status codes, average scores, top issues, failed pages, duplicate titles, and duplicate meta descriptions.

### Common use cases

- audit a new landing page before launch
- monitor a marketing site for missing SEO metadata
- find repeated titles and descriptions after a migration
- create a first-pass accessibility backlog for developers
- schedule a recurring website health check and export results as JSON, CSV, XML, or Excel through Apify
- call the Actor from an API, Make, Zapier, or an AI agent that needs structured web-quality data

### Quick start

Use this input in Apify Console or through the API:

```json
{
  "startUrls": [{ "url": "https://example.com/" }],
  "maxPages": 50,
  "maxCrawlDepth": 1,
  "discoverSitemaps": true,
  "respectRobotsTxt": true,
  "includeSubdomains": false,
  "maxConcurrency": 5,
  "delayMs": 300,
  "requestTimeoutSecs": 30
}
```

For one-page checks, set `maxCrawlDepth` to `0`. For a broader crawl, increase it gradually and keep `maxPages` at a predictable limit.

### Input reference

| Field | Default | Purpose |
| --- | ---: | --- |
| `startUrls` | required | One or more public HTTP(S) start pages |
| `maxPages` | `50` | Hard cap across all sites, from 1 to 500 |
| `maxCrawlDepth` | `1` | Same-site link depth, from 0 to 5 |
| `discoverSitemaps` | `true` | Read `Sitemap:` entries and try `/sitemap.xml` |
| `respectRobotsTxt` | `true` | Check robots rules before fetching pages |
| `includeSubdomains` | `false` | Include child subdomains of each start hostname |
| `includePatterns` | `[]` | Optional shell-style URL allow patterns |
| `excludePatterns` | `[]` | Optional shell-style URL deny patterns |
| `maxConcurrency` | `5` | Concurrent workers across sites |
| `delayMs` | `300` | Minimum request spacing per origin |
| `requestTimeoutSecs` | `30` | Timeout for each request |

### Scoring methodology

The SEO score starts at 100 and applies documented deductions for non-2xx responses, missing or unusually sized titles and descriptions, missing or multiple H1 headings, missing canonical, language and viewport metadata, and `noindex` directives.

The accessibility score starts at 100 and applies bounded deductions for missing page language, image alt attributes, accessible button/link names, form labels, and heading-order skips. Empty `alt=""` is reported separately but is not automatically treated as an error because it can be correct for decorative images.

Scores are prioritization aids, not guarantees. Every deduction is backed by a visible issue code in the same dataset row.

### Pricing

This Actor is designed for **pay per event** billing. Apify's synthetic `apify-default-dataset-item` event is charged once when one page-level result is written to the default dataset. The source includes a suggested launch price of **$0.005 per page**; the live event and price must be confirmed in the Actor's Apify Console monetization settings. Do not add a second custom per-page event, because that would charge the same result twice.

Users control cost with `maxPages` and Apify's maximum charge per run. When the spending limit is reached, the Actor stops gracefully.

### Safety and responsible use

- Only public HTTP and HTTPS targets are supported.
- Localhost, private, loopback, link-local, reserved, multicast, and other non-public addresses are blocked.
- Every redirect target is validated before it is requested.
- Response bodies are capped at 5 MB.
- Crawling stays within the hostname scope of each start URL unless child subdomains are explicitly enabled.
- Binary and common static-asset extensions are skipped.
- `robots.txt` is respected by default, including a bounded crawl delay.
- The Actor does not accept cookies, credentials, custom headers, stealth settings, or login sessions.

Only audit websites you are authorized to access and follow applicable site terms and laws.

### Limitations

This is a static server-rendered HTML audit. It does not execute JavaScript and is not a Lighthouse performance run. It cannot evaluate color contrast, keyboard behavior, focus order, video captions, ARIA widget behavior, or content visible only after client-side rendering.

The accessibility result is not a WCAG certification or legal compliance opinion. Use it as a fast automated first pass and follow with browser-based and manual testing.

### Run locally

Python 3.11 or newer is required.

```bash
python -m venv .venv
.venv/Scripts/pip install -r requirements.txt
python -m unittest discover -s tests -v
apify run
```

For a local Actor run without Apify CLI, place the input at `storage/key_value_stores/default/INPUT.json` and run `python -m my_actor`. To exercise pay-per-event behavior locally, set `ACTOR_TEST_PAY_PER_EVENT=true`.

### Output example

```json
{
  "url": "https://example.com/",
  "statusCode": 200,
  "title": "Example Domain",
  "h1Count": 1,
  "seoScore": 75,
  "accessibilityScore": 100,
  "issueCodes": [
    "TITLE_TOO_SHORT",
    "META_DESCRIPTION_MISSING",
    "CANONICAL_MISSING"
  ]
}
```

### Support

When reporting a problem, include the run ID, a public example URL, and the unexpected issue code. Do not send passwords, cookies, API tokens, or private-site content.

### FAQ

**Why does the result differ from Lighthouse?** This Actor audits returned HTML without launching a browser. Lighthouse executes a browser and measures a different, broader set of signals.

**Can it audit a login-only site?** No. Credentials, cookies, and private networks are intentionally unsupported.

**Why was a page skipped?** Check `errorCode`, URL patterns, hostname scope, robots.txt, the page cap, and the run spending limit.

**Can I resell the report?** You may use and export Actor results, subject to the audited website's terms and applicable law.

# Actor input Schema

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

Public HTTP or HTTPS pages where the audit starts. Private networks, localhost, login sessions, and custom cookies are intentionally unsupported.

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

Hard cap across all start URLs. One saved audit result equals one billable dataset-item event.

## `maxCrawlDepth` (type: `integer`):

0 audits only start URLs; 1 also audits directly linked pages; higher values follow more same-site levels.

## `discoverSitemaps` (type: `boolean`):

Read sitemap locations from robots.txt and try /sitemap.xml, while keeping the same page cap and site scope.

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

Check robots.txt before auditing a page. Keep enabled unless you own the website and have a specific reason.

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

Allow links whose hostname is a child of each start URL hostname.

## `includePatterns` (type: `array`):

Optional shell-style patterns, for example *://example.com/blog/*. Empty means include every in-scope page.

## `excludePatterns` (type: `array`):

Optional shell-style patterns, for example */cart* or \*.pdf.

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

Number of concurrent workers across sites. Requests to the same origin are still paced.

## `delayMs` (type: `integer`):

Minimum spacing between requests to the same origin. A longer robots.txt crawl-delay takes precedence.

## `requestTimeoutSecs` (type: `integer`):

Maximum time allowed for one HTTP request.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://example.com/"
    }
  ],
  "maxPages": 50,
  "maxCrawlDepth": 1,
  "discoverSitemaps": true,
  "respectRobotsTxt": true,
  "includeSubdomains": false,
  "includePatterns": [],
  "excludePatterns": [],
  "maxConcurrency": 5,
  "delayMs": 300,
  "requestTimeoutSecs": 30
}
```

# Actor output Schema

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

One structured SEO and accessibility audit record per processed page.

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

Aggregate scores, issue totals, status codes, failures, and duplicate metadata groups.

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

// Run the Actor and wait for it to finish
const run = await client.actor("zsb1978/seo-accessibility-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://example.com/" }] }

# Run the Actor and wait for it to finish
run = client.actor("zsb1978/seo-accessibility-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://example.com/"
    }
  ]
}' |
apify call zsb1978/seo-accessibility-auditor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,zsb1978/seo-accessibility-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/118dwfLaBpQJEJq4O/builds/uv6dh6JDzzYVfsOyP/openapi.json
