# Bulk SEO Metadata & Indexability Checker (`vertaelme/seo-metadata-indexability-snapshot`) Actor

Check titles, descriptions, H1/H2, canonicals, hreflang, robots directives and social metadata on authorized pages. Export one JSON/CSV snapshot per page for n8n and SEO monitoring. Static HTML only; no Google indexing claims.

- **URL**: https://apify.com/vertaelme/seo-metadata-indexability-snapshot.md
- **Developed by:** [Vertaelme](https://apify.com/vertaelme) (community)
- **Categories:** SEO tools, Developer tools
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$0.75 / 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

## Bulk SEO Metadata & Indexability Checker

Audit explicit URLs you own or have permission to check. Spot missing titles, unexpected noindex directives, conflicting canonicals and heading changes, then send consistent page snapshots to n8n, a dashboard or your SEO reporting workflow.

One result per successfully parsed static HTML page. No browser, login, site-wide crawl or AI service.

### Get started

1. Paste the full page addresses into **Pages to check**.
2. Confirm that you have permission to check them.
3. Choose **Maximum pages** and start the run.
4. Open the Dataset for JSON/CSV, and the run report for failures or limits.

You can try the explicitly permitted scraping sandbox:

```json
{
  "urls": ["https://books.toscrape.com/"],
  "confirmSourceRights": true,
  "maxResults": 1
}
```

Only the supplied pages are checked. Links and sitemaps are not followed. A maximum of 100 input URLs and 100 page results is supported per run.

### What you get

| Data | Fields |
|---|---|
| Page identity | `url`, `finalUrl`, `httpStatus`, `checkedAt` |
| Titles and descriptions | `title`, `titleLength`, `description`, `descriptionLength` |
| Headings and language | `h1`, `h2`, `language` |
| Canonical and language alternatives | `canonicalUrl`, `canonicalCount`, `hreflang` |
| Indexing directives | `metaRobots`, `xRobotsTag`, `noindexDetected` |
| Social and structured metadata | `openGraph`, `twitter`, `schemaTypes` |
| Findings | Missing fields, multiple titles/H1/canonicals, noindex and invalid JSON-LD |

Missing text is `null`; lists and objects can be empty. Values are extracted from the received HTML and response headers, not invented. JSON-LD output lists declared types; it is not complete schema validation or rich-result certification.

This shortened example is illustrative, generated from the bundled HTML fixture rather than a live request:

```json
{
  "url": "https://owned.example/office/",
  "finalUrl": "https://owned.example/office/",
  "httpStatus": 200,
  "title": "Example Store | Office Supplies",
  "description": "Office supplies for small teams. Explore our product range.",
  "canonicalUrl": "https://owned.example/office/",
  "h1": ["Office supplies"],
  "h2": ["For small teams"],
  "noindexDetected": false,
  "schemaTypes": ["WebPage"],
  "findings": [],
  "checkedAt": "2026-10-24T00:00:00Z"
}
```

The complete sample and field definitions ship with the release package.

### Understand the results

**A valid snapshot:** an HTTP 200 HTML document with an HTML element that passes the source checks and is not recognized as an access challenge. Missing SEO fields are useful findings, so a page without a title or description can still be a valid snapshot. Errors, denied requests and non-HTML responses do not become page results. Challenge detection is best-effort and cannot identify every interstitial.

**Noindex is a declaration, not a search-engine verdict.** `noindexDetected` flags noindex/none directives conservatively, including engine-scoped headers. It does not show whether Google indexed a page, prove eligibility for indexing or predict rankings. Review the raw directive fields for context.

**Duplicates:** repeated input URLs are normalized and deduplicated. Different inputs leading to the same final URL produce one snapshot. Canonical declarations do not merge distinct pages. Resolving a redirect alias can still use web requests.

**Partial or empty results:** inspect `RUN_REPORT`. It records request counts, errors, stop reason, write uncertainty and skipped prior output attempts. An empty result set is not proof that a site has no SEO metadata. Completed results remain available after an error or time limit; source/output errors finish the run as failed.

### Limits and supported pages

- Static HTTP(S) HTML only. No JavaScript rendering, Core Web Vitals, rank tracking, backlink checks or full SEO scores.
- Robots.txt is always respected. Missing robots files (404/410) permit crawling, but do not grant content rights. Other unreadable or failed policies block access.
- Same-host HTTP-to-HTTPS upgrades are supported. Redirects to another hostname, HTTPS downgrades and unusual ports are rejected. Supply the final address when possible.
- Standard ports 80/443 and public destinations only. Private/internal addresses, credentialed URLs, configured source proxies and unsupported compression are rejected.
- The checker requests uncompressed HTML and rejects compressed responses before decoding. Sites that ignore this preference are currently unsupported.
- Requests have an eight-second timeout. Pages are limited to 1 MB, robots files to 128 KB, and each redirect chain to two followed redirects. Result records are limited to 1 MB of JSON.

Public visibility alone is not permission to collect or reuse a website's data. Use the checker for authorized pages and follow the applicable terms.

### Advanced limits

Defaults: `maxResults: 10`, `maxSourceRequests: 20`, `timeoutSeconds: 30`. Web request limits include robots, redirects and failures, so the requested page count may not be reached. The processing time limit includes page lookups, requests, parsing and writes; platform startup and cleanup are separate. Larger batches may need a higher platform run timeout.

There are no automatic source-request or dataset-write retries. After an uncertain storage acknowledgement, the same run does not replay that output attempt. This favors avoiding duplicates over completeness: a reserved result may be missing after a crash. Check the Dataset and run report before investigating. A fresh run is a new audit; checking the same URL in a new run is a new snapshot.

### JSON, CSV and automation

Use Apify Dataset export, or read the existing Dataset through the API:

```text
GET https://api.apify.com/v2/datasets/DATASET_ID/items?format=json&clean=true
Authorization: Bearer YOUR_APIFY_TOKEN
```

Change `format=json` to `format=csv` for CSV. Nested lists and objects may expand into CSV columns, depending on export options. Import website-supplied text as text in spreadsheet applications; do not execute it as formulas. Keep API tokens in secrets, not in URLs or workflow logs.

For n8n, start an Actor run through the Apify integration, wait for completion, then read its Dataset and `RUN_REPORT`. Scheduling is optional and creates separate runs; no automatic schedule is configured by this release.

### Pricing

**Introductory price: $0.75 per 1,000 valid page snapshots** ($0.00075 each), with no startup event. The result event applies only to valid snapshots saved to the default Dataset, not failed web requests or individual metadata fields. Check the Pricing tab for the currently effective settings.

An acknowledgement can be lost after a record was saved. That record may still be present and charged once; it is not automatically written or charged again by this Actor. Platform credits, free-plan rules and run limits still apply. Build/resource usage and event charges are different things.

### Help with a run

Start with the Dataset and `RUN_REPORT`. `ROBOTS_DENIED` means the page was excluded by the site’s crawling rules; `NOT_HTML` means the response is not an HTML page; `SOURCE_PROXY_UNSUPPORTED` means the configured source proxy cannot provide the required connection safeguards. A spending-limit stop is not proof that a page lacks metadata. Include the run ID and affected URL when reporting a problem; keep tokens and private query parameters out of support messages.

# Actor input Schema

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

Paste up to 100 full page addresses, one per line. Use pages you own or have permission to check. The example uses Books to Scrape, a site provided for scraping practice. Only these pages are checked; links are not followed.

## `confirmSourceRights` (type: `boolean`):

The example is preconfirmed only for the permitted Books to Scrape sandbox. If you replace its URL, confirm that you own the new pages or have permission to check them. API requests must explicitly set this field to true. The checker also respects robots.txt restrictions.

## `maxResults` (type: `integer`):

Stop after this many valid page snapshots. Missing metadata is reported as a finding; failed or blocked requests are not page results.

## `maxSourceRequests` (type: `integer`):

Count all requests, including robots.txt, redirects and failures. Raising the maximum does not increase the number of pages you requested. No automatic request retries.

## `timeoutSeconds` (type: `integer`):

Stop page processing after this duration. Completed pages remain available. Static HTML only: JavaScript is not run.

## Actor input object example

```json
{
  "urls": [
    "https://books.toscrape.com/"
  ],
  "confirmSourceRights": true,
  "maxResults": 1,
  "maxSourceRequests": 2,
  "timeoutSeconds": 20
}
```

# Actor output Schema

## `dataset` (type: `string`):

No description

## `report` (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://books.toscrape.com/"
    ],
    "confirmSourceRights": true,
    "maxResults": 1,
    "maxSourceRequests": 2,
    "timeoutSeconds": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("vertaelme/seo-metadata-indexability-snapshot").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/"],
    "confirmSourceRights": True,
    "maxResults": 1,
    "maxSourceRequests": 2,
    "timeoutSeconds": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("vertaelme/seo-metadata-indexability-snapshot").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/"
  ],
  "confirmSourceRights": true,
  "maxResults": 1,
  "maxSourceRequests": 2,
  "timeoutSeconds": 20
}' |
apify call vertaelme/seo-metadata-indexability-snapshot --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,vertaelme/seo-metadata-indexability-snapshot"
        }
    }
}
```

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/5nSu3xALwQDDj6PXd/builds/D3JaCwnt0cqqK8Vci/openapi.json
