{ "id": "Schema7p4Lx2Nd8w", "name": "Detect JSON-LD and schema markup regressions with Apify", "nodes": [ { "parameters": { "content": "## Detect structured-data regressions\n\nUse this template to monitor public pages for deterministic JSON-LD, Microdata, and RDFa regressions after CMS, theme, or product-catalog changes. It is for technical SEO teams, ecommerce operators, agencies, and release workflows that need explicit schema issue codes rather than a generic visual or text diff.\n\n### How it works\n1. The schedule starts the workflow once per day.\n2. **Set monitored pages** groups page URLs, optional sitemap URLs, namespace, first-run behavior, and page ceiling.\n3. **Audit structured data** runs Structured Data Change Monitor through Apify's verified n8n node. The Actor creates a quiet first baseline, applies stable rule-based checks, and suppresses unchanged pages.\n4. **Keep schema regressions** routes only negative changes: removed schema types, newly added issue codes, falling QA scores, or HTTP errors. Improvements do not create alerts.\n\n### Setup\n1. Install the verified `@apify/n8n-nodes-apify` node if Apify is not already available in your n8n instance.\n2. Add an Apify API credential to **Audit structured data**.\n3. Replace the example URL, add optional sitemap URLs, and choose a unique namespace in **Set monitored pages**.\n4. Test once to create the silent baseline, then activate the daily schedule.\n\n### Customization\nConnect the final node to issue tracking, email, Slack, a database, or a deployment-review workflow. The Actor's score and readiness fields are deterministic signals, not a promise that a search engine will display a rich result.", "height": 760, "width": 1700 }, "id": "schema-overview-sticky", "name": "Workflow overview", "type": "n8n-nodes-base.stickyNote", "typeVersion": 1, "position": [ -860, -1160 ] }, { "parameters": { "rule": { "interval": [ { "field": "days", "daysInterval": 1 } ] } }, "id": "schedule-schema-check", "name": "Every day", "type": "n8n-nodes-base.scheduleTrigger", "typeVersion": 1.2, "position": [ -200, 0 ] }, { "parameters": { "assignments": { "assignments": [ { "id": "schema-page-urls", "name": "pageUrlsCsv", "value": "https://apify.com/xerophylous_mercy_0wn/structured-data-change-monitor", "type": "string" }, { "id": "schema-sitemap-urls", "name": "sitemapUrlsCsv", "value": "", "type": "string" }, { "id": "schema-namespace", "name": "stateNamespace", "value": "production-structured-data", "type": "string" }, { "id": "schema-first-run", "name": "firstRunMode", "value": "baseline_only", "type": "string" }, { "id": "schema-max-pages", "name": "maxPages", "value": 25, "type": "number" } ] }, "options": {} }, "id": "set-monitored-pages", "name": "Set monitored pages", "type": "n8n-nodes-base.set", "typeVersion": 3.4, "position": [ 40, 0 ] }, { "parameters": { "memory": 1024, "actorId": "xerophylous_mercy_0wn~structured-data-change-monitor", "resource": "Actors", "operation": "Run actor and get dataset", "customBody": "={{ JSON.stringify({ urls: $json.pageUrlsCsv.split(',').map((value) => value.trim()).filter(Boolean), sitemapUrls: $json.sitemapUrlsCsv.split(',').map((value) => value.trim()).filter(Boolean), maxPages: $json.maxPages, monitorChanges: true, onlyChanges: true, firstRunMode: $json.firstRunMode, stateStoreName: 'structured-data-monitor-state', stateNamespace: $json.stateNamespace }) }}", "timeout": 180, "maxTotalChargeUsd": 1, "build": "" }, "id": "audit-structured-data", "name": "Audit structured data", "type": "@apify/n8n-nodes-apify.apify", "typeVersion": 1, "position": [ 320, 0 ] }, { "parameters": { "jsCode": "return items\n .filter((item) => {\n if (item.json.recordType !== 'structured_data_audit' || item.json.changed !== true) return false;\n const change = item.json.change ?? {};\n return (change.schemaTypesRemoved?.length ?? 0) > 0\n || (change.issueCodesAdded?.length ?? 0) > 0\n || Number(change.scoreDelta ?? 0) < 0\n || Number(item.json.httpStatus ?? 0) >= 400;\n })\n .map((item) => ({\n json: {\n url: item.json.url,\n finalUrl: item.json.finalUrl,\n httpStatus: item.json.httpStatus,\n title: item.json.title,\n score: item.json.score,\n scoreDelta: item.json.change?.scoreDelta ?? 0,\n richResultReady: item.json.richResultReady,\n removedSchemaTypes: item.json.change?.schemaTypesRemoved ?? [],\n newIssueCodes: item.json.change?.issueCodesAdded ?? [],\n currentIssues: item.json.issues ?? [],\n checkedAt: item.json.checkedAt\n },\n pairedItem: item.pairedItem\n }));" }, "id": "keep-schema-regressions", "name": "Keep schema regressions", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 600, 0 ] }, { "parameters": { "content": "### 1. Schedule and targets\nEdit page URLs, optional sitemap URLs, the page ceiling, and a unique state namespace before activation.", "height": 240, "width": 500, "color": 7 }, "id": "schema-configure-sticky", "name": "Schedule and targets note", "type": "n8n-nodes-base.stickyNote", "typeVersion": 1, "position": [ -300, -320 ] }, { "parameters": { "content": "### 2. Audit with state\nAdd an Apify credential. The Actor audits markup, stores fingerprints, and suppresses unchanged pages.", "height": 240, "width": 260, "color": 7 }, "id": "schema-monitor-sticky", "name": "Structured data audit note", "type": "n8n-nodes-base.stickyNote", "typeVersion": 1, "position": [ 280, -320 ] }, { "parameters": { "content": "### 3. Route regressions\nOnly negative schema changes remain. Connect issue tracking, alerting, or deployment-review steps here.", "height": 240, "width": 260, "color": 7 }, "id": "schema-route-sticky", "name": "Regression route note", "type": "n8n-nodes-base.stickyNote", "typeVersion": 1, "position": [ 580, -320 ] } ], "connections": { "Every day": { "main": [ [ { "node": "Set monitored pages", "type": "main", "index": 0 } ] ] }, "Set monitored pages": { "main": [ [ { "node": "Audit structured data", "type": "main", "index": 0 } ] ] }, "Audit structured data": { "main": [ [ { "node": "Keep schema regressions", "type": "main", "index": 0 } ] ] } }, "active": false, "settings": { "executionOrder": "v1" }, "versionId": "00000000-0000-4000-8000-000000000003", "meta": { "templateCredsSetupCompleted": false }, "tags": [] }