N
n8n Store
Workflow Market
Weekly GSC Metric Send (Incl. Brand vs NonBrand) - Generic

Weekly GSC Metric Send (Incl. Brand vs NonBrand) - Generic

Description

Categories

📢 Marketing🤖 AI & Machine Learning

Nodes Used

n8n-nodes-base.ifn8n-nodes-base.coden8n-nodes-base.coden8n-nodes-base.coden8n-nodes-base.coden8n-nodes-base.coden8n-nodes-base.coden8n-nodes-base.coden8n-nodes-base.gmailn8n-nodes-base.merge
PriceGratis
Views0
Last Updated11/28/2025
workflow.json
{
  "id": "91yA1R0n3iR9BtYC",
  "meta": {
    "instanceId": "bd3424651820da96219b3bf8bf1cdfabcd2b0cc2dbde58159a106ccfa63cca09",
    "templateCredsSetupCompleted": true
  },
  "name": "Weekly GSC Metric Send (Incl. Brand vs NonBrand) - Generic",
  "tags": [
    {
      "id": "10",
      "name": "SEO",
      "createdAt": "2022-11-25T12:57:02.999Z",
      "updatedAt": "2022-11-25T12:57:02.999Z"
    },
    {
      "id": "dO9obbaAMg8UNnbo",
      "name": "Scheduled",
      "createdAt": "2025-07-10T15:12:41.643Z",
      "updatedAt": "2025-07-10T15:12:41.643Z"
    }
  ],
  "nodes": [
    {
      "id": "8056e0f1-b8d5-4c77-bf68-4c2972c9d562",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        1160,
        -3160
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "weeks",
              "triggerAtDay": [
                1
              ],
              "triggerAtHour": 15
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "cf42380c-42a6-4cb7-9fb3-52d58f305e12",
      "name": "Define Weeks",
      "type": "n8n-nodes-base.code",
      "position": [
        1320,
        -3160
      ],
      "parameters": {
        "jsCode": "function formatDate(date) {\n  return date.toISOString().split('T')[0];\n}\n\nconst today = new Date();\n\n// Prior week (15 to 9 days ago)\nconst priorStart = new Date(today);\npriorStart.setDate(today.getDate() - 15);\n\nconst priorEnd = new Date(today);\npriorEnd.setDate(today.getDate() - 9);\n\n// Last week (8 to 2 days ago)\nconst lastStart = new Date(today);\nlastStart.setDate(today.getDate() - 8);\n\nconst lastEnd = new Date(today);\nlastEnd.setDate(today.getDate() - 2);\n\nreturn [\n  {\n    json: {\n      label: \"2 Weeks Ago\",\n      startDate: formatDate(priorStart),\n      endDate: formatDate(priorEnd)\n    }\n  },\n  {\n    json: {\n      label: \"Last Week\",\n      startDate: formatDate(lastStart),\n      endDate: formatDate(lastEnd)\n    }\n  }\n];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "fe2cab2e-0d51-48cf-b073-a9d6aa9ddb99",
      "name": "Brand Filter",
      "type": "n8n-nodes-base.code",
      "position": [
        1800,
        -3060
      ],
      "parameters": {
        "jsCode": "// Add brand terms here\nconst brandTerms = ['BRAND TERMS'];\nlet brandClicks = 0;\nlet nonBrandClicks = 0;\n\nfor (const row of $json.rows || []) {\n  const query = row.keys?.[0]?.toLowerCase() || '';\n  const clicks = row.clicks || 0;\n\n  if (brandTerms.some(term => query.includes(term))) {\n    brandClicks += clicks;\n  } else {\n    nonBrandClicks += clicks;\n  }\n}\n\nreturn [\n  {\n    json: {\n      startDate: $input.first().json.startDate,\n      endDate: $input.first().json.endDate,\n      label: \"Last Week GSC\",\n      brandClicks,\n      nonBrandClicks\n    }\n  }\n];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "1340e164-7f1c-45fe-9534-7f60a2cfdc42",
      "name": "Brand Filter1",
      "type": "n8n-nodes-base.code",
      "position": [
        1800,
        -3400
      ],
      "parameters": {
        "jsCode": "// Add brand terms here\nconst brandTerms = ['BRAND TERMS'];\nlet brandClicks = 0;\nlet nonBrandClicks = 0;\n\nfor (const row of $json.rows || []) {\n  const query = row.keys?.[0]?.toLowerCase() || '';\n  const clicks = row.clicks || 0;\n\n  if (brandTerms.some(term => query.includes(term))) {\n    brandClicks += clicks;\n  } else {\n    nonBrandClicks += clicks;\n  }\n}\n\nreturn [\n  {\n    json: {\n      startDate: $input.first().json.startDate,\n      endDate: $input.first().json.endDate,\n      label: \"2 Weeks Ago GSC\",\n      brandClicks,\n      nonBrandClicks\n    }\n  }\n];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "75aabedf-8065-4b3f-b85e-9e0113fac9fd",
      "name": "Flatten8",
      "type": "n8n-nodes-base.code",
      "position": [
        1800,
        -2900
      ],
      "parameters": {
        "jsCode": "const { startDate, endDate } = $input.all()[0].json;\nconst row = $json.rows?.[0] || {};\n\nreturn [{\n  json: {\n    startDate,\n    endDate,\n    ...row\n  }\n}];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "99402142-24b2-4b24-b275-0427ee05f759",
      "name": "Flatten9",
      "type": "n8n-nodes-base.code",
      "position": [
        1800,
        -3240
      ],
      "parameters": {
        "jsCode": "const { startDate, endDate } = $input.all()[0].json;\nconst row = $json.rows?.[0] || {};\n\nreturn [{\n  json: {\n    startDate,\n    endDate,\n    ...row\n  }\n}];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "80e99961-68ed-435b-8e0b-fdc6ef7528cb",
      "name": "If4",
      "type": "n8n-nodes-base.if",
      "position": [
        1480,
        -3160
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "8ab18755-9c4f-40d8-a0c0-f16ab3b7d940",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.label }}",
              "rightValue": "2 Weeks Ago"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "ba691c97-8c1c-42bf-8eee-33f64455d343",
      "name": "Merge 2 Weeks Ago",
      "type": "n8n-nodes-base.merge",
      "position": [
        2000,
        -3320
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "combineBy": "combineByPosition"
      },
      "typeVersion": 3
    },
    {
      "id": "c48b74df-2d9f-4a2a-bb69-60401436b151",
      "name": "Merge Last Week",
      "type": "n8n-nodes-base.merge",
      "position": [
        2000,
        -2980
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "combineBy": "combineByPosition"
      },
      "typeVersion": 3
    },
    {
      "id": "a7035456-2779-4ee0-912f-fa2d17a712f9",
      "name": "Brand/NB Pull1",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1660,
        -3060
      ],
      "parameters": {
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"startDate\": \"{{$json.startDate}}\",\n  \"endDate\": \"{{$json.endDate}}\",\n  \"dimensions\": [\"query\"],\n  \"rowLimit\": 5000\n}\n",
        "sendBody": true,
        "specifyBody": "=json",
        "authentication": "predefinedCredentialType",
        "bodyParameters": {
          "parameters": [
            {}
          ]
        },
        "nodeCredentialType": "googleOAuth2Api"
      },
      "credentials": {
        "googleOAuth2Api": {
          "id": "vGLK8wkbx5dOMive",
          "name": "My GSC Account"
        }
      },
      "executeOnce": false,
      "typeVersion": 4.2,
      "alwaysOutputData": false
    },
    {
      "id": "0e12a39c-2919-4849-88e6-95f988ebb1d7",
      "name": "Brand/NB Pull",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1660,
        -3400
      ],
      "parameters": {
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"startDate\": \"{{$json.startDate}}\",\n  \"endDate\": \"{{$json.endDate}}\",\n  \"dimensions\": [\"query\"],\n  \"rowLimit\": 5000\n}\n",
        "sendBody": true,
        "specifyBody": "=json",
        "authentication": "predefinedCredentialType",
        "bodyParameters": {
          "parameters": [
            {}
          ]
        },
        "nodeCredentialType": "googleOAuth2Api"
      },
      "credentials": {
        "googleOAuth2Api": {
          "id": "vGLK8wkbx5dOMive",
          "name": "My GSC Account"
        }
      },
      "executeOnce": false,
      "typeVersion": 4.2,
      "alwaysOutputData": false
    },
    {
      "id": "f80fa85c-0cf7-420f-9a1b-18df52f08bd4",
      "name": "Total Metrics Pull1",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1660,
        -2900
      ],
      "parameters": {
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"startDate\": \"{{$json.startDate}}\",\n  \"endDate\": \"{{$json.endDate}}\",\n  \"rowLimit\": 5000\n}\n",
        "sendBody": true,
        "specifyBody": "=json",
        "authentication": "predefinedCredentialType",
        "bodyParameters": {
          "parameters": [
            {}
          ]
        },
        "nodeCredentialType": "googleOAuth2Api"
      },
      "credentials": {
        "googleOAuth2Api": {
          "id": "vGLK8wkbx5dOMive",
          "name": "My GSC Account"
        }
      },
      "executeOnce": false,
      "typeVersion": 4.2,
      "alwaysOutputData": false
    },
    {
      "id": "a3bf4392-665f-40b6-8cf2-5d78ad8b0639",
      "name": "Total Metrics Pull",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1660,
        -3240
      ],
      "parameters": {
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"startDate\": \"{{$json.startDate}}\",\n  \"endDate\": \"{{$json.endDate}}\",\n  \"rowLimit\": 5000\n}\n",
        "sendBody": true,
        "specifyBody": "=json",
        "authentication": "predefinedCredentialType",
        "bodyParameters": {
          "parameters": [
            {}
          ]
        },
        "nodeCredentialType": "googleOAuth2Api"
      },
      "credentials": {
        "googleOAuth2Api": {
          "id": "vGLK8wkbx5dOMive",
          "name": "My GSC Account"
        }
      },
      "executeOnce": false,
      "typeVersion": 4.2,
      "alwaysOutputData": false
    },
    {
      "id": "9edc39e5-a46a-49e5-b5fe-97aca12562cc",
      "name": "Add Brand Name",
      "type": "n8n-nodes-base.code",
      "position": [
        2380,
        -3160
      ],
      "parameters": {
        "jsCode": "return [\n  {\n    json: {\n      // Add brand name here\n      brand: \"BRAND NAME\",\n      data: items.map(item => item.json)\n    }\n  }\n];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "a45a4cb9-baab-4cae-a569-77f96d9a09c8",
      "name": "Merge Time Periods",
      "type": "n8n-nodes-base.merge",
      "position": [
        2220,
        -3160
      ],
      "parameters": {},
      "typeVersion": 3
    },
    {
      "id": "01b59e92-11ac-42a9-8184-23bbcf95c9aa",
      "name": "Generate Brand Performance Table",
      "type": "n8n-nodes-base.code",
      "position": [
        2540,
        -3160
      ],
      "parameters": {
        "jsCode": "const brands = $input.all();\n\nlet html = '';\n\nfunction formatNumber(value) {\n  return typeof value === 'number' ? value.toLocaleString() : value;\n}\n\nfunction formatPercentChange(current, previous, precision = 1, invert = false) {\n  if (previous === 0 || previous === undefined || current === undefined) return '';\n  const rawChange = ((current - previous) / previous) * 100;\n  const change = invert ? -rawChange : rawChange;\n  const rounded = change.toFixed(precision);\n  const color = change > 0 ? 'green' : change < 0 ? 'red' : '#333';\n  return `<span style=\"color: ${color}; font-weight: 500;\">${change > 0 ? '+' : ''}${rounded}%</span>`;\n}\n\nfunction formatCTR(value) {\n  return typeof value === 'number' ? `${(value * 100).toFixed(2)}%` : value;\n}\n\nfor (const item of brands) {\n  const brand = item.json.brand;\n  const rows = item.json.data;\n\n  if (!rows || rows.length < 2) continue;\n\n  // Map rows by label\n  const rowMap = {};\n  rows.forEach(r => {\n    if (r.label) rowMap[r.label] = r;\n  });\n\n  const current = rowMap[\"Last Week GSC\"];\n  const previous = rowMap[\"2 Weeks Ago GSC\"];\n\n  if (!current || !previous) continue;\n\n  const baseFields = [\"brandClicks\", \"nonBrandClicks\"];\n  const deltaFields = [\"clicks\", \"impressions\", \"ctr\", \"position\"];\n\n  const headers = [\"Label\"];\n  headers.push(...baseFields);\n  deltaFields.forEach(f => {\n    headers.push(`% Δ ${f}`);\n    headers.push(f);\n  });\n\n  html += `\n    <div style=\"margin-bottom: 40px; border-top: 3px solid #333; padding-top: 20px;\">\n      <table style=\"border-collapse: collapse; width: 100%; font-family: sans-serif; font-size: 14px; border: 1px solid #999;\">\n        <caption style=\"caption-side: top; font-weight: bold; font-size: 18px; margin-bottom: 12px; text-align: left;\">${brand}</caption>\n        <thead>\n          <tr>\n            ${headers.map(h => `<th style=\"border: 1px solid #999; padding: 8px 10px; background-color: #f2f2f2; text-align: left;\">${h}</th>`).join('')}\n          </tr>\n        </thead>\n        <tbody>\n  `;\n\n  [\"2 Weeks Ago GSC\", \"Last Week GSC\"].forEach(label => {\n    const row = rowMap[label];\n    html += `<tr><td style=\"border: 1px solid #999; padding: 8px 10px;\">${label}</td>`;\n\n    // Add base fields (no delta)\n    baseFields.forEach(field => {\n      html += `<td style=\"border: 1px solid #999; padding: 8px 10px;\">${formatNumber(row[field]) ?? ''}</td>`;\n    });\n\n    // Add delta fields\n    deltaFields.forEach(field => {\n      let displayVal = row[field];\n      if (field === \"ctr\") {\n        displayVal = formatCTR(displayVal);\n      } else {\n        displayVal = formatNumber(displayVal);\n      }\n\n      if (label === \"2 Weeks Ago GSC\") {\n        html += `<td style=\"border: 1px solid #999; padding: 8px 10px;\"></td>`;\n        html += `<td style=\"border: 1px solid #999; padding: 8px 10px;\">${displayVal ?? ''}</td>`;\n      } else {\n        const delta = formatPercentChange(current[field], previous[field], 1, field === 'position');\n        html += `<td style=\"border: 1px solid #999; padding: 8px 10px;\">${delta}</td>`;\n        html += `<td style=\"border: 1px solid #999; padding: 8px 10px;\">${displayVal ?? ''}</td>`;\n      }\n    });\n\n    html += `</tr>`;\n  });\n\n  html += `</tbody></table></div>`;\n}\n\nreturn [{ json: { htmlTable: html } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "308b3eaa-ddf8-4cdf-bb84-01f5be7567bf",
      "name": "Weekly GSC Metric Email",
      "type": "n8n-nodes-base.gmail",
      "position": [
        2700,
        -3160
      ],
      "webhookId": "56443199-fa21-46ae-ae67-352b6cb85e30",
      "parameters": {
        "message": "={{ $json[\"htmlTable\"] }}",
        "options": {},
        "subject": "Weekly GSC Metric Send"
      },
      "credentials": {
        "gmailOAuth2": {
          "id": "020fc4ae-9c23-4719-8484-ebde4daead63",
          "name": "Gmail Account 01 (matthew.fritschle)"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "53064791-2cfa-46c6-a5df-83209e9b436a",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2640,
        -3340
      ],
      "parameters": {
        "height": 140,
        "content": "## Connect to Gmail account or update to something else"
      },
      "typeVersion": 1
    },
    {
      "id": "73debc42-b892-4b85-b844-76978d21b2e0",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1580,
        -3540
      ],
      "parameters": {
        "width": 180,
        "height": 100,
        "content": "## Connect to GSC account"
      },
      "typeVersion": 1
    },
    {
      "id": "f9b96cd1-b4f2-4c24-8a09-45e499ec7f6b",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1800,
        -3560
      ],
      "parameters": {
        "width": 200,
        "height": 120,
        "content": "## Update with your own brand term(s)"
      },
      "typeVersion": 1
    },
    {
      "id": "732610cd-82ed-4027-8150-f0fdd9bc9248",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        360,
        -3400
      ],
      "parameters": {
        "width": 720,
        "height": 680,
        "content": "## This workflow generates a weekly performance summary from Google Search Console, focused on brand-level SEO metrics and week-over-week trends.\n\nIt provides a structured view of how each brand segment is performing, with clean formatting for quick insights.\n\nKey Features\n- Sends a weekly email with a table showing clicks, impressions, CTR, and position — along with % change vs. the previous week.\n- Highlights both brand and non-brand clicks separately.\n- Color-coded % changes make it easy to spot wins (green) and losses (red) at a glance.\n\nIt’s designed to give SEO teams a consistent overview of performance by brand, helping to track directional shifts and support deeper analysis when needed.\n\n### How it works\n1. Runs weekly (e.g. every Monday) to compare “Last Week” vs. “2 Weeks Ago” from GSC data.\n2. Includes both brand + non-brand click breakdown.\n3. Calculates raw values and week-over-week % change for clicks, impressions, CTR, and position.\n4. Outputs a clean, formatted table with labeled rows and color-coded changes.\n5. Sends the table as part of a scheduled email (can also be adapted for Slack or other channels).\n\n### Setup steps\n- Requires connected Google Search Console data (per brand segment).\n- Email delivery is included by default (customizable to other platforms).\n- Update brand segmentation logic to match your tracking needs (e.g. domain, label, or custom filters).\n- Typical setup time: ~5-10 minutes with structured input data.\n"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "487f2671-b2bb-4124-83da-4534e344328b",
  "connections": {
    "If4": {
      "main": [
        [
          {
            "node": "Brand/NB Pull",
            "type": "main",
            "index": 0
          },
          {
            "node": "Total Metrics Pull",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Brand/NB Pull1",
            "type": "main",
            "index": 0
          },
          {
            "node": "Total Metrics Pull1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Flatten8": {
      "main": [
        [
          {
            "node": "Merge Last Week",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Flatten9": {
      "main": [
        [
          {
            "node": "Merge 2 Weeks Ago",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Brand Filter": {
      "main": [
        [
          {
            "node": "Merge Last Week",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Define Weeks": {
      "main": [
        [
          {
            "node": "If4",
            "type": "main",
            "index": 0
          },
          {
            "node": "Merge 2 Weeks Ago2",
            "type": "main",
            "index": 0
          },
          {
            "node": "Merge 2 Weeks Ago3",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Brand Filter1": {
      "main": [
        [
          {
            "node": "Merge 2 Weeks Ago",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Brand/NB Pull": {
      "main": [
        [
          {
            "node": "Brand Filter1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Add Brand Name": {
      "main": [
        [
          {
            "node": "Generate Brand Performance Table",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Brand/NB Pull1": {
      "main": [
        [
          {
            "node": "Brand Filter",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge Last Week": {
      "main": [
        [
          {
            "node": "Merge Time Periods",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Define Weeks",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge 2 Weeks Ago": {
      "main": [
        [
          {
            "node": "Merge Time Periods",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge Time Periods": {
      "main": [
        [
          {
            "node": "Add Brand Name",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Total Metrics Pull": {
      "main": [
        [
          {
            "node": "Flatten9",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Total Metrics Pull1": {
      "main": [
        [
          {
            "node": "Flatten8",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Brand Performance Table": {
      "main": [
        [
          {
            "node": "Weekly GSC Metric Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

相关工作流