# On Page SEO Audit: Score, Issues and Fixes per URL (`pistachio_implementation/on-page-seo-audit`) Actor

Audit any list of pages or crawl a site and get a 0 to 100 SEO score per page with issues and fix hints: title, meta description, H1, canonical, indexability, hreflang, Open Graph, schema, alt text, thin content, speed, mixed content and optional broken links. $3 per 1,000 pages.

- **URL**: https://apify.com/pistachio\_implementation/on-page-seo-audit.md
- **Developed by:** [Hay Equipos](https://apify.com/pistachio_implementation) (community)
- **Categories:** SEO tools, Marketing, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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

## On Page SEO Audit: Score, Issues and Fixes per URL

Audit a list of pages, or crawl a whole site, and get one row per page with a 0 to 100 SEO score, every issue found (error, warning or notice) and a plain fix hint for each. The actor checks the things that decide whether a page can rank and how it looks in search and social previews: title, meta description, H1 and heading structure, canonical, indexability (noindex, X Robots Tag, robots.txt), hreflang, language, mobile viewport, Open Graph and Twitter cards, structured data (JSON LD and microdata), image alt text, thin content, text to HTML ratio, server response time, page weight, redirects, HTTPS and mixed content, favicon, empty links, and optionally broken links.

It runs on plain HTTP with no browser, so it is fast and costs $3 per 1,000 pages audited, plus a start fee of $0.00005 per run. At the end of each run a site summary (average score, the most common issues, duplicate titles and duplicate meta descriptions) is saved to the run's key value store as `SUMMARY`.

### What you can use it for

- A quick technical SEO health check of your site or a client's site.
- Weekly monitoring: schedule the actor and watch scores and issue counts over time.
- Competitor comparison: audit their top landing pages side by side with yours.
- Pre launch checks on a staging list of URLs.
- Feed issues and fix hints into an AI agent or a ticketing system.

### Input

| Field | What it does | Default |
|---|---|---|
| Page URLs | Pages to audit, or start pages when crawling | required |
| Crawl the site | Follow links to other pages on the same site | off |
| Maximum pages | Total pages to audit | 20 |
| Check for broken links | Test every internal link on each page | off |
| Also check external links | Include links to other sites in the check | off |
| Links to check per page | Upper limit per page | 50 |
| Respect robots.txt | Skip pages robots.txt closes to crawlers (skipped pages are free) | on |
| Parallel requests | Pages worked on at once; one site always gets one request per second | 3 |

Example input:

```json
{
  "urls": ["https://books.toscrape.com/"],
  "crawlSite": true,
  "maxPages": 50,
  "checkLinks": true
}
```

### Output

One row per page. The dataset has a ready made "Audit by page" table view; download as JSON, CSV or Excel, or read it through the API.

```json
{
  "url": "https://quotes.toscrape.com/",
  "finalUrl": "https://quotes.toscrape.com/",
  "success": true,
  "statusCode": 200,
  "responseTimeMs": 412,
  "htmlBytes": 11054,
  "score": 76,
  "errorCount": 0,
  "warningCount": 4,
  "noticeCount": 4,
  "title": "Quotes to Scrape",
  "titleLength": 16,
  "metaDescription": null,
  "metaDescriptionLength": 0,
  "h1": ["Quotes to Scrape"],
  "h1Count": 1,
  "headingCounts": { "h2": 1, "h3": 0, "h4": 0, "h5": 0, "h6": 0 },
  "canonical": null,
  "isIndexable": true,
  "indexabilityReasons": [],
  "language": "en",
  "hasViewport": false,
  "structuredDataTypes": [],
  "wordCount": 279,
  "imageCount": 0,
  "imagesMissingAlt": 0,
  "internalLinkCount": 47,
  "externalLinkCount": 2,
  "issues": [
    { "severity": "warning", "code": "title_too_short", "message": "The title is only 16 characters.", "fix": "Use 30 to 60 characters with the main keyword first." },
    { "severity": "warning", "code": "description_missing", "message": "The page has no meta description.", "fix": "Add a meta description of 70 to 160 characters that summarizes the page." },
    { "severity": "warning", "code": "viewport_missing", "message": "No mobile viewport meta tag.", "fix": "Add <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">." },
    { "severity": "warning", "code": "thin_content", "message": "Only 279 words of visible text.", "fix": "Add useful content; pages under about 300 words rarely rank." }
  ],
  "linksChecked": 15,
  "brokenLinks": [],
  "auditedAt": "2026-09-27T06:14:02.518Z"
}
```

Some fields are trimmed above; every row also carries `metaRobots`, `xRobotsTag`, `charset`, `hreflang`, `openGraph`, `twitterCard`, `textToHtmlRatio`, `imagesEmptyAlt`, `nofollowLinkCount`, `mixedContentCount` and `hasFavicon`.

**Score.** Each page starts at 100 and loses 12 points per error, 5 per warning and 1 per notice, with a floor of 0. Broken links count as one error per page.

### Pricing

Pay per event, no subscription, no charge for platform usage on top.

| Event | Price |
|---|---|
| Page audited | $0.003 ($3 per 1,000 pages) |
| Actor start | $0.00005 per run (Apify's standard start event) |

Only pages that answered with a status below 400 and real content are charged. Unreachable pages, bot checks, non HTML files and robots.txt skips are free. The broken link check is included in the page price. You can set a maximum charge per run in Apify and the actor stops cleanly when it is reached.

### Limits

- Plain HTTP only: it audits the HTML the server sends. Content added later by JavaScript is not seen, and it does not measure Core Web Vitals (use a browser based tool for those).
- Sites behind a bot check return an error row for free rather than a misleading audit.
- One request per second per site, so a 500 page crawl takes about 8 to 9 minutes.
- Crawling stays on the start site (www and non www count as one) and skips images, PDFs, scripts and other files.
- Up to 2,000 pages per run.

### FAQ

**Is it only for my own site?** It audits any public page. Robots.txt is respected by default; turn it off only for sites you own.

**How is this different from Lighthouse?** Lighthouse loads one page in a browser and measures speed. This actor checks on page SEO signals across many pages quickly and cheaply, and flags duplicates across the site.

**Where is the site summary?** In the run's key value store under the key `SUMMARY`: pages audited, average score, issue counts, duplicate titles and duplicate descriptions.

**Does the broken link check cost more?** No. It is part of the page price, but it makes runs slower, so it is off by default.

**Can an AI agent use it?** Yes. Every issue has a stable `code`, a `severity`, a readable `message` and a `fix`, which makes the output easy to act on automatically.

# Actor input Schema

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

Pages to audit. With "Crawl the site" on, these are the starting pages and links on the same site are followed.

## `crawlSite` (type: `boolean`):

Follow links to other pages on the same site (same host, www ignored) until the page limit is reached.

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

Stop after auditing this many pages in total.

## `checkLinks` (type: `boolean`):

Send a light request to every internal link on each page and report the ones that return 4xx, 5xx or fail. Slower.

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

With the broken link check on, also check links to other sites.

## `maxLinksPerPage` (type: `integer`):

Upper limit of links checked on each page.

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

Skip pages that robots.txt closes to crawlers. Turn off only for sites you own. Skipped pages are free.

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

Pages worked on at once. Requests to one site are always spaced one second apart.

## Actor input object example

```json
{
  "urls": [
    "https://books.toscrape.com/",
    "https://quotes.toscrape.com/"
  ],
  "crawlSite": false,
  "maxPages": 20,
  "checkLinks": false,
  "checkExternalLinks": false,
  "maxLinksPerPage": 50,
  "respectRobotsTxt": true,
  "maxConcurrency": 3
}
```

# Actor output Schema

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

All rows the run saved to the default 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://books.toscrape.com/",
        "https://quotes.toscrape.com/"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("pistachio_implementation/on-page-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://books.toscrape.com/",
        "https://quotes.toscrape.com/",
    ] }

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

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,pistachio_implementation/on-page-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/WUDY5CGtKmHMTgZBt/builds/PnfpO875rVRV1aQbG/openapi.json
