AutomationFlowsEmail & Gmail › url-uptime-monitor

url-uptime-monitor

url-uptime-monitor. Uses scheduleTrigger, splitOut, googleSheets, summarize. Scheduled trigger; 18 nodes.

Cron / scheduled trigger★★★★☆ complexity18 nodesGoogle SheetsGmailHttp RequestTelegram
Email & Gmail Trigger: Cron / scheduled Nodes: 18 Complexity: ★★★★☆

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

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
{
  "name": "url-uptime-monitor",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "minutes",
              "minutesInterval": 1
            },
            {}
          ]
        }
      },
      "id": "1c49ff18-191b-4795-bdbe-2c80be834427",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        416,
        560
      ],
      "typeVersion": 1.2
    },
    {
      "parameters": {
        "fieldToSplitOut": "urls",
        "options": {}
      },
      "id": "6f9a98c2-130d-49f6-9497-8e792f43263b",
      "name": "Split Out",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        896,
        464
      ],
      "typeVersion": 1
    },
    {
      "parameters": {
        "mode": "raw",
        "jsonOutput": "{\n  \"urls\": {\n    \"URL 1\": \"https://cuongit.net\",\n    \"URL 2\": \"https://tech36.net\",\n    \"URL 3\": \"https://test.cuongit.net\", \n    \"URL 4\": \"https://test1.cuongit.net\"\n  }\n}",
        "options": {}
      },
      "id": "3cf83123-9829-4dba-96fc-820b72f91970",
      "name": "URLs",
      "type": "n8n-nodes-base.set",
      "position": [
        672,
        464
      ],
      "typeVersion": 3.4
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "",
          "mode": "url"
        },
        "sheetName": {
          "__rl": true,
          "value": "gid=0",
          "mode": "list",
          "cachedResultName": "Monitorweb",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1audghXxGikfYkIMzGo-RANzCZCS2PablpGbr9uVQNBw/edit#gid=0"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "done": "=Done: {{ (Array.isArray($('Loop URLs').item.json.urls) ? $('Loop URLs').item.json.urls.join(', ') : $('Loop URLs').item.json.urls).toString().replace(/\\r?\\n/g, ' ') }}",
            "time": "={{ new Date().toLocaleString('sv-SE', { timeZone: 'Asia/Ho_Chi_Minh' }) }}"
          },
          "matchingColumns": [
            "time"
          ],
          "schema": [
            {
              "id": "time",
              "displayName": "time",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "error",
              "displayName": "error",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "done",
              "displayName": "done",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "id": "08cace4f-d4b9-4abe-b035-bc59ec260473",
      "name": "Success",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1664,
        624
      ],
      "typeVersion": 4.6,
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "",
          "mode": "url"
        },
        "sheetName": {
          "__rl": true,
          "value": "gid=0",
          "mode": "list",
          "cachedResultName": "Monitorweb",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1audghXxGikfYkIMzGo-RANzCZCS2PablpGbr9uVQNBw/edit#gid=0"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "error": "=Error: {{ $json.urls }}",
            "time": "={{ new Date().toLocaleString('vi-VN', { timeZone: 'Asia/Ho_Chi_Minh', hour12: false }) }}"
          },
          "matchingColumns": [],
          "schema": [
            {
              "id": "time",
              "displayName": "time",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "error",
              "displayName": "error",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "done",
              "displayName": "done",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "id": "6f910bbf-70bf-4002-8ade-fc28ed3ab50e",
      "name": "Error",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1664,
        848
      ],
      "typeVersion": 4.6,
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "fieldsToSummarize": {
          "values": [
            {
              "field": "done"
            },
            {
              "field": "error"
            }
          ]
        },
        "options": {}
      },
      "id": "12651162-043c-4e0a-8ef3-7bc8b7fdc04e",
      "name": "Total",
      "type": "n8n-nodes-base.summarize",
      "position": [
        1312,
        384
      ],
      "typeVersion": 1.1
    },
    {
      "parameters": {},
      "id": "3ae075a8-d4b5-4e38-a8cd-37cb1b3b2a3a",
      "name": "Run trigger",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        416,
        368
      ],
      "typeVersion": 1
    },
    {
      "parameters": {
        "subject": "Webs Down",
        "emailType": "text",
        "message": "=We inform you that the following URLs are currently down:\n{{ ($('Aggregate Errors').first().json.urls || []).join('\\n') }}\n\nError: {{ $('Total').first().json.count_error }}\nTime: {{ new Date().toLocaleString('vi-VN', { timeZone: 'Asia/Ho_Chi_Minh', hour12: false }) }}\n",
        "options": {}
      },
      "id": "3e06e308-db71-4b93-b36b-c4b4e25655f3",
      "name": "Send a message",
      "type": "n8n-nodes-base.gmail",
      "position": [
        2080,
        512
      ],
      "typeVersion": 2.1,
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// 1) Fetch ALL items emitted by the \"Loop URLs\" node\nconst items = $items('Loop URLs'); // the node name must match exactly\nconsole.log(items);\n\n// 2) Extract the \"error\" property and drop empty/whitespace-only values\nconst errorUrls = items\n  .map(it => it.json.error)\n  .filter(url => typeof url === 'string' && url.trim().length);\n\nif (errorUrls.length === 0) {\n  throw new Error('No URLs were found in the \"error\" property.');\n}\n\n// Return one item per failed URL: { json: { url: \"<the url>\" } }\nreturn errorUrls.map(url => ({ json: { url } }));\n"
      },
      "id": "42d267ed-5d71-496c-a72e-d0727c56f86f",
      "name": "Code",
      "type": "n8n-nodes-base.code",
      "position": [
        1520,
        384
      ],
      "typeVersion": 2
    },
    {
      "parameters": {
        "fieldToSplitOut": "url",
        "options": {}
      },
      "id": "8032daea-87bf-4c60-954a-53617827b36d",
      "name": "Split Out2",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        1696,
        384
      ],
      "typeVersion": 1
    },
    {
      "parameters": {
        "url": "={{ $json.urls }}",
        "options": {}
      },
      "id": "fe2cdc77-ec82-4a70-88ae-881974d18533",
      "name": "Request",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1296,
        592
      ],
      "typeVersion": 4.2,
      "onError": "continueErrorOutput"
    },
    {
      "parameters": {
        "content": "## How it works (P1)\nBefore the loop, you enter the URLs to scan in the \"URLs\" stream, then start the trigger either manually or scheduled.",
        "width": 260,
        "color": 5
      },
      "id": "055e1a64-3c30-45e0-97a8-eb3aa7e9fcfa",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        640,
        688
      ],
      "typeVersion": 1
    },
    {
      "parameters": {
        "content": "## How it works (P2)\nStart a loop for each URL entered, adding the status to a Google Sheet, then collect the status of each URL, filter out the crashes, and send an email, telegram with the crashes.",
        "height": 180,
        "width": 260
      },
      "id": "e3d65562-83a8-4b48-933f-f01215d893ba",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1920,
        816
      ],
      "typeVersion": 1
    },
    {
      "parameters": {
        "content": "### Enter URLs to scan here",
        "height": 220,
        "width": 180,
        "color": 5
      },
      "id": "f1fd407b-548b-4f53-8781-43d9bd8e45ad",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        640,
        400
      ],
      "typeVersion": 1
    },
    {
      "parameters": {
        "content": "### As a trigger you could also implement webhook or MCP",
        "height": 480,
        "width": 180,
        "color": 5
      },
      "id": "5914dd63-1292-4ef4-b15f-d57d6dc2d8bc",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        368,
        240
      ],
      "typeVersion": 1
    },
    {
      "parameters": {
        "options": {}
      },
      "id": "0afdf309-8ad9-4e41-b323-9970fb0bf631",
      "name": "Loop URLs",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        1088,
        464
      ],
      "typeVersion": 3
    },
    {
      "parameters": {
        "text": "=We inform you that the following URLs are currently down:  {{$items(\"Split Out2\").map(it => it.json.url.replace(/^Error:\\s*/, '')).join('\\n')}}\n\nError: {{ $('Total').first().json.count_error }} \nTime: {{ new Date().toLocaleString('vi-VN', { timeZone: 'Asia/Ho_Chi_Minh', hour12: false }) }}",
        "additionalFields": {}
      },
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        2064,
        208
      ],
      "id": "fc9cb246-8ec2-474a-b3e0-2304f6d957b3",
      "name": "Send a text message",
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// Aggregate Errors \u2014 preserve pairing so downstream nodes don't complain\nconst input = $input.all();\n\nconst urls = input\n  .map(it => String(it.json?.url || '').replace(/^(Error|Fail):\\s*/, ''))\n  .filter(Boolean);\n\n// If nothing came in, still return one item to avoid \"no items\" issues\nreturn [{\n  json: {\n    urls,\n    count_error: urls.length\n  },\n  // Preserve pairing information for *all* input items\n  pairedItem: input.map((_, idx) => ({ item: idx }))\n}];\n"
      },
      "id": "912a2064-16cc-4900-a023-0d7c840d76f4",
      "name": "Aggregate Errors",
      "type": "n8n-nodes-base.code",
      "position": [
        1872,
        384
      ],
      "typeVersion": 2
    }
  ],
  "connections": {
    "Code": {
      "main": [
        [
          {
            "node": "Split Out2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "URLs": {
      "main": [
        [
          {
            "node": "Split Out",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Error": {
      "main": [
        [
          {
            "node": "Loop URLs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Total": {
      "main": [
        [
          {
            "node": "Code",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Request": {
      "main": [
        [
          {
            "node": "Success",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Error",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Success": {
      "main": [
        [
          {
            "node": "Loop URLs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Out": {
      "main": [
        [
          {
            "node": "Loop URLs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Out2": {
      "main": [
        [
          {
            "node": "Aggregate Errors",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Run trigger": {
      "main": [
        [
          {
            "node": "URLs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send a message": {
      "main": [
        []
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "URLs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop URLs": {
      "main": [
        [
          {
            "node": "Total",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate Errors": {
      "main": [
        [
          {
            "node": "Send a message",
            "type": "main",
            "index": 0
          },
          {
            "node": "Send a text message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {},
  "versionId": "a57dca2e-09ec-4a0d-bea2-7b395bb7c745",
  "meta": {
    "templateId": "5298",
    "templateCredsSetupCompleted": true
  },
  "id": "dTeyTnpDAIYf2r5K",
  "tags": []
}

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.

About this workflow

url-uptime-monitor. Uses scheduleTrigger, splitOut, googleSheets, summarize. Scheduled trigger; 18 nodes.

Source: https://github.com/Cuongyd196/n8n-workflows/blob/main/workflows/devops/url-uptime-monitor/url-uptime-monitor.json — original creator credit. Request a take-down →

More Email & Gmail workflows → · Browse all categories →