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 →
{
"name": "Run scheduled recurring scans from Google Sheets",
"nodes": [
{
"parameters": {
"content": "## Run Scheduled Recurring Scans from Google Sheets\n\n**Who is this for:** Users who want to manage recurring scan schedules in a spreadsheet rather than creating individual campaigns.\n\n**What this workflow does:**\n1. Reads scan configuration from Google Sheets\n2. Runs scans on your specified schedule\n3. Supports different frequencies per location\n4. Logs results back to the sheet\n5. Sends summary notification\n\n**How to set up:**\n1. Add your Local Falcon API credentials (get your key at https://www.localfalcon.com/api/credentials)\n2. Create a Google Sheet with columns: place_id, keyword, lat, lng, frequency, last_scan, next_scan\n3. Add your scan configurations\n4. Activate the workflow\n\n**Requirements:**\n- Local Falcon account with API access and credits\n- Google Sheets with scan configuration\n- Sufficient credits for recurring scans\n\n**How to customize:**\n- Add priority levels for critical locations\n- Implement credit budgeting logic\n- Create different schedules by day of week\n- Add error retry logic",
"height": 500,
"width": 460,
"color": 5
},
"id": "sticky-main",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
100,
-160
]
},
{
"parameters": {
"content": "### Sheet Format\n\n| place_id | keyword | lat | lng | frequency | last_scan |\n|----------|---------|-----|-----|-----------|------------|\n| ChIJ... | pizza | 40.7 | -74 | daily | 2024-01-15 |\n| ChIJ... | dentist | 34.0 | -118 | weekly | 2024-01-10 |",
"height": 160,
"width": 340
},
"id": "sticky-format",
"name": "Sticky Note Format",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
580,
-160
]
},
{
"parameters": {
"content": "### Step 1: Schedule\nRuns hourly to check due scans.",
"height": 100,
"width": 200
},
"id": "sticky-step1",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
40,
640
]
},
{
"parameters": {
"content": "### Step 2: Read Config\nGets scan configuration.",
"height": 100,
"width": 200
},
"id": "sticky-step2",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
300,
640
]
},
{
"parameters": {
"content": "### Step 3: Filter Due\nFinds scans that are due.",
"height": 100,
"width": 200
},
"id": "sticky-step3",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
560,
640
]
},
{
"parameters": {
"content": "### Step 4: Run Scans\nExecutes due scans.",
"height": 100,
"width": 200
},
"id": "sticky-step4",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
820,
640
]
},
{
"parameters": {
"content": "### Step 5: Update\nLogs results to sheet.",
"height": 100,
"width": 200
},
"id": "sticky-step5",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
1080,
640
]
},
{
"parameters": {
"rule": {
"interval": [
{
"field": "hours",
"hoursInterval": 1
}
]
}
},
"id": "schedule",
"name": "Every Hour",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.2,
"position": [
140,
420
]
},
{
"parameters": {
"operation": "read",
"documentId": {
"__rl": true,
"mode": "id",
"value": "YOUR_SCAN_CONFIG_SHEET_ID"
},
"sheetName": {
"__rl": true,
"mode": "name",
"value": "Scans"
},
"options": {}
},
"id": "read-config",
"name": "Read Scan Config",
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4.5,
"position": [
400,
420
],
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "const configs = $input.all();\nconst now = new Date();\nconst dueScans = [];\n\nfor (const config of configs) {\n const data = config.json;\n const lastScan = data.last_scan ? new Date(data.last_scan) : new Date(0);\n const frequency = (data.frequency || 'daily').toLowerCase();\n \n let isDue = false;\n const hoursSinceLastScan = (now - lastScan) / (1000 * 60 * 60);\n \n switch (frequency) {\n case 'hourly':\n isDue = hoursSinceLastScan >= 1;\n break;\n case 'daily':\n isDue = hoursSinceLastScan >= 24;\n break;\n case 'weekly':\n isDue = hoursSinceLastScan >= 168;\n break;\n case 'monthly':\n isDue = hoursSinceLastScan >= 720;\n break;\n }\n \n if (isDue) {\n dueScans.push({ json: data });\n }\n}\n\nreturn dueScans;"
},
"id": "filter-due",
"name": "Filter Due Scans",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
660,
420
]
},
{
"parameters": {
"resource": "scan",
"operation": "run",
"platform": "={{ $json.platform || 'google' }}",
"placeId": "={{ $json.place_id }}",
"keyword": "={{ $json.keyword }}",
"lat": "={{ $json.lat }}",
"lng": "={{ $json.lng }}",
"gridSize": "={{ $json.grid_size || 7 }}",
"radius": "={{ $json.radius || 1 }}",
"measurement": "mi"
},
"id": "run-scan",
"name": "Run Due Scan",
"type": "@local-falcon/n8n-nodes-localfalcon.localFalcon",
"typeVersion": 1,
"position": [
920,
420
],
"credentials": {
"localFalconApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "update",
"documentId": {
"__rl": true,
"mode": "id",
"value": "YOUR_SCAN_CONFIG_SHEET_ID"
},
"sheetName": {
"__rl": true,
"mode": "name",
"value": "Scans"
},
"columns": {
"mappingMode": "defineBelow",
"value": {
"last_scan": "={{ $now.toISO() }}",
"last_result": "={{ $json.avg_rank || 'N/A' }}",
"report_key": "={{ $json.report_key }}"
}
},
"options": {}
},
"id": "update-sheet",
"name": "Update Last Scan",
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4.5,
"position": [
1180,
420
],
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
}
}
],
"connections": {
"Every Hour": {
"main": [
[
{
"node": "Read Scan Config",
"type": "main",
"index": 0
}
]
]
},
"Read Scan Config": {
"main": [
[
{
"node": "Filter Due Scans",
"type": "main",
"index": 0
}
]
]
},
"Filter Due Scans": {
"main": [
[
{
"node": "Run Due Scan",
"type": "main",
"index": 0
}
]
]
},
"Run Due Scan": {
"main": [
[
{
"node": "Update Last Scan",
"type": "main",
"index": 0
}
]
]
}
},
"meta": {
"templateCredsSetupCompleted": true
}
}
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.
googleSheetsOAuth2ApilocalFalconApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Run scheduled recurring scans from Google Sheets. Uses googleSheets, @local-falcon/n8n-nodes-localfalcon. Scheduled trigger; 12 nodes.
Source: https://github.com/local-falcon/n8n-templates/blob/3dd7676046b6b8efc3bda40821cc944664db80f2/templates/29-scheduled-recurring-scans.json — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Generate and send monthly client ranking reports. Uses googleSheets, @local-falcon/n8n-nodes-localfalcon, emailSend. Scheduled trigger; 11 nodes.
This workflow automates video distribution to 9 social platforms simultaneously using Blotato's API. It includes both a scheduled publisher (checks Google Sheets for videos marked "Ready") and a subwo
YogiAI. Uses googleSheets, googleSheetsTool, httpRequest, stopAndError. Scheduled trigger; 61 nodes.
This workflow monitors Google Calendar for events indicating that a customer will visit the company today or the next day, retrieves the required details, and sends reminder notifications to the relev
Useful if a team is working within a single instance and you want to be notified of what workflows have changed since you last visited them. Another use-case might be monitoring your managed instances