AutomationFlowsEmail & Gmail › Monitor Core Web Vitals at Scale with Pagespeed API and Google Sheets

Monitor Core Web Vitals at Scale with Pagespeed API and Google Sheets

ByLukasz Korba @lkorba on n8n.io

This workflow audits a list of URLs from a CSV upload or an XML sitemap using the Google PageSpeed Insights API, writes Lighthouse and Core Web Vitals metrics into a new Google Sheets report, and emails a completion summary via Gmail. Starts either on a schedule, via manual…

Event trigger★★★★★ complexity31 nodesHTTP RequestXMLForm TriggerGoogle SheetsGmail
Email & Gmail Trigger: Event Nodes: 31 Complexity: ★★★★★ Added:

This workflow corresponds to n8n.io template #15911 — we link there as the canonical source.

This workflow follows the Form Trigger → Gmail recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "id": "vQoMqhlLAuy1uCJp",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Monitor Core Web Vitals at scale with PageSpeed API & Google Sheets",
  "tags": [],
  "nodes": [
    {
      "id": "f3ded7a8-81ea-435b-88cb-57f8a70067da",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3104,
        -256
      ],
      "parameters": {
        "color": 7,
        "width": 848,
        "height": 304,
        "content": "## Fetch XML sitemap & extract URLs\nIf no file is uploaded, workflow will fetch the XML sitemap defined in the Config node and extract all URLs."
      },
      "typeVersion": 1
    },
    {
      "id": "7539fc6a-3bb0-4ab3-9444-25e787149809",
      "name": "Fetch the XML sitemap",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -3024,
        -144
      ],
      "parameters": {
        "url": "={{ $('Config').item.json.xml_sitemap_url }}",
        "options": {}
      },
      "typeVersion": 4.4
    },
    {
      "id": "c4a37165-700e-44d8-8573-8d094206f590",
      "name": "Convert to JSON",
      "type": "n8n-nodes-base.xml",
      "position": [
        -2832,
        -144
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 1
    },
    {
      "id": "54889c3c-42ce-4f6f-be39-a855d5d913f2",
      "name": "Extract URL tags",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        -2624,
        -144
      ],
      "parameters": {
        "options": {},
        "fieldToSplitOut": "urlset.url"
      },
      "typeVersion": 1
    },
    {
      "id": "0c795eda-84b7-4287-b6f3-ce28031a94cb",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -4256,
        -832
      ],
      "parameters": {
        "color": 7,
        "width": 800,
        "height": 704,
        "content": "## Workflow setup\n\n**Required:**\n1. Insert your PageSpeed API key into **api_key** field in the *Config* node\n2. Insert your XML sitemap address into **xml_sitemap_url** field *(required unless CSV file is used)*\n3. Create and select the default Google Sheet into which report is saved *(new sheets, columns and data will populate automatically on each run).*\n4. Add your email address inside the 'Send audit summary email' node to get the email notification \n\n\n**Optional customisation:**\n- Change the **lighthouse_device** to switch between mobile/desktop audit *(default: desktop)*\n- Change the **analytics_campaign_utm** to keep track of the audit data in your website analytics platform; can also be removed entirely *(default: PageSpeedAudit)*\n- Update the **cwv_pass_thresholds** object if you need looser/stricter pass thresholds *(current defaults were taken from official Lighthouse/CWV documentation which may change in the future)*\n\n\n[Official Lighthouse performance scoring documentation](https://developer.chrome.com/docs/lighthouse/performance/performance-scoring)\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "bbbaa679-56de-46ca-9a9d-dbeae17b1511",
      "name": "Loop Over Items",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        -1872,
        -544
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "84d3e39c-9586-41a1-904a-417b02ba48d4",
      "name": "Upload CSV",
      "type": "n8n-nodes-base.formTrigger",
      "position": [
        -4608,
        -128
      ],
      "parameters": {
        "options": {},
        "formTitle": "Upload CSV",
        "formFields": {
          "values": [
            {
              "fieldType": "file",
              "fieldLabel": "CSV file:",
              "requiredField": true,
              "acceptFileTypes": ".csv"
            }
          ]
        },
        "formDescription": "Upload a CSV file containing URLs to be audited in .CSV format (comma-delimited)."
      },
      "typeVersion": 2.5
    },
    {
      "id": "e6c0adeb-1899-47ba-beae-f346441d32f4",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2128,
        -768
      ],
      "parameters": {
        "color": 7,
        "width": 1060,
        "height": 896,
        "content": "## Main audit loop\nSplit in batches, test each URL, and save results to spreadsheet. Any errors will be caught and inserted into spreadsheet along with Lighthouse error message for debugging.\n\n**Optional customisation:**\n- Change the time interval in the Wait node if you want to limit performance impact of the audit on your site *(useful for large audits, default: 1 second)*"
      },
      "typeVersion": 1
    },
    {
      "id": "de694c52-2794-4aa2-b0fa-6f0050a3e2ee",
      "name": "When clicking \u2018Execute workflow\u2019",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        -4608,
        -368
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "d08aa690-091b-4603-b992-fadb086b1cae",
      "name": "Pick default spreadsheet to save the report",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -4016,
        -368
      ],
      "parameters": {
        "title": "=PageSpeed_audit_{{ $json.lighthouse_device }}_{{ $now.format('dd-MM-yyyy_HH:mm') }}",
        "options": {},
        "operation": "create",
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": ""
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "3d8bedbc-dc42-4064-9a70-1cab3cb97e5d",
      "name": "Add column headers to new sheet",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -3808,
        -368
      ],
      "parameters": {
        "url": "=https://sheets.googleapis.com/v4/spreadsheets/{{ $('Pick default spreadsheet to save the report').item.json.spreadsheetId }}/values/{{ $('Pick default spreadsheet to save the report').item.json.title }}!A1:AB?valueInputOption=RAW",
        "method": "PUT",
        "options": {},
        "jsonBody": "={\n\t\"range\": \"{{ $('Pick default spreadsheet to save the report').item.json.title }}!A1:AB\",\n\t\"majorDimension\": \"ROWS\",\n\t\"values\": [{{ $('Config').item.json.report_column_names.toJsonString() }}]\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "googleSheetsOAuth2Api"
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "02cf2511-e26c-49ef-86b3-3f6d731c4f83",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3104,
        -656
      ],
      "parameters": {
        "color": 7,
        "width": 464,
        "height": 320,
        "content": "## Extract URLs from the CSV file\nIf CSV file is uploaded manually, it will be processed instead of XML sitemap defined in the Config node."
      },
      "typeVersion": 1
    },
    {
      "id": "68688f68-2dc4-4975-a766-0858722beeaf",
      "name": "Config",
      "type": "n8n-nodes-base.set",
      "position": [
        -4208,
        -368
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "f3b9a727-cd59-4166-9dbf-4e4abb54ff0a",
              "name": "api_key",
              "type": "string",
              "value": "> Insert your API key here <"
            },
            {
              "id": "f7b57b1e-20c7-4c8d-b095-1f88d29d60d0",
              "name": "xml_sitemap_url",
              "type": "string",
              "value": "> Insert your XML sitemap URL here <"
            },
            {
              "id": "beb25ef0-f0e3-4d20-b8c8-14a8a0ae07af",
              "name": "lighthouse_category",
              "type": "string",
              "value": "performance"
            },
            {
              "id": "b024103b-8d3d-4366-8276-811c8dfb7f43",
              "name": "lighthouse_device",
              "type": "string",
              "value": "desktop"
            },
            {
              "id": "a27363c6-1904-4a25-9b61-0b8c6c3f762c",
              "name": "analytics_campaign_utm",
              "type": "string",
              "value": "PageSpeedAudit"
            },
            {
              "id": "52e003be-4759-410e-aab8-8f0ca1f7e937",
              "name": "cwv_pass_thresholds",
              "type": "object",
              "value": "{ \t\"LCP_pass_threshold\" : 2500,  \t\"INP_pass_threshold\" : 200,  \t\"CLS_pass_threshold\" : 0.1,  \t\"FCP_pass_threshold\" : 1800,  \t\"TTFB_pass_threshold\" : 800,  \t\"TBT_pass_threshhold\": 200,  \t\"SI_pass_threshold\" : 3400,  }"
            },
            {
              "id": "a7377308-0513-46cf-9af3-51a7c1034322",
              "name": "report_column_names",
              "type": "array",
              "value": "[\"url\", \"HTTP_status_code\", \"download_size_KiB\", \"DOM_size\", \"lh_performance_score\", \"lh_FCP_score\", \"lh_FCP_result\", \"lh_LCP_score\", \"lh_LCP_result\",  \"lh_TBT_score\", \"lh_TBT_result\", \"lh_CLS_score\", \"lh_CLS_result\", \"lh_SI_score\", \"lh_SI_result\", \"cwv_LCP_score\", \"cwv_LCP_result\", \"cwv_INP_score\", \"cwv_INP_result\", \"cwv_CLS_score\", \"cwv_CLS_result\", \"cwv_FCP_score\", \"cwv_FCP_result\", \"cwv_TTFB_score\", \"cwv_TTFB_result\", \"audit_timestamp\", \"warnings_errors\"]"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "75d158f4-7a8f-4f54-8e72-2b183f242c2f",
      "name": "Process CSV file if uploaded, otherwise fetch the XML sitemap",
      "type": "n8n-nodes-base.if",
      "position": [
        -3360,
        -368
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "7b7cb9f5-e69b-4185-9923-433aa9d26e87",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $('Upload CSV').isExecuted }}",
              "rightValue": ""
            }
          ]
        }
      },
      "executeOnce": true,
      "typeVersion": 2.3
    },
    {
      "id": "ff85a5dc-2c67-4b2c-9da1-07ba68923caf",
      "name": "Run PageSpeed audit",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "continueErrorOutput",
      "position": [
        -1680,
        -480
      ],
      "parameters": {
        "url": "https://www.googleapis.com/pagespeedonline/v5/runPagespeed",
        "options": {},
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "category",
              "value": "={{ $('Config').item.json.lighthouse_category }}"
            },
            {
              "name": "strategy",
              "value": "={{ $('Config').item.json.lighthouse_device }}"
            },
            {
              "name": "url",
              "value": "={{ $('Loop Over Items').item.json.urls}}"
            },
            {
              "name": "key",
              "value": "={{ $('Config').item.json.api_key }}"
            },
            {
              "name": "utm_campaign",
              "value": "={{ $('Config').item.json.analytics_campaign_utm }}"
            }
          ]
        }
      },
      "notesInFlow": false,
      "retryOnFail": false,
      "typeVersion": 4.4,
      "alwaysOutputData": false
    },
    {
      "id": "763262aa-153b-4191-9e73-f74acffb75c1",
      "name": "Extract URLs from file",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        -2816,
        -544
      ],
      "parameters": {
        "include": "=",
        "options": {
          "destinationFieldName": "urls"
        },
        "fieldToSplitOut": "row"
      },
      "typeVersion": 1
    },
    {
      "id": "84edb909-f156-44a9-bd6f-b74e200ccd40",
      "name": "Extract data from file",
      "type": "n8n-nodes-base.extractFromFile",
      "position": [
        -3024,
        -544
      ],
      "parameters": {
        "options": {
          "delimiter": ",",
          "headerRow": false,
          "readAsString": true
        },
        "binaryPropertyName": "={{ $('Upload CSV').item.binary.CSV_file_ }}"
      },
      "typeVersion": 1.1
    },
    {
      "id": "307c902f-c0af-4cfb-9933-5597e99041be",
      "name": "Create audit metadata",
      "type": "n8n-nodes-base.set",
      "position": [
        -3600,
        -368
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "8f8fb1da-a8a7-45c3-bbb1-a574bb0f12fb",
              "name": "audit_meta",
              "type": "object",
              "value": "={ \t\"device_tested\": \"{{ $('Config').item.json.lighthouse_device }}\",\t\"urls_processed\": 0, \t\"errors\": 0, \t\"audit_start_time\": \"{{ $now.toUTC().format('dd-MM-yyyy HH:mm') }}\", \t\"audit_finish_time\": \"\", \t\"final_audit_url\": \"https://docs.google.com/spreadsheets/d/{{ $('Pick default spreadsheet to save the report').item.json.spreadsheetId }}/?gid={{ $('Pick default spreadsheet to save the report').item.json.sheetId }}\", }"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "76256599-28e0-46c5-bba7-15855a04cd79",
      "name": "Increment error count meta",
      "type": "n8n-nodes-base.set",
      "position": [
        -1456,
        -352
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "517d61b3-0113-49ea-83f8-c17ce855bd5f",
              "name": "audit_meta.errors",
              "type": "number",
              "value": "={{ $('Create audit metadata').item.json.audit_meta.errors += 1 }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "1c6d32fb-afd6-4bd4-8d40-e51ed50b7a9d",
      "name": "Insert API audit data into sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -1632,
        -112
      ],
      "parameters": {
        "columns": {
          "value": {
            "url": "={{ $json.lighthouseResult.finalUrl }}",
            "DOM_size": "={{ $json.lighthouseResult.audits['dom-size-insight'].details.debugData.totalElements }}",
            "lh_SI_score": "={{ ($json.lighthouseResult.audits['speed-index'].numericValue / 1000).round(2)}}",
            "lh_CLS_score": "={{ $json.lighthouseResult.audits['cumulative-layout-shift'].numericValue.round(3) }}",
            "lh_FCP_score": "={{ ($json.lighthouseResult.audits['first-contentful-paint'].numericValue / 1000).round(2)}}",
            "lh_LCP_score": "={{ ($json.lighthouseResult.audits['largest-contentful-paint'].numericValue / 1000).round(2)}}",
            "lh_SI_result": "={{ $json.lighthouseResult.audits['speed-index'].numericValue <= $('Config').item.json.cwv_pass_thresholds.SI_pass_threshold ? \"Pass\" : \"Fail\"}}",
            "lh_TBT_score": "={{ $json.lighthouseResult.audits['total-blocking-time'].numericValue.round(0) }}",
            "cwv_CLS_score": "={{ $json.loadingExperience.metrics.CUMULATIVE_LAYOUT_SHIFT_SCORE.percentile }}",
            "cwv_FCP_score": "={{ $json.loadingExperience.metrics.FIRST_CONTENTFUL_PAINT_MS.percentile }}",
            "cwv_INP_score": "={{ $json.loadingExperience.metrics.INTERACTION_TO_NEXT_PAINT.percentile }}",
            "cwv_LCP_score": "={{ $json.loadingExperience.metrics.LARGEST_CONTENTFUL_PAINT_MS.percentile }}",
            "lh_CLS_result": "={{ $json.lighthouseResult.audits['cumulative-layout-shift'].numericValue <= $('Config').item.json.cwv_pass_thresholds.CLS_pass_threshold ? \"Pass\" : \"Fail\"}}",
            "lh_FCP_result": "={{ $json.lighthouseResult.audits['first-contentful-paint'].numericValue <= $('Config').item.json.cwv_pass_thresholds.FCP_pass_threshold ? \"Pass\" : \"Fail\" }}",
            "lh_LCP_result": "={{ $json.lighthouseResult.audits['largest-contentful-paint'].numericValue <= $('Config').item.json.cwv_pass_thresholds.LCP_pass_threshold ? \"Pass\" : \"Fail\"}}",
            "lh_TBT_result": "={{ $json.lighthouseResult.audits['total-blocking-time'].numericValue <= $('Config').item.json.cwv_pass_thresholds.TBT_pass_threshhold ? \"Pass\" : \"Fail\"}}",
            "cwv_CLS_result": "={{ $json.loadingExperience.metrics.CUMULATIVE_LAYOUT_SHIFT_SCORE.percentile <= $('Config').item.json.cwv_pass_thresholds.CLS_pass_threshold ? \"Pass\" : \"Fail\"}}",
            "cwv_FCP_result": "={{ $json.loadingExperience.metrics.FIRST_CONTENTFUL_PAINT_MS.percentile <= $('Config').item.json.cwv_pass_thresholds.FCP_pass_threshold ? \"Pass\" : \"Fail\"}}",
            "cwv_INP_result": "={{ $json.loadingExperience.metrics.INTERACTION_TO_NEXT_PAINT.percentile <= $('Config').item.json.cwv_pass_thresholds.INP_pass_threshold ? \"Pass\" : \"Fail\"}}",
            "cwv_LCP_result": "={{ $json.loadingExperience.metrics.LARGEST_CONTENTFUL_PAINT_MS.percentile <= $('Config').item.json.cwv_pass_thresholds.LCP_pass_threshold ? \"Pass\" : \"Fail\"}}",
            "cwv_TTFB_score": "={{ $json.loadingExperience.metrics.EXPERIMENTAL_TIME_TO_FIRST_BYTE.percentile }}",
            "audit_timestamp": "={{ $now.toUTC().format('dd-MM-yyyy HH:mm') }}",
            "cwv_TTFB_result": "={{ $json.loadingExperience.metrics.EXPERIMENTAL_TIME_TO_FIRST_BYTE.percentile <= $('Config').item.json.cwv_pass_thresholds.TTFB_pass_threshold ? \"Pass\" : \"Fail\"}}",
            "warnings_errors": "={{ $json.lighthouseResult.runWarnings.length > 0 ? $json.lighthouseResult.runWarnings : \"\" }}",
            "HTTP_status_code": "={{ $json.lighthouseResult.audits['network-requests'].details.items[0].statusCode }}",
            "download_size_KiB": "={{ ($json.lighthouseResult.audits['total-byte-weight'].numericValue / 1024).round(1)}}",
            "lh_performance_score": "={{ $json.lighthouseResult.categories.performance.score * 100}}"
          },
          "schema": [
            {
              "id": "url",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "url",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "HTTP_status_code",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "HTTP_status_code",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "download_size_KiB",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "download_size_KiB",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "DOM_size",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "DOM_size",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "lh_performance_score",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "lh_performance_score",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "lh_FCP_score",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "lh_FCP_score",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "lh_FCP_result",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "lh_FCP_result",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "lh_LCP_score",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "lh_LCP_score",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "lh_LCP_result",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "lh_LCP_result",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "lh_TBT_score",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "lh_TBT_score",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "lh_TBT_result",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "lh_TBT_result",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "lh_CLS_score",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "lh_CLS_score",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "lh_CLS_result",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "lh_CLS_result",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "lh_SI_score",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "lh_SI_score",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "lh_SI_result",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "lh_SI_result",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "cwv_LCP_score",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "cwv_LCP_score",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "cwv_LCP_result",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "cwv_LCP_result",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "cwv_INP_score",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "cwv_INP_score",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "cwv_INP_result",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "cwv_INP_result",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "cwv_CLS_score",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "cwv_CLS_score",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "cwv_CLS_result",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "cwv_CLS_result",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "cwv_FCP_score",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "cwv_FCP_score",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "cwv_FCP_result",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "cwv_FCP_result",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "cwv_TTFB_score",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "cwv_TTFB_score",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "cwv_TTFB_result",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "cwv_TTFB_result",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "audit_timestamp",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "audit_timestamp",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "warnings_errors",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "warnings_errors",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {
          "useAppend": true
        },
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Pick default spreadsheet to save the report').item.json.sheetId }}"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Pick default spreadsheet to save the report').item.json.spreadsheetId }}"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "e5e6eaed-5351-4eba-939f-3656d2878559",
      "name": "Wait between each batch",
      "type": "n8n-nodes-base.wait",
      "position": [
        -2032,
        -96
      ],
      "parameters": {
        "amount": 1
      },
      "typeVersion": 1.1
    },
    {
      "id": "9819d61c-ec29-4021-978d-d279fbe2b35a",
      "name": "Increment urls_processed meta",
      "type": "n8n-nodes-base.set",
      "position": [
        -1392,
        -112
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "c12d35fa-d746-4aa0-b9a8-224adf395488",
              "name": "urls_processed",
              "type": "number",
              "value": "={{ $('Create audit metadata').item.json.audit_meta.urls_processed += 1 }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "943ac022-083a-46fc-994d-b73c899ff98b",
      "name": "Update audit_finish_time meta",
      "type": "n8n-nodes-base.set",
      "position": [
        -1360,
        -560
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "78d94c01-c6ba-4d5b-8698-01c8dd653b93",
              "name": "audit_meta.audit_finish_time",
              "type": "string",
              "value": "={{ $now.toUTC().format('dd-MM-yyyy HH:mm') }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "cbaea255-e164-4f2a-aee5-6913fdbf9630",
      "name": "Insert error data into sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -1264,
        -352
      ],
      "parameters": {
        "columns": {
          "value": {
            "url": "={{ $('Run PageSpeed audit').item.json.urls }}",
            "audit_timestamp": "={{ $now.toUTC().format('dd-MM-yyyy HH:mm') }}",
            "warnings_errors": "={{ $('Run PageSpeed audit').item.json.error.message }}",
            "HTTP_status_code": "={{ $('Run PageSpeed audit').item.json.error.status }}"
          },
          "schema": [
            {
              "id": "url",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "url",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "HTTP_status_code",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "HTTP_status_code",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "download_size_KiB",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "download_size_KiB",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "DOM_size",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "DOM_size",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "lh_performance_score",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "lh_performance_score",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "lh_FCP_score",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "lh_FCP_score",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "lh_FCP_result",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "lh_FCP_result",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "lh_LCP_score",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "lh_LCP_score",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "lh_LCP_result",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "lh_LCP_result",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "lh_TBT_score",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "lh_TBT_score",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "lh_TBT_result",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "lh_TBT_result",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "lh_CLS_score",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "lh_CLS_score",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "lh_CLS_result",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "lh_CLS_result",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "lh_SI_score",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "lh_SI_score",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "lh_SI_result",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "lh_SI_result",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "cwv_LCP_score",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "cwv_LCP_score",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "cwv_LCP_result",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "cwv_LCP_result",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "cwv_INP_score",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "cwv_INP_score",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "cwv_INP_result",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "cwv_INP_result",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "cwv_CLS_score",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "cwv_CLS_score",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "cwv_CLS_result",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "cwv_CLS_result",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "cwv_FCP_score",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "cwv_FCP_score",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "cwv_FCP_result",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "cwv_FCP_result",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "cwv_TTFB_score",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "cwv_TTFB_score",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "cwv_TTFB_result",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "cwv_TTFB_result",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "audit_timestamp",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "audit_timestamp",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "warnings_errors",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "warnings_errors",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Pick default spreadsheet to save the report').item.json.sheetId }}"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Pick default spreadsheet to save the report').item.json.spreadsheetId }}"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "b4ca0c24-dac1-4a0e-b555-34a1de5066bd",
      "name": "Extract URLs",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        -2416,
        -144
      ],
      "parameters": {
        "options": {
          "destinationFieldName": "urls"
        },
        "fieldToSplitOut": "loc"
      },
      "typeVersion": 1
    },
    {
      "id": "d645e845-6bfc-4bdb-acb7-fc5d84199407",
      "name": "Schedule Audit",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -4608,
        -592
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "weeks",
              "triggerAtDay": [
                1
              ]
            }
          ]
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "650c9999-5813-4ba1-b07b-b2d7cdd0d85e",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -4720,
        -832
      ],
      "parameters": {
        "color": 7,
        "width": 384,
        "height": 864,
        "content": "## Ways to run this workflow:\n- On-demand via 'Upload CSV' node (URLs extracted from .csv file)\n- On a regular schedule via 'Schedule Audit' node (URLs fetched from XML sitemap)\n- Manually via 'Execute workflow' node (URLs fetched from XML sitemap)"
      },
      "typeVersion": 1
    },
    {
      "id": "467017ad-0252-422a-8697-d42bdc213e86",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -992,
        -768
      ],
      "parameters": {
        "color": 7,
        "width": 416,
        "height": 432,
        "content": "## Send audit notification email\nOnce the audit finishes, this node builds a simple HTML email and sends it to your pre-defined address. \n\nEmail includes basic audit metadata and a link to the spreadsheet with full report."
      },
      "typeVersion": 1
    },
    {
      "id": "62cbddc7-3681-4bf2-a56f-4327ae38f6c2",
      "name": "Send audit summary email",
      "type": "n8n-nodes-base.gmail",
      "position": [
        -832,
        -560
      ],
      "parameters": {
        "sendTo": "> Insert your email here <",
        "message": "=<h2>Your PageSpeed audit has finished</h2>\n\n<h3>Here are the details:</h3>\n<h4>Device tested:</h4>\n<p>{{ $('Create audit metadata').item.json.audit_meta.device_tested }}</p>\n<h4>URLs processed successfully:</h4>\n<p>{{ $('Create audit metadata').item.json.audit_meta.urls_processed }}</p>\n<h4>URLs with errors:</h4>\n<p>{{ $('Create audit metadata').item.json.audit_meta.errors }}</p>\n<h4>Start time (UTC):</h4>\n<p>{{ $('Create audit metadata').item.json.audit_meta.audit_start_time }}</p>\n<h4>Finish time (UTC):</h4>\n<p>{{ $json.audit_meta.audit_finish_time }}</p>\n<br>\n<p>Full audit has been <a href=\"{{ $('Create audit metadata').item.json.audit_meta.final_audit_url }}\">saved here.</a></p>",
        "options": {},
        "subject": "PageSpeed audit complete"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "executeOnce": true,
      "typeVersion": 2.2
    },
    {
      "id": "3ff98180-dd60-4f7d-970e-27a3d85a499a",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -5328,
        -1088
      ],
      "parameters": {
        "color": "#D3CD8D",
        "width": 528,
        "height": 1120,
        "content": "## What this workflow does\nThis workflow extracts URL addresses from either a CSV file or an XML sitemap, then uses PageSpeed API to get Lighthouse and Core Web Vitals metrics for each URL.\n\n\n### The final report contains the following columns (in order):\n\n- url\n- HTTP_status_code *(as returned by Lighthouse)*\n- download_size_KiB *(main document size in Kibibytes)*\n- DOM_size *(total number of DOM elements)*\n- lh_performance_score *(overall Lighthouse performance score,  0-100)*\n- lh_FCP_score *(notable CWV metric)*\n- lh_FCP_result *(Pass/Fail)*\n- lh_LCP_score *(main CWV metric)*\n- lh_LCP_result *(Pass/Fail)*\n- lh_TBT_score\n- lh_TBT_result *(Pass/Fail)*\n- lh_CLS_score *(main CWV metric)*\n- lh_CLS_result *(Pass/Fail)*\n- lh_SI_score\n- lh_SI_result *(Pass/Fail)*\n- cwv_LCP_score *(main CWV metric)*\n- cwv_LCP_result *(Pass/Fail)*\n- cwv_INP_score *(main CWV metric)*\n- cwv_INP_result *(Pass/Fail)*\n- cwv_CLS_score *(main CWV metric)*\n- cwv_CLS_result *(Pass/Fail)*\n- cwv_FCP_score *(notable CWV metric)*\n- cwv_FCP_result *(Pass/Fail)*\n- cwv_TTFB_score *(notable CWV metric)*\n- cwv_TTFB_result *(Pass/Fail)*\n- audit_timestamp *(in UTC timezone)*\n- warnings_errors *(warnings/error messages, if returned by Lighthouse)*\n\n\n**For brevity, the following acronyms are used in column names:**\n\n- **\"lh_\"** indicates Lighthouse lab data\n- **\"cwv_\"** indicates Core Web Vitals field data from CrUX (if available for a given URL)\n- **\"_score\"** indicates columns containing actual numeric values for each metric\n- **\"_result\"** indicates columns validating Pass/Fail results\n- **\"FCP\"** stands for First Contentful Paint\n- **\"LCP\"** stands for Largest Contentful Paint\n- **\"TBT\"** stands for Total Blocking Time\n- **\"CLS\"** stands for Cumulative Layout Shift\n- **\"SI\"** stands for Speed Index\n- **\"INP\"** stands for Interaction to Next Paint\n- **\"TTFB\"** stands for Time To First Byte\n"
      },
      "typeVersion": 1
    },
    {
      "id": "27877f97-f6d8-4c08-9a91-1c20ad37da9a",
      "name": "Sticky Note7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -4720,
        80
      ],
      "parameters": {
        "color": 5,
        "width": 384,
        "height": 208,
        "content": "## CSV file setup: \nThe CSV file should contain only a single column (no headers) with URLs separated by commas.\n\n**Format example:**\n\nhttps://firsturl.com,\nhttps://secondurl.com,\nhttps://thirdurl.com,\n..."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "executionOrder": "v1"
  },
  "versionId": "d0c5d687-b697-4925-a884-c8f3eb6e6679",
  "connections": {
    "Config": {
      "main": [
        [
          {
            "node": "Pick default spreadsheet to save the report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upload CSV": {
      "main": [
        [
          {
            "node": "Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract URLs": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Audit": {
      "main": [
        [
          {
            "node": "Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Convert to JSON": {
      "main": [
        [
          {
            "node": "Extract URL tags",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Over Items": {
      "main": [
        [
          {
            "node": "Update audit_finish_time meta",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Run PageSpeed audit",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract URL tags": {
      "main": [
        [
          {
            "node": "Extract URLs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Run PageSpeed audit": {
      "main": [
        [
          {
            "node": "Insert API audit data into sheet",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Increment error count meta",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create audit metadata": {
      "main": [
        [
          {
            "node": "Process CSV file if uploaded, otherwise fetch the XML sitemap",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch the XML sitemap": {
      "main": [
        [
          {
            "node": "Convert to JSON",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract URLs from file": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract data from file": {
      "main": [
        [
          {
            "node": "Extract URLs from file",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait between each batch": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Increment error count meta": {
      "main": [
        [
          {
            "node": "Insert error data into sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Insert error data into sheet": {
      "main": [
        [
          {
            "node": "Wait between each batch",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Increment urls_processed meta": {
      "main": [
        [
          {
            "node": "Wait between each batch",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update audit_finish_time meta": {
      "main": [
        [
          {
            "node": "Send audit summary email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Add column headers to new sheet": {
      "main": [
        [
          {
            "node": "Create audit metadata",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Insert API audit data into sheet": {
      "main": [
        [
          {
            "node": "Increment urls_processed meta",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When clicking \u2018Execute workflow\u2019": {
      "main": [
        [
          {
            "node": "Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Pick default spreadsheet to save the report": {
      "main": [
        [
          {
            "node": "Add column headers to new sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Process CSV file if uploaded, otherwise fetch the XML sitemap": {
      "main": [
        [
          {
            "node": "Extract data from file",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Fetch the XML sitemap",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

This workflow audits a list of URLs from a CSV upload or an XML sitemap using the Google PageSpeed Insights API, writes Lighthouse and Core Web Vitals metrics into a new Google Sheets report, and emails a completion summary via Gmail. Starts either on a schedule, via manual…

Source: https://n8n.io/workflows/15911/ — original creator credit. Request a take-down →

More Email & Gmail workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

Email & Gmail

Submit any YouTube, Vimeo, or Zoom webinar URL using a simple form and the workflow handles everything from there. It runs a two-phase pipeline: first identifying the top viral moments in your video w

Form Trigger, HTTP Request, Google Sheets +1
Email & Gmail

Stop chasing blurry receipts and manually typing expense data. This workflow creates an intelligent, "snap-and-submit" reimbursement pipeline that hosts photos via UploadToURL, extracts deep data via

Form Trigger, N8N Nodes Uploadtourl, HTTP Request +3
Email & Gmail

Atlas Opco Import. Uses github, stopAndError, formTrigger, googleSheets. Event-driven trigger; 22 nodes.

GitHub, Stop And Error, Form Trigger +4
Email & Gmail

Shopify and E-Commerce store owners often struggle to create engaging 3D videos from static product images. This workflow automates that entire process—from image upload to video delivery—so store own

Form Trigger, Google Drive, HTTP Request +2
Email & Gmail

Streamline Your Zoom Meetings With Secure Automated Stripe Payments. Uses zoom, httpRequest, gmail, googleSheets. Event-driven trigger; 20 nodes.

Zoom, HTTP Request, Gmail +3