# SEO Audit — bulk on-page checks with a scored report (`titian_troupe/seo-audit`) Actor

Audit any list of URLs for on-page SEO: title and meta description length, H1 structure, canonical, viewport, lang, Open Graph, noindex, image alt text, internal/external links, word count and response time. Returns a 0-100 score with the exact issues behind it. No API keys required.

- **URL**: https://apify.com/titian\_troupe/seo-audit.md
- **Developed by:** [Rumblingb](https://apify.com/titian_troupe) (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

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-usage

## 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

## SEO Audit — bulk on-page checks with a scored report

Audit any list of URLs for on-page SEO and get back a **0–100 score plus the exact issues
behind it**. Not a black box: every deduction is named, so you can hand the output straight
to a client or a developer.

No API keys. No external services. No per-result vendor cost.

### What it checks

| Area | Checks |
|---|---|
| **Title** | Present, length against the 30–60 character target |
| **Meta description** | Present, length against the 70–160 character target |
| **Headings** | H1 present and unique, H2/H3 counts |
| **Indexability** | `noindex` in robots meta, canonical link present |
| **Mobile** | Viewport meta tag |
| **Accessibility** | Images missing `alt` text |
| **Social** | Open Graph title and description |
| **Content** | Word count against a 300-word thin-content threshold |
| **Technical** | HTTP status, HTTPS, redirects, response time, `lang` attribute |
| **Links** | Total, internal and external counts |

### Input

```json
{
  "urls": ["https://example.com", "example.com/pricing"],
  "maxConcurrency": 5,
  "timeoutMs": 15000
}
```

A bare domain is accepted — `https://` is assumed. Wire `urls` to the output of a crawler
Actor to audit an entire site.

| Field | Default | Notes |
|---|---|---|
| `urls` | — | Required. List of pages to audit. |
| `maxConcurrency` | 5 | Lower this when auditing many pages on one host. |
| `timeoutMs` | 15000 | Slow pages are recorded as failed rather than stalling the run. |
| `userAgent` | *(see input)* | Some sites serve different markup to unknown agents. |

### Output

One dataset item per URL:

```json
{
  "url": "https://example.com/",
  "ok": true,
  "score": 66,
  "issueCount": 5,
  "issues": [
    "Title is short (14 chars, aim 30-60)",
    "Missing meta description",
    "No canonical link",
    "Thin content (21 words, aim 300+)",
    "No Open Graph tags (poor social previews)"
  ],
  "statusCode": 200,
  "https": true,
  "title": "Example Domain",
  "titleLength": 14,
  "metaDescriptionLength": 0,
  "h1Count": 1,
  "h2Count": 0,
  "canonical": null,
  "noindex": false,
  "images": 0,
  "imagesMissingAlt": 0,
  "internalLinks": 0,
  "externalLinks": 1,
  "wordCount": 21,
  "responseTimeMs": 214
}
```

A `SUMMARY` record is written to the key-value store with run-level totals: average score,
how many pages are missing a title, meta description or H1, how many are `noindex`, and how
many are thin.

### How the score works

Every page starts at 100 and loses points per issue. The heaviest deductions are the ones
that actually stop a page ranking:

| Deduction | Issue |
|---:|---|
| −30 | HTTP 4xx/5xx |
| −20 | Marked `noindex` |
| −15 | Missing title, or not served over HTTPS |
| −12 | Missing meta description |
| −10 | No H1 |
| −8 | No viewport, or thin content |
| −5 | Multiple H1s, no canonical, title/meta length off target |

The score is clamped to 0–100. Because deductions are listed in `issues`, you can always
reconstruct exactly how a page got its number.

### Typical uses

- **Agency site audits** — run a client's sitemap, sort by score ascending, fix the worst first
- **Pre-launch QA** — catch missing meta and `noindex` before a release goes out
- **Competitor benchmarking** — audit competitor pages alongside your own
- **Regression monitoring** — schedule weekly and alert when a score drops
- **Lead generation** — audit prospect sites and lead with the specific problems you found

### Notes and limits

Pages are fetched as static HTML. Content injected by client-side JavaScript after load is
**not** evaluated — for heavily client-rendered sites, put a rendering Actor upstream and
feed this one the rendered HTML's URL.

`robots.txt` is not consulted; this reads the pages you supply.

# Actor input Schema

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

Pages to audit. A bare domain is accepted and https:// is assumed. Wire this to the output of a crawler Actor to audit a whole site.

## `timeoutMs` (type: `integer`):

Per-URL fetch timeout. Slow pages beyond this are recorded as failed rather than stalling the run.

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

How many pages to fetch in parallel. Lower this when auditing many pages on one host so you do not trip rate limits.

## `userAgent` (type: `string`):

Sent with every request. Some sites serve different markup to unknown agents.

## Actor input object example

```json
{
  "urls": [
    "https://apify.com",
    "example.com",
    "https://github.com"
  ],
  "timeoutMs": 15000,
  "maxConcurrency": 5,
  "userAgent": "Mozilla/5.0 (compatible; SeoAuditActor/0.1; +https://apify.com)"
}
```

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

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

# Run the Actor and wait for it to finish
run = client.actor("titian_troupe/seo-audit").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).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",
    "example.com",
    "https://github.com"
  ]
}' |
apify call titian_troupe/seo-audit --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=titian_troupe/seo-audit",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/acts/oXzkVLwtmK6t2ioAT/builds/JeiNCKzVdfrvOj7EF/openapi.json
