# SEO On-Page Audit - Meta Tags, Canonical & Schema Checker (`paperlinenstudio/seo-onpage-audit`) Actor

Audit pages, a whole site (crawl) or a sitemap for on-page SEO: title and meta description length, H1/H2, canonical, noindex and robots.txt, hreflang, Open Graph, Twitter cards, JSON-LD schema types, alt texts, word count, duplicate titles. Score and plain-English issues per page. No browser.

- **URL**: https://apify.com/paperlinenstudio/seo-onpage-audit.md
- **Developed by:** [Paper & Linen Studio](https://apify.com/paperlinenstudio) (community)
- **Categories:** SEO tools, Developer tools, Automation
- **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/actors/running/actors-in-store.md#pay-per-usage

## 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 On-Page Audit – Meta Tags, Canonical & Schema Checker

Audit **single pages, a whole website (crawl) or every URL in a sitemap** for on-page SEO. You get **one row per page** with all the important tags, a **0–100 score** and a list of **plain-English issues**, plus a **site summary** with the most common problems and duplicate titles and descriptions.

Fast and cheap: plain HTTP with no browser, no API key and no proxy.

### What it checks

| Area | Fields |
|---|---|
| **Status & indexability** | HTTP status, redirects / final URL, `noindex` in meta robots or `X-Robots-Tag`, **blocked by robots.txt** (Googlebot), canonical URL and whether it is self-referencing |
| **Titles & descriptions** | title and length, meta description and length (with recommended ranges) |
| **Headings & content** | H1 list and count, H2 count and first H2s, word count (thin content), `lang` attribute, viewport (mobile) |
| **International** | `hreflang` alternates |
| **Social** | Open Graph (`og:title`, `og:description`, `og:image`, …) and Twitter card tags |
| **Structured data** | **JSON-LD schema types** (e.g. Product, Organization, BreadcrumbList, FAQPage), invalid JSON-LD blocks, microdata types |
| **Images & links** | images without `alt`, internal / external / nofollow link counts |
| **Performance hints** | response time and HTML size |

The SUMMARY record (key-value store) lists the **top issues across the site**, **duplicate titles** and **duplicate meta descriptions** with their URLs, the average score, and counts of 4xx/5xx, noindex and pages missing a title, description or H1.

### What you can use it for

- **Quick site audits** for clients or prospects: crawl 100 pages and export to Excel / Google Sheets.
- **Pre-launch / relaunch checks**: make sure no page went live with `noindex`, a wrong canonical or a missing title.
- **Monitoring**: schedule it weekly on your sitemap and catch regressions (pages that became noindex, 404s, lost structured data).
- **Content inventory**: titles, descriptions, H1s and word counts of every URL in a sitemap.
- **Structured data coverage**: which pages carry Product, Article, FAQ or Breadcrumb schema.
- Pair it with the [Sitemap URL Extractor](https://apify.com/paperlinenstudio/sitemap-url-extractor) and the [Broken Link Checker](https://apify.com/paperlinenstudio/broken-link-checker).

### Input

| Field | Description |
|---|---|
| `startUrls` | Pages, a homepage to crawl, or sitemap URLs (`.xml` / `.xml.gz`, sitemap indexes are followed) |
| `crawlSite` | Follow internal links from the start URLs (default `false`) |
| `maxPages` | Max pages audited in total (default 100) |
| `maxDepth` | Crawl depth (default 3) |
| `respectRobotsTxt` | Skip pages disallowed for Googlebot while crawling (default `true`) |
| `onlyPagesWithIssues` | Save only pages with at least one issue |
| `maxConcurrency`, `timeoutSecs` | Politeness and limits |

```json
{"startUrls": ["https://www.example.com/"], "crawlSite": true, "maxPages": 200}
```

```json
{"startUrls": ["https://www.example.com/sitemap.xml"], "maxPages": 1000, "onlyPagesWithIssues": true}
```

### Output (real example, shortened)

```json
{
  "url": "https://www.heise.de/",
  "finalUrl": "https://www.heise.de/",
  "statusCode": 200,
  "redirected": false,
  "contentType": "text/html",
  "responseTimeMs": 498,
  "pageSizeKb": 918.9,
  "isHtml": true,
  "blockedByRobotsTxt": false,
  "title": "heise online - IT-News, Nachrichten und Hintergründe | heise online",
  "titleLength": 67,
  "metaDescription": "News und Foren zu Computer, IT, Wissenschaft, Medien und Politik. Preisvergleich von Hardware und Software sowie Downloads bei Heise Medien.",
  "metaDescriptionLength": 140,
  "metaRobots": "all, max-image-preview:large",
  "xRobotsTag": null,
  "noindex": false,
  "canonical": "https://www.heise.de/",
  "canonicalIsSelf": true,
  "h1": [],
  "h1Count": 0,
  "h2Count": 23,
  "h2": [
    "Tests, Ratgeber und Kaufberatungen",
    "IT",
    "Wissenschaft & Forschung"
  ],
  "lang": "de",
  "viewport": true,
  "hreflang": [
    {
      "lang": "en",
      "url": "https://www.heise.de/en"
    },
    {
      "lang": "x-default",
      "url": "https://www.heise.de"
    }
  ],
  "openGraph": {
    "title": "heise online - IT-News, Nachrichten und Hintergründe | heise online",
    "description": "News und Foren zu Computer, IT, Wissenschaft, Medien und Politik. Preisvergleich von Hardware und Software sowie Downloads bei Heise Medien.",
    "image": "https://heise.cloudimg.io/bound/1200x1200/q85.png-lossy-85.webp-lossy-85.foil1/_www-heise-de_/icons/ho/opengraph/opengraph.png",
    "type": "website",
    "url": "https://www.heise.de",
    "site_name": "heise online"
  },
  "twitterCard": {
    "card": "summary_large_image",
    "title": "heise online - IT-News, Nachrichten und Hintergründe | heise online",
    "description": "News und Foren zu Computer, IT, Wissenschaft, Medien und Politik. Preisvergleich von Hardware und Software sowie Downloads bei Heise Medien.",
    "image": "https://heise.cloudimg.io/bound/1200x1200/q85.png-lossy-85.webp-lossy-85.foil1/_www-heise-de_/icons/ho/opengraph/opengraph.png"
  },
  "jsonLdTypes": [
    "ItemList",
    "ListItem",
    "Thing"
  ],
  "jsonLdErrors": 0,
  "microdataTypes": [],
  "wordCount": 3930,
  "imagesCount": 509,
  "imagesWithoutAlt": 32,
  "imagesWithEmptyAlt": 287,
  "internalLinks": 279,
  "externalLinks": 102,
  "nofollowLinks": 0,
  "depth": 0,
  "issues": [
    "Title too long (67 characters, may be truncated in Google; aim for 30-60)",
    "Missing H1 heading",
    "Images without alt attribute (32)"
  ],
  "score": 85,
  "auditedAt": "2026-11-03T13:28:37+00:00"
}
```

#### How the score works

Each page starts at 100. Points are deducted for every issue, weighted by importance: for example, noindex and robots.txt blocks −30, a missing title −20, a missing meta description −10, HTTP instead of HTTPS −10, a missing viewport or H1 −8, and small things like title length or missing alt text −3 to −5. 4xx/5xx pages score 0. The score is a quick way to sort pages; **the `issues` list is what to act on.**

### Limitations

- Pages are fetched **without a browser**. Tags that are added only by JavaScript (some single-page apps) are not visible. This is roughly what search engines see on their first, non-rendered pass.
- Websites with aggressive bot protection may return 403 or challenge pages. They are reported with their status code.
- The thresholds (title 30–60 characters, description 70–160, thin content under 200 words) are common best-practice guidelines, not official Google rules.
- The audit covers on-page factors only. It does not measure backlinks, rankings or Core Web Vitals.

### Pricing

Free to use. You only pay for the Apify platform usage of your run, which is very low because no browser is used.

***

### Deutsch: Kurzbeschreibung

**SEO On-Page Audit**: einzelne Seiten, eine ganze Website (Crawl) oder alle URLs einer Sitemap prüfen:

- Title und Meta-Description (inkl. Länge)
- H1/H2
- Canonical, noindex, robots.txt
- hreflang
- Open Graph und Twitter Cards
- JSON-LD-Schema-Typen
- Alt-Texte, Wortanzahl, interne und externe Links

Pro Seite gibt es einen Score von 0–100 und verständliche Hinweise. Dazu kommt eine Zusammenfassung mit den häufigsten Problemen sowie doppelten Titles und Descriptions. Ohne Browser, daher schnell und günstig. Ideal für Agenturen, Relaunch-Checks und regelmäßiges Monitoring per Zeitplan.

# Actor input Schema

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

Pages to audit. Enter single pages, a homepage (turn on "Crawl the website" to follow internal links) or a sitemap URL ending in .xml / .xml.gz (all pages in it are audited, sitemap indexes are followed).

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

Follow internal links from the start URLs (same domain, www and non-www count as the same site) up to Max pages and Max depth.

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

Maximum number of pages to audit (from all start URLs, sitemaps and crawling together).

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

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

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

Do not crawl pages that robots.txt disallows for Googlebot. Every audited page also gets a blockedByRobotsTxt flag.

## `onlyPagesWithIssues` (type: `boolean`):

Skip pages with a perfect result in the dataset (they are still counted in the SUMMARY).

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

Pages fetched in parallel. Keep it low (default 5) to be gentle with the website.

## `timeoutSecs` (type: `integer`):

How long to wait for each page.

## Actor input object example

```json
{
  "startUrls": [
    "https://www.heise.de/"
  ],
  "crawlSite": false,
  "maxPages": 100,
  "maxDepth": 3,
  "respectRobotsTxt": true,
  "onlyPagesWithIssues": false,
  "maxConcurrency": 5,
  "timeoutSecs": 20
}
```

# Actor output Schema

## `items` (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 = {
    "startUrls": [
        "https://www.heise.de/"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("paperlinenstudio/seo-onpage-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": ["https://www.heise.de/"] }

# Run the Actor and wait for it to finish
run = client.actor("paperlinenstudio/seo-onpage-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": [
    "https://www.heise.de/"
  ]
}' |
apify call paperlinenstudio/seo-onpage-audit --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,paperlinenstudio/seo-onpage-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/Zu337T81cHqhcmHu1/builds/C3UeXuEcczRjlUTwe/openapi.json
