# SEO & AI Search (GEO) Audit: ChatGPT, Perplexity Readiness (`michael80/seo-geo-audit`) Actor

Bulk SEO audit plus AI search readiness (GEO) for every page: titles, meta, headings, canonical, noindex, schema, JS-only content, AI crawler access in robots.txt (GPTBot, OAI-SearchBot, PerplexityBot, ClaudeBot), llms.txt. Scores 0-100 with concrete fixes. Failed pages free.

- **URL**: https://apify.com/michael80/seo-geo-audit.md
- **Developed by:** [Michael Schönherr](https://apify.com/michael80) (community)
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $15.00 / 1,000 page auditeds

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 & AI Search (GEO) Audit: ChatGPT, Perplexity Readiness

Audit any number of pages for **classic SEO** and for **AI search readiness (GEO)** in one run. Every page gets two scores from 0 to 100, a short list of the most important issues and **a concrete fix for every issue**. One flat row per page, ready for CSV, Excel or a client report.

### What it checks

| Area | Checks |
| --- | --- |
| **Indexing** | HTTP status, HTTPS, redirect chains, `noindex` (meta and X-Robots-Tag), Googlebot blocked in robots.txt, canonical |
| **On-page SEO** | title and meta description length, H1 count, heading order, `lang`, mobile viewport, image alt texts, word count, internal/external links, Open Graph, server response time |
| **AI search (GEO)** | content visible **without JavaScript** (most AI crawlers do not run JS), structured data (JSON-LD/microdata, invalid JSON), Organization/LocalBusiness schema, freshness date, subheadings and answer blocks (question headings, lists, tables) |
| **AI crawler access** | which **AI search crawlers** (OAI-SearchBot, ChatGPT-User, PerplexityBot, Perplexity-User, Claude-SearchBot, Claude-User) and **AI training crawlers** (GPTBot, ClaudeBot, Google-Extended, CCBot, Applebot-Extended, Meta-ExternalAgent) robots.txt blocks **for this exact page**. robots.txt is read like Google does (most specific group, longest rule, `*` and `$` wildcards) |
| **llms.txt** | whether `/llms.txt` exists and follows the proposed format |

Blocking **training** crawlers is a legitimate choice and is only reported. Blocking **search** crawlers lowers the GEO score, because those crawlers fetch pages for answers in ChatGPT, Perplexity and Claude.

### Who uses it

| User | Typical use |
| --- | --- |
| SEO / GEO agency | Bulk audit of client sites, before/after reports, prioritized fix lists |
| In-house marketing | Check all pages from the sitemap for noindex mistakes, missing schema and blocked AI crawlers |
| Developers | Verify that a JavaScript site is readable for crawlers without a browser |

### Output (one row per page)

| Field | Example |
| --- | --- |
| `url`, `finalUrl`, `status`, `httpStatus` | `https://example.com/pricing`, `ok`, `200` |
| `seoScore`, `geoScore`, `issueCount`, `topIssues` | `86`, `63`, `5`, `[critical] robots.txt blocks AI search crawlers: PerplexityBot ...` |
| `issues` | list of `{area, severity, code, message, fix}` |
| `title`, `metaDescription`, `h1`, `canonical`, `canonicalStatus`, `indexable` | `self`, `true` |
| `wordCount`, `jsRenderedContent`, `structuredDataTypes`, `jsonLdErrors`, `hasFreshnessDate` | `742`, `false`, `["Organization","FAQPage"]` |
| `aiSearchCrawlersBlocked`, `aiTrainingCrawlersBlocked`, `blockedByRobotsTxt` | `["PerplexityBot"]`, `["GPTBot","CCBot"]`, `false` |
| `llmsTxt`, `llmsTxtValid`, `robotsTxt`, `sitemapInRobotsTxt` | `found`, `true`, `found`, `true` |
| `responseTimeMs`, `redirects`, `htmlKb`, `imagesMissingAlt`, `internalLinks` | `412`, `0`, `85.3`, `2`, `48` |

The **Overview** table shows scores, top issues and AI crawler access at a glance.

### Pricing

| | Price |
| --- | --- |
| `page-audited` | **$15.00 per 1,000 pages** ($0.015 per page) |
| Pages that fail, are blocked, return 4xx/5xx or are not HTML | free |
| Invalid inputs | free |

robots.txt, llms.txt and the sitemap are read once per website and are included. Set a maximum cost per run in Apify: the Actor stops cleanly when it is reached.

### Input example

```json
{
  "urls": ["example.com", "https://example.com/pricing"],
  "crawlSitemap": true,
  "maxPagesPerSite": 50,
  "checkAiCrawlers": true
}
```

### Tips

- **Whole website:** enter the homepage and switch on *Also audit pages from the sitemap*.
- **Fix the critical issues first:** `topIssues` is sorted by severity (critical, high, medium, low).
- **Monthly report:** schedule the Actor and export the dataset to Google Sheets or Looker Studio.
- **Core Web Vitals:** combine with our [Bulk PageSpeed Insights Checker](https://apify.com/michael80/pagespeed-core-web-vitals-bulk-monitor).

### FAQ

**Does it render JavaScript?** No, on purpose. It sees the page like most AI crawlers do. If your content only appears after JavaScript runs, `jsRenderedContent` is `true`, and that is exactly the problem to fix.

**Does llms.txt matter?** It is a proposed standard. No major AI search engine has confirmed that it uses llms.txt, so a missing file is only a low-severity hint.

**Is the GEO score a ranking guarantee?** No. It measures technical readiness: whether AI systems can access, read and understand the page. Whether you are cited also depends on your content and reputation.

**Something wrong or missing?** Open an issue on the Actor page. We usually answer within one business day.

***

### Kurz auf Deutsch

Prüft beliebig viele Seiten (optional die ganze Sitemap) auf klassisches **SEO** und auf **Sichtbarkeit in KI-Suchen (GEO)**: Indexierbarkeit, Titel, Meta-Beschreibung, Überschriften, Canonical, strukturierte Daten, Inhalt ohne JavaScript, gesperrte KI-Crawler in der robots.txt und llms.txt. Je Seite zwei Punktwerte (0–100) und zu jedem Problem eine konkrete Lösung. Fehlgeschlagene Seiten sind kostenlos.

***

**Anbieter / Impressum (Angaben gemäß § 5 DDG):** Michael Schönherr · Am Spiegelwald 1 · 08344 Grünhain-Beierfeld · Deutschland · E-Mail: m.schoenherr80@gmail.com

# Changelog

This Actor's version history is a separate document: https://apify.com/michael80/seo-geo-audit/changelog.md

# Actor input Schema

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

Page URLs or domains, one per line (e.g. 'example.com' or 'https://example.com/pricing'). Each entry is audited as one page. Duplicates are removed; up to 10,000 pages per run.

## `crawlSitemap` (type: `boolean`):

For every homepage in the list, also audit pages from the site's XML sitemap (from robots.txt or /sitemap.xml, including sitemap indexes and .gz files). Only pages on the same host are added.

## `maxPagesPerSite` (type: `integer`):

Upper limit of pages per website when 'Also audit pages from the sitemap' is on. Each audited page is charged once.

## `checkAiCrawlers` (type: `boolean`):

Reads robots.txt and reports which AI search crawlers (OAI-SearchBot, ChatGPT-User, PerplexityBot, Claude-SearchBot ...) and AI training crawlers (GPTBot, ClaudeBot, Google-Extended, CCBot ...) are blocked for each page.

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

How many pages are loaded at the same time. Keep it low for small websites.

## `selfTest` (type: `boolean`):

Audits one fixed public page to check that everything works. Ignores the page list.

## Actor input object example

```json
{
  "urls": [
    "https://www.python.org/",
    "https://apify.com/"
  ],
  "crawlSitemap": false,
  "maxPagesPerSite": 25,
  "checkAiCrawlers": true,
  "maxConcurrency": 5,
  "selfTest": false
}
```

# Actor output Schema

## `overview` (type: `string`):

One row per page: scores, top issues, indexability, schema types, AI crawler access.

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

Complete rows including every issue with severity, explanation and fix.

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

// Run the Actor and wait for it to finish
const run = await client.actor("michael80/seo-geo-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://www.python.org/",
        "https://apify.com/",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("michael80/seo-geo-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://www.python.org/",
    "https://apify.com/"
  ]
}' |
apify call michael80/seo-geo-audit --silent --output-dataset

```

## MCP server setup

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