← All Analytics

Google Search Console email verification with BillionVerify

Google Search Console provides critical SEO performance data β€” crawl errors, index coverage, and search analytics. Agencies and SEO teams often combine this data with email reporting workflows. BillionVerify keeps those recipient lists clean, so automated Search Console digest emails and alerts reach real stakeholders without bouncing.

Why verify before the send

Automated SEO reports and crawl-error alerts are only valuable when they reach the right people. Sending to outdated, invalid, or role-based email addresses causes bounces that degrade your sending domain over time. BillionVerify validates every address in your reporting distribution list before your Search Console digests go out.

Ready-to-use n8n workflow

Import this workflow into n8n β€” it verifies every address with BillionVerify before Google Search Console sends, so only deliverable contacts are emailed. Install the BillionVerify community node first, then add your API key. Adapted from this n8n template

verify-emails-in-google-search-console.json
{
  "name": "Detect content decay from Google Search Console and alert via Slack and email + BillionVerify",
  "nodes": [
    {
      "id": "1fd97044-715a-4542-a99e-140e1d73ce73",
      "name": "Weekly Monday 8AM Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        320,
        320
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "weeks",
              "triggerAtDay": [
                1
              ],
              "triggerAtHour": 8
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "34069535-dd52-4536-bbc8-f9abc5d7b9cc",
      "name": "Calculate Date Ranges",
      "type": "n8n-nodes-base.code",
      "position": [
        528,
        320
      ],
      "parameters": {
        "jsCode": "// Calculate date ranges\nconst now = new Date();\n\n// Recent period: last 7 days\nconst recentEnd = new Date(now);\nrecentEnd.setDate(recentEnd.getDate() - 3); // GSC data has 3-day delay\nconst recentStart = new Date(recentEnd);\nrecentStart.setDate(recentStart.getDate() - 7);\n\n// Previous period: 28 days before that\nconst prevEnd = new Date(recentStart);\nprevEnd.setDate(prevEnd.getDate() - 1);\nconst prevStart = new Date(prevEnd);\nprevStart.setDate(prevStart.getDate() - 28);\n\nconst fmt = (d) => d.toISOString().split('T')[0];\n\nreturn [{\n  json: {\n    recent_start: fmt(recentStart),\n    recent_end: fmt(recentEnd),\n    previous_start: fmt(prevStart),\n    previous_end: fmt(prevEnd)\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "fdecf37e-0b0a-4542-9b18-6af9c37e894b",
      "name": "Fetch GSC Recent 7 Days",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        768,
        224
      ],
      "parameters": {
        "url": "https://www.googleapis.com/webmasters/v3/sites/{{ encodeURIComponent($env.GSC_SITE_URL) }}/searchAnalytics/query",
        "options": {
          "response": {
            "response": {
              "responseFormat": "json"
            }
          }
        },
        "jsonBody": "={\n  \"startDate\": \"{{ $json.recent_start }}\",\n  \"endDate\": \"{{ $json.recent_end }}\",\n  \"dimensions\": [\"page\"],\n  \"rowLimit\": 500,\n  \"startRow\": 0\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "googleSearchConsoleOAuth2Api"
      },
      "typeVersion": 4.2
    },
    {
      "id": "7a9e78b0-dd0c-4fec-b8f2-6fffd93e56de",
      "name": "Fetch GSC Previous 28 Days",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        768,
        416
      ],
      "parameters": {
        "url": "https://www.googleapis.com/webmasters/v3/sites/{{ encodeURIComponent($env.GSC_SITE_URL) }}/searchAnalytics/query",
        "options": {
          "response": {
            "response": {
              "responseFormat": "json"
            }
          }
        },
        "jsonBody": "={\n  \"startDate\": \"{{ $json.previous_start }}\",\n  \"endDate\": \"{{ $json.previous_end }}\",\n  \"dimensions\": [\"page\"],\n  \"rowLimit\": 500,\n  \"startRow\": 0\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "googleSearchConsoleOAuth2Api"
      },
      "typeVersion": 4.2
    },
    {
      "id": "81576c4a-ae86-4edf-b2d6-fe218c9023e8",
      "name": "Compare Periods and Detect Decay",
      "type": "n8n-nodes-base.code",
      "position": [
        1040,
        320
      ],
      "parameters": {
        "jsCode": "// Compare recent vs previous performance per page\nconst recentData = $('Fetch GSC Recent 7 Days').first().json;\nconst prevData = $('Fetch GSC Previous 28 Days').first().json;\n\nconst recentRows = recentData.rows || [];\nconst prevRows = prevData.rows || [];\n\n// Build lookup from previous period (normalize to weekly average)\nconst prevMap = {};\nfor (const row of prevRows) {\n  const page = row.keys[0];\n  prevMap[page] = {\n    clicks: row.clicks / 4,       // 28 days β†’ weekly avg\n    impressions: row.impressions / 4,\n    ctr: row.ctr,\n    position: row.position\n  };\n}\n\nconst results = [];\n\nfor (const row of recentRows) {\n  const page = row.keys[0];\n  const prev = prevMap[page];\n\n  if (!prev) continue; // New page, skip\n  if (prev.clicks < 2) continue; // Too little traffic to matter\n\n  const clickChange = row.clicks - prev.clicks;\n  const clickChangePct = prev.clicks > 0 ? parseFloat(((clickChange / prev.clicks) * 100).toFixed(1)) : 0;\n  const impressionChange = row.impressions - prev.impressions;\n  const impressionChangePct = prev.impressions > 0 ? parseFloat(((impressionChange / prev.impressions) * 100).toFixed(1)) : 0;\n  const positionChange = parseFloat((row.position - prev.position).toFixed(1)); // positive = worse\n\n  // Determine decay severity\n  let signal = 'STABLE';\n  let severity = 'ok';\n\n  if (clickChangePct <= -50 || (positionChange >= 5 && clickChangePct <= -30)) {\n    signal = 'CRITICAL_DECAY';\n    severity = 'critical';\n  } else if (clickChangePct <= -30 || positionChange >= 3) {\n    signal = 'DECAYING';\n    severity = 'warning';\n  } else if (clickChangePct <= -15 || positionChange >= 1.5) {\n    signal = 'EARLY_DECAY';\n    severity = 'watch';\n  } else if (clickChangePct >= 20) {\n    signal = 'GROWING';\n    severity = 'good';\n  }\n\n  // Clean page path\n  let pagePath = page;\n  try {\n    pagePath = new URL(page).pathname;\n  } catch(e) {}\n\n  results.push({\n    json: {\n      page_url: page,\n      page_path: pagePath,\n      \n      clicks_now: row.clicks,\n      clicks_before: parseFloat(prev.clicks.toFixed(1)),\n      click_change: clickChange,\n      click_change_pct: clickChangePct,\n      \n      impressions_now: row.impressions,\n      impressions_before: parseFloat(prev.impressions.toFixed(1)),\n      impression_change_pct: impressionChangePct,\n      \n      position_now: parseFloat(row.position.toFixed(1)),\n      position_before: parseFloat(prev.position.toFixed(1)),\n      position_change: positionChange,\n      \n      ctr_now: parseFloat((row.ctr * 100).toFixed(2)),\n      ctr_before: parseFloat((prev.ctr * 100).toFixed(2)),\n      \n      signal: signal,\n      severity: severity,\n      \n      date: new Date().toISOString().split('T')[0]\n    }\n  });\n}\n\n// Sort by worst decay first\nresults.sort((a, b) => a.json.click_change_pct - b.json.click_change_pct);\n\nreturn results;"
      },
      "typeVersion": 2
    },
    {
      "id": "7a51c471-9f95-470c-b668-b5ea0aa46648",
      "name": "Log All Results to Decay Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1280,
        416
      ],
      "parameters": {
        "columns": {
          "value": {
            "date": "={{ $json.date }}",
            "signal": "={{ $json.signal }}",
            "ctr_now": "={{ $json.ctr_now }}",
            "page_path": "={{ $json.page_path }}",
            "clicks_now": "={{ $json.clicks_now }}",
            "position_now": "={{ $json.position_now }}",
            "clicks_before": "={{ $json.clicks_before }}",
            "impressions_now": "={{ $json.impressions_now }}",
            "position_before": "={{ $json.position_before }}",
            "position_change": "={{ $json.position_change }}",
            "click_change_pct": "={{ $json.click_change_pct }}",
            "impression_change_pct": "={{ $json.impression_change_pct }}"
          },
          "mappingMode": "defineBelow"
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Decay Log"
        },
        "documentId": {
          "__rl": true,
          "mode": "url",
          "value": "={{ $env.DECAY_SHEET_URL }}"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "15898af0-2461-4f42-9804-6b02650ab69d",
      "name": "Filter Only Decaying Pages",
      "type": "n8n-nodes-base.filter",
      "position": [
        1280,
        224
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "or",
          "conditions": [
            {
              "id": "is-critical",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.severity }}",
              "rightValue": "critical"
            },
            {
              "id": "is-warning",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.severity }}",
              "rightValue": "warning"
            },
            {
              "id": "is-watch",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.severity }}",
              "rightValue": "watch"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "51aa6fc9-3b2f-46d1-832a-aba1ef343635",
      "name": "Build Weekly Decay Report",
      "type": "n8n-nodes-base.code",
      "position": [
        1520,
        224
      ],
      "parameters": {
        "jsCode": "// Build the weekly decay report\nconst allPages = $('Compare Periods and Detect Decay').all().map(i => i.json);\nconst decaying = $input.all().map(i => i.json);\n\nconst critical = decaying.filter(p => p.severity === 'critical');\nconst warning = decaying.filter(p => p.severity === 'warning');\nconst watch = decaying.filter(p => p.severity === 'watch');\nconst stable = allPages.filter(p => p.severity === 'ok');\nconst growing = allPages.filter(p => p.severity === 'good');\n\nconst today = new Date().toISOString().split('T')[0];\n\n// Total clicks lost\nconst totalClicksLost = decaying\n  .filter(p => p.click_change < 0)\n  .reduce((sum, p) => sum + Math.abs(p.click_change), 0);\n\nlet report = `*CONTENT DECAY REPORT β€” ${today}*\\n`;\nreport += `Pages analyzed: ${allPages.length}\\n\\n`;\n\nreport += `Critical decay: ${critical.length}\\n`;\nreport += `Decaying: ${warning.length}\\n`;\nreport += `Early signs: ${watch.length}\\n`;\nreport += `Stable: ${stable.length}\\n`;\nreport += `Growing: ${growing.length}\\n`;\nreport += `\\n*Est. weekly clicks lost: ${Math.round(totalClicksLost)}*\\n`;\n\nif (critical.length > 0) {\n  report += `\\n\\n*CRITICAL β€” Act now:*\\n`;\n  for (const p of critical.slice(0, 10)) {\n    report += `β€’ \\`${p.page_path}\\`\\n`;\n    report += `  Clicks: ${p.clicks_before} β†’ ${p.clicks_now} (*${p.click_change_pct}%*)\\n`;\n    report += `  Position: ${p.position_before} β†’ ${p.position_now} (${p.position_change > 0 ? '+' : ''}${p.position_change})\\n`;\n  }\n}\n\nif (warning.length > 0) {\n  report += `\\n*DECAYING β€” Update soon:*\\n`;\n  for (const p of warning.slice(0, 10)) {\n    report += `β€’ \\`${p.page_path}\\` β€” clicks ${p.click_change_pct}%, pos ${p.position_change > 0 ? '+' : ''}${p.position_change}\\n`;\n  }\n}\n\nif (watch.length > 0) {\n  report += `\\n*EARLY SIGNS β€” Keep watching:*\\n`;\n  for (const p of watch.slice(0, 5)) {\n    report += `β€’ \\`${p.page_path}\\` β€” clicks ${p.click_change_pct}%\\n`;\n  }\n}\n\nif (growing.length > 0) {\n  report += `\\n*TOP GROWING:*\\n`;\n  const topGrow = growing.sort((a, b) => b.click_change_pct - a.click_change_pct).slice(0, 3);\n  for (const p of topGrow) {\n    report += `β€’ \\`${p.page_path}\\` β€” clicks +${p.click_change_pct}%\\n`;\n  }\n}\n\nreturn [{\n  json: {\n    report: report,\n    total_pages: allPages.length,\n    critical_count: critical.length,\n    warning_count: warning.length,\n    watch_count: watch.length,\n    clicks_lost: Math.round(totalClicksLost),\n    has_critical: critical.length > 0,\n    critical_pages: critical.slice(0, 10),\n    warning_pages: warning.slice(0, 10)\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "48e028a0-fce2-4585-a9b4-ddef05f29639",
      "name": "Post Report to Slack",
      "type": "n8n-nodes-base.slack",
      "position": [
        1760,
        112
      ],
      "webhookId": "3d3e49f9-3bf4-463d-9e58-456befa19c00",
      "parameters": {
        "text": "={{ $json.report }}",
        "otherOptions": {}
      },
      "typeVersion": 2.2
    },
    {
      "id": "aff9be9e-2e4f-4df1-ad5f-e32c1669476f",
      "name": "Email Weekly Report",
      "type": "n8n-nodes-base.emailSend",
      "position": [
        1760,
        320
      ],
      "webhookId": "8628387b-cbc1-4f0d-99e4-994e14ecfa8b",
      "parameters": {
        "options": {},
        "subject": "=Content Decay Report β€” {{ $json.critical_count }} critical, {{ $json.warning_count }} decaying | {{ $json.clicks_lost }} clicks lost"
      },
      "typeVersion": 2.1
    },
    {
      "id": "4cf96f14-e1e9-4164-aee7-1002121f0d90",
      "name": "Check If Any Critical Pages",
      "type": "n8n-nodes-base.filter",
      "position": [
        1760,
        512
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "has-critical",
              "operator": {
                "type": "boolean",
                "operation": "true"
              },
              "leftValue": "={{ $json.has_critical }}",
              "rightValue": "={{true}}"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "52ef07b8-caa3-43a0-9898-efc5ff320bc1",
      "name": "Generate Fix Tasks for Critical Pages",
      "type": "n8n-nodes-base.code",
      "position": [
        1968,
        512
      ],
      "parameters": {
        "jsCode": "// Create individual tasks for critical decaying pages\nconst data = $input.first().json;\nconst tasks = [];\n\nfor (const page of data.critical_pages) {\n  let action = '';\n  \n  if (page.position_change >= 5) {\n    action = 'Major ranking drop. Check: lost backlinks, technical errors (404/redirect), or competitor new content. Run a backlink audit.';\n  } else if (page.impression_change_pct <= -40) {\n    action = 'Impressions tanking. Possible: Google algorithm change, keyword cannibalization, or index issues. Check Coverage report in GSC.';\n  } else if (page.ctr_now < page.ctr_before * 0.7) {\n    action = 'CTR dropped significantly. Update title tag and meta description. Check if SERP features (featured snippets, ads) pushed you down.';\n  } else {\n    action = 'Content likely outdated. Refresh with updated stats, new sections, better internal links. Check if search intent has shifted.';\n  }\n\n  tasks.push({\n    json: {\n      page_path: page.page_path,\n      page_url: page.page_url,\n      signal: page.signal,\n      click_change_pct: page.click_change_pct,\n      position_change: page.position_change,\n      recommended_action: action,\n      priority: 'HIGH',\n      created: new Date().toISOString().split('T')[0]\n    }\n  });\n}\n\nreturn tasks;"
      },
      "typeVersion": 2
    },
    {
      "id": "76f2b5b6-32c3-4261-8f6b-9183c61e38e1",
      "name": "Log Fix Tasks to Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2192,
        512
      ],
      "parameters": {
        "columns": {
          "value": {
            "signal": "={{ $json.signal }}",
            "created": "={{ $json.created }}",
            "page_url": "={{ $json.page_url }}",
            "priority": "={{ $json.priority }}",
            "page_path": "={{ $json.page_path }}",
            "position_change": "={{ $json.position_change }}",
            "click_change_pct": "={{ $json.click_change_pct }}",
            "recommended_action": "={{ $json.recommended_action }}"
          },
          "mappingMode": "defineBelow"
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Fix Tasks"
        },
        "documentId": {
          "__rl": true,
          "mode": "url",
          "value": "={{ $env.DECAY_SHEET_URL }}"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "dd580d9b-91f2-4e75-b5d3-5467860eb2e7",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -704,
        -224
      ],
      "parameters": {
        "color": "#8D9C44",
        "width": 660,
        "height": 976,
        "content": "## Detect content decay and alert via Slack and email\n\nThis workflow automatically detects declining content performance by comparing your recent Google Search Console data against a historical baseline, then alerts you with actionable fix recommendations.\n\n## How it works\n1. **Runs every Monday at 8 AM** on a weekly schedule\n2. **Pulls last 7 days** of page-level data from Google Search Console (recent performance)\n3. **Pulls previous 28 days** as a baseline (normalized to weekly averages)\n4. **Compares per-page metrics** β€” clicks, impressions, position, and CTR\n5. **Classifies each page** into decay severity levels:\n   - **CRITICAL_DECAY** β€” clicks dropped 50%+ or position fell 5+ spots\n   - **DECAYING** β€” clicks dropped 30%+ or position fell 3+ spots\n   - **EARLY_DECAY** β€” clicks dropped 15%+ or position fell 1.5+ spots\n   - **STABLE** β€” no significant change\n   - **GROWING** β€” clicks up 20%+\n6. **Logs all results** to a Google Sheet for historical trend tracking\n7. **Sends a weekly report** via Slack and email with full breakdown\n8. **Auto-generates fix tasks** for critical pages with specific recommended actions\n\n## Setup steps\n1. Set **n8n environment variable** `GSC_SITE_URL` to your site (e.g., `https://yoursite.com`)\n2. Set **n8n environment variable** `DECAY_SHEET_URL` to your Google Sheet URL\n3. Create a Google Sheet with two tabs:\n   - `Decay Log` β€” headers: `date`, `page_path`, `signal`, `clicks_now`, `clicks_before`, `click_change_pct`, `position_now`, `position_before`, `position_change`, `impressions_now`, `impression_change_pct`, `ctr_now`\n   - `Fix Tasks` β€” headers: `created`, `priority`, `page_path`, `page_url`, `signal`, `click_change_pct`, `position_change`, `recommended_action`\n4. Connect **Google Search Console OAuth2** credentials\n5. Connect **Google Sheets OAuth2** credentials\n6. Connect **Slack OAuth2** credentials and set target channel\n7. Configure **SMTP / email** credentials and update recipient address\n\n## Customization\n- Adjust decay thresholds in the \"Compare Periods and Detect Decay\" node\n- Change schedule frequency (daily, bi-weekly, monthly)\n- Add more notification channels (Telegram, Discord, etc.)\n- Extend fix task logic with AI-powered content refresh suggestions"
      },
      "typeVersion": 1
    },
    {
      "id": "4ef309df-5f0e-44da-9136-6275543ae50e",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        432,
        48
      ],
      "parameters": {
        "color": "#413A3A",
        "width": 328,
        "height": 164,
        "content": "## 1. Fetch GSC Data\nCalculates date ranges (recent 7 days vs. previous 28-day baseline) and fetches page-level performance from Google Search Console in parallel."
      },
      "typeVersion": 1
    },
    {
      "id": "6881902b-441f-45b6-84d2-030540de839f",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1072,
        0
      ],
      "parameters": {
        "color": "#413A3A",
        "width": 332,
        "height": 164,
        "content": "## 2. Detect Decay\nCompares recent vs. baseline metrics per page. Classifies each page as CRITICAL_DECAY, DECAYING, EARLY_DECAY, STABLE, or GROWING based on click and position changes."
      },
      "typeVersion": 1
    },
    {
      "id": "89fa3787-df97-47dd-8dda-505d1c1d1416",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1680,
        -96
      ],
      "parameters": {
        "color": "#413A3A",
        "width": 276,
        "height": 180,
        "content": "## 3. Report and Alert\nBuilds a comprehensive weekly report with decay breakdown. Sends to Slack and email simultaneously. Pages with no decay are logged but not alerted."
      },
      "typeVersion": 1
    },
    {
      "id": "02088b0f-83f2-4b51-afa7-ac7345e04529",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1744,
        752
      ],
      "parameters": {
        "color": "#413A3A",
        "width": 332,
        "height": 180,
        "content": "## 4. Auto-Generate Fix Tasks\nFor critical pages, generates specific remediation tasks (backlink audit, content refresh, CTR optimization) and logs them to a separate Google Sheet tab."
      },
      "typeVersion": 1
    },
    {
      "id": "d1e6cb92-3abe-4f97-a433-4c80070e718b",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        208,
        512
      ],
      "parameters": {
        "color": 3,
        "width": 480,
        "height": 140,
        "content": "⚠️ **Set environment variables** before activating:\n- `GSC_SITE_URL` β†’ your verified site URL (e.g., `https://yoursite.com`)\n- `DECAY_SHEET_URL` β†’ your Google Sheet URL for logging\n\nSee [n8n docs on environment variables](https://docs.n8n.io/hosting/configuration/environment-variables/)."
      },
      "typeVersion": 1
    },
    {
      "id": "8ab794a9-6860-45d1-80ff-bf6fba6ee2d0",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1984,
        304
      ],
      "parameters": {
        "color": 3,
        "width": 360,
        "height": 80,
        "content": "⚠️ **Update the email address** in this node to your own recipient address before activating."
      },
      "typeVersion": 1
    },
    {
      "parameters": {
        "operation": "verify",
        "email": "={{ $json.email || $json.Email }}",
        "additionalOptions": {}
      },
      "type": "n8n-nodes-billionverify.billionVerify",
      "typeVersion": 1,
      "position": [
        1400,
        320
      ],
      "name": "Verify Email (BillionVerify)",
      "credentials": {
        "billionVerifyApi": {
          "id": "",
          "name": "BillionVerify account"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "is-deliverable",
              "leftValue": "={{ $json.is_deliverable }}",
              "rightValue": "",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ]
        }
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        1580,
        320
      ],
      "name": "IF deliverable"
    }
  ],
  "connections": {
    "Calculate Date Ranges": {
      "main": [
        [
          {
            "node": "Fetch GSC Recent 7 Days",
            "type": "main",
            "index": 0
          },
          {
            "node": "Fetch GSC Previous 28 Days",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch GSC Recent 7 Days": {
      "main": [
        [
          {
            "node": "Compare Periods and Detect Decay",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Weekly Decay Report": {
      "main": [
        [
          {
            "node": "Check If Any Critical Pages",
            "type": "main",
            "index": 0
          },
          {
            "node": "Post Report to Slack",
            "type": "main",
            "index": 0
          },
          {
            "node": "Verify Email (BillionVerify)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Weekly Monday 8AM Trigger": {
      "main": [
        [
          {
            "node": "Calculate Date Ranges",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch GSC Previous 28 Days": {
      "main": [
        [
          {
            "node": "Compare Periods and Detect Decay",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter Only Decaying Pages": {
      "main": [
        [
          {
            "node": "Build Weekly Decay Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check If Any Critical Pages": {
      "main": [
        [
          {
            "node": "Generate Fix Tasks for Critical Pages",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Compare Periods and Detect Decay": {
      "main": [
        [
          {
            "node": "Filter Only Decaying Pages",
            "type": "main",
            "index": 0
          },
          {
            "node": "Log All Results to Decay Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Fix Tasks for Critical Pages": {
      "main": [
        [
          {
            "node": "Log Fix Tasks to Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Verify Email (BillionVerify)": {
      "main": [
        [
          {
            "node": "IF deliverable",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "IF deliverable": {
      "main": [
        [
          {
            "node": "Email Weekly Report",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    }
  },
  "settings": {
    "binaryMode": "separate",
    "availableInMCP": false,
    "executionOrder": "v1"
  }
}

Workflow templates with Google Search Console

Ready-to-use workflows that verify emails before Google Search Console sends.

How it works

  1. 1

    A stakeholder or client subscribes to receive automated Google Search Console reports or alerts.

  2. 2

    Their email address is passed to BillionVerify via the n8n community node, Integrately, or the REST API.

  3. 3

    BillionVerify checks syntax, domain MX records, SMTP deliverability, disposable domain lists, and catch-all behavior.

  4. 4

    Valid addresses are added to the reporting distribution; invalid or disposable ones are rejected with a reason code.

  5. 5

    Your Search Console alert and digest emails go out with a clean list, maintaining high deliverability.

When to use this

SEO report distribution lists

Agencies managing multiple clients maintain distribution lists for automated Search Console performance reports. Verify every client contact address with BillionVerify to eliminate bounces and ensure critical SEO data reaches decision-makers reliably.

Alert subscriptions for crawl issues

When your n8n workflow sends crawl error or index drop alerts, bad addresses in the subscriber list produce bounces. Validate subscribers at sign-up time so your alerting pipeline remains healthy and your sender reputation stays intact.

FAQ

Can BillionVerify handle bulk verification for existing subscriber lists?

Yes. Upload your existing list in bulk to BillionVerify to scrub invalid, risky, and disposable addresses before your next reporting cycle. Results are returned with per-address status codes.

What is a role address and why should I filter it?

Role addresses like info@, support@, or webmaster@ route to shared inboxes that may belong to multiple people or automated systems. They often have low engagement and can generate complaints, so filtering them protects your reputation.

Is there a no-code way to add email verification to my Search Console workflow?

Yes. Integrately connects BillionVerify to hundreds of tools with a 1-click setup, requiring no code. You can also use the n8n community node if your reporting automation already runs on n8n.

How often should I re-verify my distribution list?

Email addresses change over time β€” people leave companies, domains expire. Re-verifying your list every 60 to 90 days is a good practice to keep bounce rates below the industry threshold of 2 percent.

Verify emails in Google Search Console

Create a free account, grab your API key, and stop bounces before they happen.

Get started free