# SEO Page Audit: Titles, Meta, Headings, Links | $1/1K (`one_dollar_store/seo-page-audit-scraper`) Actor

$1 per 1,000 results, platform usage included. On-page SEO audit for any URL: status and redirects, title, meta description, headings, canonical, links, images, social tags and structured data, with a list of issues and a score. Plain HTTP, no login.

- **URL**: https://apify.com/one_dollar_store/seo-page-audit-scraper.md
- **Developed by:** [One Dollar Store](https://apify.com/one_dollar_store) (community)
- **Categories:** SEO tools, Marketing
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 results

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 Page Audit: titles, meta tags, headings, links and issues for any URL

**$1 per 1,000 pages. Platform usage included. No rental, no minimum.**

Give it page addresses and get one row per page with the on-page SEO facts and a list of issues: HTTP status and redirects, title, meta description, canonical, robots tags, headings, word count, links, images without an alt attribute, Open Graph and Twitter tags, structured data types, hreflang and a score. It reads the page as plain HTML over HTTP, with no browser.

[Open it in Apify Console](https://console.apify.com/actors/zQLgLtMPT5e76qfWm/input)

### What it costs

| Pages | Price |
|---|---|
| 1,000 | $1.00 |
| 10,000 | $10.00 |
| 100,000 | $100.00 |

Plus $0.001 per run. One flat price, platform usage included, so there is no separate compute or proxy bill. A weekly audit of 500 pages costs about $0.50, or about $2 a month.

### Why use it

- **One flat price.** The bill follows the number of pages and nothing else. There is no browser or proxy behind it.
- **Facts and verdicts together.** Every row has the raw values (title text and length, heading list, link counts) and an `issues` list with a code, a severity and a plain message, so you can filter on `title_missing` or `images_missing_alt` in a spreadsheet.
- **Status codes are results too.** A page that answers 404 or 500 still gives a row with its status, so you can run a list of URLs through it to find broken pages. The redirect chain is included.
- **Built for lists.** Run hundreds of URLs in one go, schedule it, and compare `score` and `issueCount` between runs.

### Quick start

1. Open the actor, add one page address per line, press **Start**.
2. Or paste this input:

```json
{ "urls": ["https://www.python.org/", "https://docs.apify.com/platform/actors"], "maxItems": 10 }
```

3. Download the results as JSON, CSV or Excel, or read them through the API.

### Use it from code

```bash
curl -X POST "https://api.apify.com/v2/acts/one_dollar_store~seo-page-audit-scraper/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"urls": ["https://www.python.org/"], "maxItems": 10}'
```

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("one_dollar_store/seo-page-audit-scraper").call(
    run_input={"urls": ["https://www.python.org/"], "maxItems": 10}
)
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["finalUrl"], item["score"], [i["code"] for i in item["issues"]])
```

### Use it from an AI assistant

Claude, ChatGPT, Cursor and other assistants can call this actor through Apify's MCP server. Add it with this server address:

```
https://mcp.apify.com?tools=one_dollar_store/seo-page-audit-scraper
```

Then ask, for example: "Audit these 30 product pages and list the ones with a missing meta description or more than one H1."

### Switching from another SEO audit actor

Most SEO audit actors take a start URL or a list of URLs. Here it is `urls` (a list, one row per address) and `maxItems`. The core fields are `finalUrl`, `statusCode`, `title`, `metaDescription`, `h1`, `canonicalUrl`, `wordCount`, `issues` and `score`. The actor audits the pages you list. It does not crawl a whole site.

### Input

| Field | Type | Default | What it does |
|---|---|---|---|
| `urls` | list of text | none | Page addresses to audit, one per line. A bare domain or a full address. Each address gives one row. |
| `includeLinks` | true or false | false | Add a `links` list to each row (up to 500 links with address, text, internal or external, nofollow). The link counts are always included. |
| `maxItems` | number | 100 | The run stops after this many results. Minimum 10. |

A bare domain is tried over https first, then http. The same address listed twice is audited once.

### Output fields

| Field | Meaning |
|---|---|
| `url`, `finalUrl`, `statusCode` | The address, the page after redirects, and the HTTP status. |
| `redirectCount`, `redirectChain` | Redirect hops with status and address, up to 5. |
| `responseTimeMs`, `htmlBytes`, `contentType`, `server` | Time the server took to answer (measured from Apify's servers), size read, content type and `Server` header. |
| `indexable` | `false` for error pages and pages marked `noindex` in the robots meta tag or `X-Robots-Tag` header. |
| `title`, `titleLength`, `metaDescription`, `metaDescriptionLength` | Title and meta description with their lengths. |
| `canonicalUrl`, `robotsMeta`, `xRobotsTag`, `lang`, `charset`, `viewport` | Canonical link, robots directives, language, character set, viewport tag. |
| `h1`, `headingCounts`, `headings` | H1 texts, count per heading level, and the outline (first 60). |
| `wordCount` | Words of visible text in the HTML. |
| `imageCount`, `imagesMissingAlt`, `imagesEmptyAlt` | Images, those with no alt attribute, and those with an empty one (the standard mark for a decorative image, counted but not flagged). |
| `internalLinks`, `externalLinks`, `nofollowLinks`, `linksWithoutText`, `mixedContentCount` | Link counts, and http resources on an https page. |
| `openGraph`, `twitterCard`, `jsonLdTypes`, `hreflang`, `favicon` | Social tags, structured data types, language alternates, favicon link. |
| `links` | Only with `includeLinks`. |
| `score` | 100 minus 15 per error, 5 per warning, 1 per notice, never below 0. |
| `issueCount`, `issues` | Failed checks: `code`, `severity` (`error`, `warning`, `notice`) and `message`. |
| `scrapedAt` | Time of the audit in UTC. |

### Checks

| Code | Severity | Raised when |
|---|---|---|
| `http_error` | error | The status is 400 or higher. |
| `certificate_invalid` | error | The HTTPS certificate failed the check (the page is still read). |
| `not_html` | error | The address returns a file, not an HTML page. |
| `title_missing`, `h1_missing` | error | No title tag, or no H1. |
| `title_short`, `title_long` | warning | Title under 30 or over 60 characters. |
| `meta_description_missing`, `meta_description_long` | warning | No meta description, or over 160 characters. |
| `h1_multiple`, `h1_empty` | warning | More than one H1, or an empty one. |
| `canonical_missing` | warning | No canonical link. |
| `noindex` | warning | Robots meta tag or `X-Robots-Tag` says `noindex`. |
| `viewport_missing` | warning | No viewport meta tag. |
| `not_https` | warning | The final page is served over http. |
| `mixed_content` | warning | Resources load over http on an https page. |
| `images_missing_alt` | warning | Images with no alt attribute. |
| `thin_content` | warning | Under 300 words of text in the HTML. |
| `redirect_chain_long`, `slow_response` | warning | Three or more redirects, or an answer slower than 3 seconds. |
| `meta_description_short`, `heading_level_skipped`, `canonical_other_page`, `lang_missing`, `open_graph_missing`, `twitter_card_missing`, `structured_data_missing`, `favicon_missing`, `redirected`, `html_large` | notice | Smaller points: a description under 70 characters, a skipped heading level, a canonical pointing to another page, no `lang`, no Open Graph or Twitter tags, no JSON-LD, no favicon link, one or two redirects, or HTML over 1 MB. |

The length limits are common rules of thumb, not rules set by any search engine.

### Sample results

Real rows from a test run on 9 October 2026. A few fields (`url`, `headings`, `headingCounts`, `server`, `contentType`, `charset`, `viewport`, `favicon`, `hreflang`, `twitterCard`, `scrapedAt`) are left out here for space.

```json
[
  {"finalUrl": "https://www.python.org/", "statusCode": 200, "responseTimeMs": 106, "htmlBytes": 52308, "indexable": true, "title": "Welcome to Python.org", "titleLength": 21, "metaDescription": "The official home of the Python Programming Language", "metaDescriptionLength": 52, "lang": "en", "h1": ["Functions Defined", "All the Flow You’d Expect", "Compound Data Types", "Intuitive Interpretation", "Quick & Easy to Learn"], "wordCount": 971, "imageCount": 1, "imagesMissingAlt": 0, "imagesEmptyAlt": 0, "internalLinks": 126, "externalLinks": 78, "nofollowLinks": 0, "linksWithoutText": 0, "openGraph": {"title": "Welcome to Python.org", "description": "The official home of the Python Programming Language", "image": "https://www.python.org/static/opengraph-icon-200x200.png", "type": "website", "url": "https://www.python.org/", "site_name": "Python.org"}, "jsonLdTypes": ["WebSite"], "score": 83, "issueCount": 5, "issues": [{"code": "title_short", "severity": "warning", "message": "Title is 21 characters; 30 to 60 is a common target."}, {"code": "meta_description_short", "severity": "notice", "message": "Meta description is 52 characters; 70 to 160 is a common target."}, {"code": "h1_multiple", "severity": "warning", "message": "5 H1 headings."}, {"code": "canonical_missing", "severity": "warning", "message": "No canonical link."}, {"code": "twitter_card_missing", "severity": "notice", "message": "No Twitter card tag."}]},
  {"finalUrl": "https://www.gnu.org/philosophy/free-sw.html", "statusCode": 200, "responseTimeMs": 703, "htmlBytes": 50511, "indexable": true, "title": "What is Free Software? - GNU Project - Free Software Foundation", "titleLength": 63, "metaDescription": "Since 1983, developing the free Unix style operating system GNU, so that computer users can have the freedom to share and improve the software they use.", "metaDescriptionLength": 152, "lang": "en", "h1": [], "wordCount": 4167, "imageCount": 5, "imagesMissingAlt": 0, "imagesEmptyAlt": 0, "internalLinks": 103, "externalLinks": 14, "nofollowLinks": 1, "linksWithoutText": 0, "jsonLdTypes": [], "score": 72, "issueCount": 6, "issues": [{"code": "title_long", "severity": "warning", "message": "Title is 63 characters; search results often cut titles near 60."}, {"code": "h1_missing", "severity": "error", "message": "No H1 heading."}, {"code": "canonical_missing", "severity": "warning", "message": "No canonical link."}, {"code": "open_graph_missing", "severity": "notice", "message": "No Open Graph title or image for social sharing."}, {"code": "twitter_card_missing", "severity": "notice", "message": "No Twitter card tag."}, {"code": "structured_data_missing", "severity": "notice", "message": "No JSON-LD structured data."}]}
]
```

### Limits

- **It audits the pages you list.** It does not crawl a site, check every link on a page for broken targets, or measure page speed in a browser. `responseTimeMs` is only how long the server took to deliver the HTML, measured from Apify's servers.
- **No JavaScript is run.** The audit sees the HTML the server sends. A title, heading or link that scripts add later is not seen, so a single page app can look emptier than it does in a browser.
- **robots.txt is honoured.** A page it does not allow gives no row and is listed in the run summary. If robots.txt cannot be read because of a server error, the page is skipped too.
- **Sites that block cloud servers** or show a bot check page are skipped. The actor does not try to get past a check.
- **A row for any HTTP answer.** Pages that answer 404, 500 and similar give a row with that status. Addresses that cannot be reached at all (no such host, timeout) give no row and are listed in the run summary.
- **Only the first 2 MB** of a page is read. Redirects are followed, up to 5.
- **The rules are rules of thumb.** Title and description lengths, the word count for thin content and the score are common guidelines, not what any search engine requires. Use `issues` as a checklist, not a verdict.
- **Free plan runs** return the first 10 results.
- Every run writes a `RUN_SUMMARY` record with counts and the list of skipped addresses and why.

### FAQ

**How much does it cost to audit 10,000 pages?**
$10, plus $0.001 for each run.

**Do I pay for compute or proxy on top?**
No. Platform usage is included in the $1 per 1,000 price. The actor uses no proxy.

**Can I find broken pages with it?**
Yes. Run a list of URLs and filter on `statusCode` of 400 or more, or on the `http_error` issue. It checks the pages you list, not the links inside them.

**Does it crawl my whole site?**
No. Give it the URLs, for example from your sitemap. That keeps the price per page fixed and the run predictable.

**How is the score worked out?**
It starts at 100 and loses 15 points per error, 5 per warning and 1 per notice. The full list of checks is in the Checks table above.

**Is it legal?**
The actor reads public pages that a site sends to any visitor, with no login, and respects robots.txt. Audit your own sites and other public pages in line with each site's terms and the law where you live.

**Is this an official product of any search engine or SEO tool?**
No. It is an independent tool and is not affiliated with or endorsed by any search engine or SEO company.

**Do I need an Apify account?**
Yes. Actors run from an Apify account, and a free account is enough to start: it includes $5 of platform usage every month. You can [sign up here](https://www.apify.com?fpr=ymnoit). This is a referral link and it costs you nothing.

**Is there a limit on the free plan?**
Yes. A run started from a free plan account delivers the first 10 results, then ends with a message and a link to the plans. Any paid plan returns everything you ask for, up to your own limit. The price per result is the same on every plan.

### More from The One Dollar Store

Every actor here costs $1 per 1,000 results, platform usage included. See the whole store: https://apify.com/one_dollar_store

**Website tools**

- [Website Technology Detector](https://apify.com/one_dollar_store/website-technology-detector): Frameworks, CMS, analytics and ad tags on any site.
- [Website to Markdown Converter](https://apify.com/one_dollar_store/website-to-markdown-converter): Clean Markdown text from any web page.
- [Website Contact Scraper](https://apify.com/one_dollar_store/website-emails-phones-scraper): Emails, phones and social links from company sites.
- [PDF Text Extractor](https://apify.com/one_dollar_store/pdf-text-extractor-scraper): Text and page data from PDF links.

### Changelog

- October 2026: first release.

# Actor input Schema

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

Pages to audit, one per line. A bare domain (example.com) or a full address. Each address gives one row. Pages that robots.txt does not allow are skipped. Local and private addresses are refused. The page is read as plain HTML, so content that scripts add later is not part of the audit.

## `includeLinks` (type: `boolean`):

Add a links list to each row (up to 500 links, each with address, link text, internal or external and nofollow). Counts are always included.

## `maxItems` (type: `integer`):

The run stops after this many results. Minimum 10.

## Actor input object example

```json
{
  "urls": [
    "https://www.python.org/",
    "https://nextjs.org/docs",
    "https://www.gnu.org/philosophy/free-sw.html",
    "https://docs.apify.com/platform/actors",
    "https://en.wikipedia.org/wiki/Search_engine_optimization"
  ],
  "includeLinks": false,
  "maxItems": 10
}
```

# Actor output Schema

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

No description

## `summary` (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 = {
    "urls": [
        "https://www.python.org/",
        "https://nextjs.org/docs",
        "https://www.gnu.org/philosophy/free-sw.html",
        "https://docs.apify.com/platform/actors",
        "https://en.wikipedia.org/wiki/Search_engine_optimization"
    ],
    "maxItems": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("one_dollar_store/seo-page-audit-scraper").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://www.python.org/",
        "https://nextjs.org/docs",
        "https://www.gnu.org/philosophy/free-sw.html",
        "https://docs.apify.com/platform/actors",
        "https://en.wikipedia.org/wiki/Search_engine_optimization",
    ],
    "maxItems": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("one_dollar_store/seo-page-audit-scraper").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://www.python.org/",
    "https://nextjs.org/docs",
    "https://www.gnu.org/philosophy/free-sw.html",
    "https://docs.apify.com/platform/actors",
    "https://en.wikipedia.org/wiki/Search_engine_optimization"
  ],
  "maxItems": 10
}' |
apify call one_dollar_store/seo-page-audit-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,one_dollar_store/seo-page-audit-scraper"
        }
    }
}
```

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/zQLgLtMPT5e76qfWm/builds/OdvPBFtTtBeFd0hN7/openapi.json
