# SiteVerdict — Schema Markup Validator & JSON-LD Generator (`siteverdict/siteverdict-schema-markup-tool`) Actor

Extract and validate JSON-LD, Microdata and RDFa against schema.org and Google rich-result rules (Product, Article, FAQ, Recipe, Event, JobPosting, LocalBusiness...). Get exact errors, ready-to-paste JSON-LD for each page and optional AI generation with your own key.

- **URL**: https://apify.com/siteverdict/siteverdict-schema-markup-tool.md
- **Developed by:** [Aman Thakran](https://apify.com/siteverdict) (community)
- **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

## SiteVerdict — Schema Markup Validator & JSON-LD Generator

**Find the structured-data problems that can stop your pages getting rich results, and get corrected JSON-LD to paste.** The tool extracts **JSON-LD, Microdata and RDFa**, checks them against schema.org and **Google's rich-result requirements**, and reports each problem with its exact path. Example: `Product.offers[0].price: "£89.99" must be a plain number`. Every page also gets **recommended JSON-LD** built from what is actually on the page. An optional AI step on **your own** Anthropic/OpenAI key writes complete markup, which we then re-validate.

Who it is for: SEO agencies, e-commerce and publisher teams, developers shipping templates, and AI agents (MCP-ready).

### How to use it

1. Add one or more page URLs under **URLs** (or paste HTML / JSON-LD instead).
2. Set **Max cost per run** in the run options (each validated page costs $0.002).
3. Click **Start**. Open the **Output** tab, or download `ISSUES-CSV` from the key-value store.

**The example input** (the same input the console pre-fills) costs **$0.002** and took about 2 seconds on 15 Oct 2026:

```json
{ "urls": [{ "url": "https://www.bbcgoodfood.com/recipes/easy-pancakes" }] }
```

### Three ways people use it

1. **Template QA.** Validate one product, article and category URL after every release. Monitoring alerts you when a page **loses** markup or rich-result eligibility.
2. **Site-wide audit.** Turn on `crawl`, validate up to 2,000 pages, and download `ISSUES-CSV` for the dev team.
3. **Generate markup.** For pages with no or broken markup, copy the recommended JSON-LD snippet. Fill in the few `FILL_IN` values we could not read from the page, or let your own AI key complete them.

### Example (sample mode, abridged)

```json
{ "url": "https://demo-schema.siteverdict.test/products/trail-boot",
  "invalidFeatures": ["Product snippet", "Merchant listing"],
  "items": [{ "type": "Product", "source": "json-ld", "errors": [
    { "path": "Product.offers[0].price", "message": "Price \"£89.99\" must be a plain number (no currency symbol or thousands separator), e.g. 1299.99" },
    { "path": "Product.offers[0].priceCurrency", "message": "Offer needs \"priceCurrency\"" },
    { "path": "Product.offers[0].availability", "message": "\"In stock\" is not a valid value (use https://schema.org/InStock, ...)" },
    { "path": "Product.aggregateRating.ratingValue", "message": "ratingValue 6 is outside 1-5 (set bestRating if your scale is not 1-5)" },
    { "path": "Product.Description", "message": "Unknown property \"Description\" - did you mean \"description\"? (property names are case-sensitive)" } ] }],
  "recommendation": { "pageType": "product", "fillIn": ["sku", "brand.name"], "snippet": "<script type=\"application/ld+json\">..." } }
```

### Rich-result rules encoded (Google Search Central, checked 11 Oct 2026)

Article / NewsArticle / BlogPosting · Breadcrumb · Product snippet · Merchant listing (offers, GTIN, shipping/returns) · Review snippet (with a note on self-serving reviews) · Recipe · Event (location, ISO dates with timezone, status/attendance enums) · Job posting (expired `validThrough`, employment type, remote jobs) · Local business (address, geo, opening hours hh:mm) · Organization (logo, sameAs) · FAQ (flagged: limited to authoritative gov/health sites since 2023) · Q\&A · Video · Software app · Dataset · Discussion forum · Profile page · Image metadata (licence) · Carousel (ItemList) · How-to and Sitelinks search box (flagged as retired).

Generic checks on every item: JSON parse errors with line/column, missing `@context`/`@type`, unknown or wrongly-cased types and properties, empty values, relative URLs, non-ISO dates/durations, ISO 4217 currencies, rating ranges and `<script>` injected into values. Unknown types and properties are flagged only when a close match exists, so rare valid terms are not called errors.

### Output

- Dataset **Pages**: score (0–100), rich-result eligibility per feature (`eligible` / `partial` = recommended fields missing / `invalid`), errors, warnings, notes, formats found and the recommended JSON-LD.
- **AI-generated JSON-LD** rows (if enabled), each with our validation result.
- **Summary**, plus key-value store `REPORT` (HTML), `ISSUES-CSV` and `SUMMARY`.

### Pricing (pay per event)

| Event | Price | When |
|---|---|---|
| `page-validated` | $0.002 | Each page (or pasted HTML/JSON-LD) validated, including the recommended JSON-LD |
| `ai-schema` | $0.01 | Each AI-generated JSON-LD block delivered (your AI key pays the provider) |

Examples: 100 pages ≈ **$0.20**. 1,000 pages ≈ **$2.00**. 10 AI generations add **$0.10**. Errors, robots-blocked pages, non-HTML URLs, failed AI calls and sample mode are free. You pay only these event prices; Apify platform usage for the run is included. The run stops cleanly at your **Max cost per run**.

### FAQ

**Is this the same as Google's Rich Results Test?** No. It encodes Google's published required and recommended properties and value formats so you can check thousands of pages at once. Google alone decides whether to show a rich result, so confirm key templates with Google's test.

**What about JSON-LD injected by JavaScript?** Only markup present in the server HTML is read. If a tag manager adds yours, paste the rendered HTML into `html`.

**Will the generator invent data?** The built-in generator uses only facts found on the page and marks unknown required values `FILL_IN: ...`. The optional AI step is told not to invent prices, ratings, reviews, addresses or dates, and its output is re-validated, but AI models can still make mistakes. AI rows are kept apart as `type: "ai-schema"`: check every value against the page before you publish it.

### Limits

- 2,000 URLs per run, 5 MB per page, 2 MB of pasted content, 50 AI generations per run.
- The vocabulary is a curated subset of schema.org, so very rare types are never flagged as unknown.

### Sources & compliance

- Pages are fetched politely with the honest user agent `Mozilla/5.0 (compatible; SiteVerdictBot/1.0; SiteVerdict/schema-markup-tool; +https://apify.com/siteverdict/siteverdict-schema-markup-tool)`. robots.txt is obeyed for the `SiteVerdictBot` token; blocked pages are reported and not charged.
- Rules: Google Search Central structured-data docs (developers.google.com/search/docs/appearance/structured-data, read 11 Oct 2026). The schema.org type and property **names** were written by hand; no vocabulary files are bundled.
- Fetching is SSRF-safe. The tool never bypasses bot protection.
- The Actor does not look for personal data. Structured data can contain names the site publishes (for example authors or reviewers), and only that is repeated in the output. Under UK GDPR / EU GDPR **you are the controller** of your audit; check only pages you own or may audit. This is not legal advice.
- The AI key and webhook URL are secret inputs, redacted from logs and output. The AI step is off by default and uses only your key. When it is on, the facts extracted from the page, the recommended JSON-LD and the problems found are sent to the AI provider you chose, under your own account with that provider.
- **Independent tool:** not affiliated with or endorsed by Google or schema.org. Google alone decides whether a rich result is shown.

### Your data, storage and retention

- Results go only to **your own Apify account**: the run's dataset and key-value store (`REPORT`, `ISSUES-CSV`, `SUMMARY`). They are kept for your Apify plan's data-retention period, unless you delete them sooner.
- With `monitoring` on (the default), the Actor keeps one snapshot per project in the named key-value store `siteverdict-schema-history` in your account: for each URL, the schema types found, the rich-result features that were eligible and the error count. It stays until you delete it. Turn `monitoring` off to keep nothing between runs.
- An alert to your webhook (only when monitoring finds a change) contains the affected URLs, the types or features lost, new errors and fixed items.
- The Actor contacts only the pages you submit, your AI provider if enabled, and your webhook if set. It sends nothing to the developer, and the developer receives no copy of your input or results.

# Changelog

This Actor's version history is a separate document: https://apify.com/siteverdict/siteverdict-schema-markup-tool/changelog.md

# Actor input Schema

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

One or more page URLs (max 2,000).

## `crawl` (type: `boolean`):

Follow internal links from each URL and validate every page found (up to 'Max pages').

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

Upper limit of pages validated when crawling.

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

How deep to follow links.

## `includePatterns` (type: `array`):

Globs (/products/\*\*) or /regex/.

## `excludePatterns` (type: `array`):

Globs or /regex/.

## `html` (type: `string`):

Paste a full HTML page or snippet to validate without fetching anything (max 2 MB).

## `jsonLd` (type: `string`):

Paste a JSON-LD object/array to validate (max 2 MB).

## `generateRecommendations` (type: `boolean`):

Build ready-to-paste JSON-LD for each page from facts on the page (title, description, images, prices, FAQs...). Unknown values are marked FILL_IN.

## `aiGenerate` (type: `boolean`):

For pages with errors or no markup, ask your own Anthropic/OpenAI key for complete JSON-LD; the result is re-validated by our rules before output.

## `aiProvider` (type: `string`):

Which API your key belongs to.

## `aiApiKey` (type: `string`):

Your own API key (we never use ours). Stored encrypted as an Apify secret, never logged or written to the output.

## `aiModel` (type: `string`):

Leave empty for a fast, low-cost default (claude-haiku-4-5 / gpt-4.1-mini).

## `maxAiPages` (type: `integer`):

Cap on AI calls per run.

## `monitoring` (type: `boolean`):

Remember markup types and rich-result eligibility per URL (named store 'siteverdict-schema-history') and report pages that lost markup or eligibility.

## `projectName` (type: `string`):

Optional stable name used to store the monitoring snapshot, e.g. 'client-acme'. Defaults to the site host.

## `alertWebhookUrl` (type: `string`):

Optional. Slack incoming-webhook URL or any https:// endpoint that receives a JSON POST when monitoring finds new problems. Stored encrypted as an Apify secret, never logged or written to the output.

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

Parallel page fetches.

## `requestDelayMs` (type: `integer`):

Polite spacing per host. robots.txt Crawl-delay is honoured when it is larger.

## `requestTimeoutSecs` (type: `integer`):

Per request.

## `sampleMode` (type: `boolean`):

Run on a built-in demo with planted problems. No network requests and nothing charged. Use it to see the output format.

## Actor input object example

```json
{
  "urls": [
    {
      "url": "https://www.bbcgoodfood.com/recipes/easy-pancakes"
    }
  ],
  "crawl": false,
  "maxPages": 50,
  "maxDepth": 5,
  "generateRecommendations": true,
  "aiGenerate": false,
  "aiProvider": "anthropic",
  "maxAiPages": 5,
  "monitoring": true,
  "maxConcurrency": 2,
  "requestDelayMs": 500,
  "requestTimeoutSecs": 20,
  "sampleMode": false
}
```

# Actor output Schema

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

No description

## `report` (type: `string`):

No description

## `summary` (type: `string`):

No description

## `files` (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": [
        {
            "url": "https://www.bbcgoodfood.com/recipes/easy-pancakes"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("siteverdict/siteverdict-schema-markup-tool").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": [{ "url": "https://www.bbcgoodfood.com/recipes/easy-pancakes" }] }

# Run the Actor and wait for it to finish
run = client.actor("siteverdict/siteverdict-schema-markup-tool").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": [
    {
      "url": "https://www.bbcgoodfood.com/recipes/easy-pancakes"
    }
  ]
}' |
apify call siteverdict/siteverdict-schema-markup-tool --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,siteverdict/siteverdict-schema-markup-tool"
        }
    }
}
```

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/BA8SAYN3ymg346Jz7/builds/1mD6giHjBHhifYEh9/openapi.json
