AutomationFlowsData & Sheets › Wf Maps Serp Backup Data

Wf Maps Serp Backup Data

wf_maps_serp_backup_data. Uses httpRequest, executeWorkflowTrigger, googleSheets, supabase. Event-driven trigger; 10 nodes.

Event trigger★★★★☆ complexity10 nodesHTTP RequestExecute Workflow TriggerGoogle SheetsSupabaseDiscord
Data & Sheets Trigger: Event Nodes: 10 Complexity: ★★★★☆ Added:

This workflow follows the Discord → HTTP Request 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
{
  "name": "wf_maps_serp_backup_data",
  "nodes": [
    {
      "parameters": {
        "workflowId": {
          "__rl": true,
          "value": "wRCvCmRGcILIoLjy",
          "mode": "list",
          "cachedResultName": "Underfoot \u2014 wf_error_notifications"
        },
        "workflowInputs": {
          "mappingMode": "defineBelow",
          "value": {
            "json": "={{ { ...$input.all() } }}",
            "callingWorkflow": "={ name: {{$workflow.name}}, id: {{$execution.id}} }"
          },
          "matchingColumns": [
            "json"
          ],
          "schema": [
            {
              "id": "json",
              "displayName": "json",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "canBeUsedToMatch": true,
              "type": "object",
              "removed": false
            },
            {
              "id": "callingWorkflow",
              "displayName": "callingWorkflow",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "canBeUsedToMatch": true,
              "type": "object",
              "removed": false
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": true
        },
        "options": {
          "waitForSubWorkflow": false
        }
      },
      "type": "n8n-nodes-base.executeWorkflow",
      "typeVersion": 1.2,
      "position": [
        1632,
        -448
      ],
      "id": "60dbc180-14f5-42cd-94ef-393f688a4cbe",
      "name": "call_error_notifier",
      "retryOnFail": false
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.brightdata.com/request",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "brightdataApi",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"zone\": \"serp_api1\",\n  \"url\": \"{{$json.url}}\",\n  \"format\": \"raw\",\n  \"headers\": {\n    \"x-unblock-data-format\": \"parsed_light\"\n  }\n}",
        "options": {
          "timeout": 10000
        }
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        736,
        -480
      ],
      "id": "2b881369-048e-482f-91c5-c7ef5cbf096e",
      "name": "brightdata_serp_api",
      "executeOnce": false,
      "retryOnFail": true,
      "credentials": {
        "brightdataApi": {
          "name": "<your credential>"
        }
      },
      "onError": "continueErrorOutput"
    },
    {
      "parameters": {
        "inputSource": "jsonExample",
        "jsonExample": "{\n  \"intent\": \"an interest\",\n  \"location\": \"a place to look for events\"\n}"
      },
      "type": "n8n-nodes-base.executeWorkflowTrigger",
      "typeVersion": 1.1,
      "position": [
        64,
        -400
      ],
      "id": "74f79f8d-6497-4bbe-affc-15fe82493c65",
      "name": "workflow_trigger",
      "alwaysOutputData": false
    },
    {
      "parameters": {
        "jsCode": "// Code node \u2014 Run Once for All Items\n// Build a Google Maps search URL from intent + location, biasing by coordinates if available.\n// Input item shape supported:\n// { query: { intent, location, latitude, longitude } }  OR flat { intent, location, latitude, longitude }\n// Output: ARRAY of { url }\n\nconst items = $input.all();\n\nconst norm = (s) => (s ?? \"\").toString().trim().replace(/\\s+/g, \" \");\n\nfunction buildMapsSearchURL({ intent, location, latitude, longitude }) {\n  const i = norm(intent);\n  const l = norm(location);\n  const hasLatLng =\n    Number.isFinite(latitude) && Number.isFinite(longitude);\n\n  if (!i && !l && !hasLatLng) {\n    throw new Error(\"Provide at least an intent, a location, or coordinates\");\n  }\n\n  // Prefer \"intent near lat,lng\" when coords exist; otherwise combine intent + location.\n  let q = \"\";\n  if (hasLatLng) q = i ? `${i} near ${latitude},${longitude}` : `${latitude},${longitude}`;\n  else if (i && l) q = `${i} ${l}`;\n  else q = i || l;\n\n  return `https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(q)}`;\n}\n\nreturn items.map((item) => {\n  const j = item.json ?? item;\n  const src = j.query ?? j;\n  const feature = j.features[0];\n\n  const lat = Number(src.latitude ?? src.lat ?? src.coordinates?.lat);\n  const lng = Number(src.longitude ?? src.lng ?? src.coordinates?.lng);\n\n  return {\n    url: buildMapsSearchURL({\n      intent: $('workflow_trigger').first().json.intent,\n      location: src.text ?? feature.properties.formatted,\n      latitude: Number.isFinite(feature.properties.lat) ? feature.properties.lat : undefined,\n      longitude: Number.isFinite(feature.properties.lon) ? feature.properties.lon : undefined,\n    }),\n  };\n});"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        512,
        -400
      ],
      "id": "12a68956-31fd-44f9-a28e-5a7ed09e8c93",
      "name": "format_search_url",
      "onError": "continueErrorOutput"
    },
    {
      "parameters": {
        "jsCode": "// Inputs:\n// - html body at $input.first().json.body  (required)\n// - base_url at $json.base_url OR $input.first().json.url (optional but recommended)\n//\n// Output: [{ json: { structured, scraped_data } }]\n\nconst html = $input.first().json.body || \"\";\nconst baseUrl =\n  $input.first().json.url ||\n  \"\";\n\n// --- helpers ---\nfunction categorizeLink(url) {\n  if (!url) return \"unknown\";\n  const u = url.toLowerCase();\n  if (u.startsWith(\"mailto:\") || u.startsWith(\"tel:\")) return \"contact\";\n  if (u.includes(\"facebook\") || u.includes(\"linkedin\") || u.includes(\"pinterest\") || u.includes(\"youtube\") || u.includes(\"twitter\") || u.includes(\"x.com\") || u.includes(\"instagram\") || u.includes(\"tiktok\")) return \"social\";\n  if (u.startsWith(\"/\")) return \"internal\";\n  try {\n    const abs = new URL(u, baseUrl);\n    const base = new URL(baseUrl || abs.href);\n    return abs.origin === base.origin ? \"internal\" : \"external\";\n  } catch {\n    return u.startsWith(\"http\") ? \"external\" : \"unknown\";\n  }\n}\n\nfunction toAbs(u) {\n  if (!u) return u;\n  try { return new URL(u, baseUrl).href; } catch { return u; }\n}\n\n// very light HTML entity decode (good enough for headings/link text)\nfunction decodeHTML(str) {\n  if (!str) return str;\n  const map = { amp: \"&\", lt: \"<\", gt: \">\", quot: '\"', apos: \"'\" };\n  return str.replace(/&(#\\d+|#x[0-9a-fA-F]+|[a-zA-Z]+);/g, (m, ent) => {\n    if (ent[0] === \"#\") {\n      const code = ent[1].toLowerCase() === \"x\" ? parseInt(ent.slice(2),16) : parseInt(ent.slice(1),10);\n      return Number.isFinite(code) ? String.fromCharCode(code) : m;\n    }\n    return map[ent] ?? m;\n  });\n}\n\nfunction textOnly(s) { return decodeHTML(s.replace(/<[^>]+>/g, \" \").replace(/\\s+/g, \" \").trim()); }\n\n// --- headings ---\nconst headings = [...html.matchAll(/<h([1-6])[^>]*>([\\s\\S]*?)<\\/h\\1>/gi)].map(m => {\n  let inner = m[2];\n  const spanMatch = inner.match(/<span[^>]*class=\"([^\"]*)\"[^>]*data-ux=\"([^\"]*)\"?[^>]*>([\\s\\S]*?)<\\/span>/i);\n  if (spanMatch) inner = spanMatch[3];\n  return { tag: m[1], text: textOnly(inner) };\n});\n\n// --- links (href + text) ---\nconst links = [...html.matchAll(/<a\\b([^>]*?)href=\"([^\"]+)\"([^>]*)>([\\s\\S]*?)<\\/a>/gi)].map(m => {\n  const href = m[2];\n  const text = textOnly(m[4]);\n  const url = toAbs(href);\n  return { url, text, category: categorizeLink(href) };\n});\n\n// --- images (src + alt) ---\nconst images = [...html.matchAll(/<img\\b([^>]*?)src=\"([^\"]+)\"([^>]*?)>/gi)].map(m => {\n  const src = toAbs(m[2]);\n  const altMatch = m[0].match(/\\balt=\"([^\"]*)\"/i);\n  const alt = altMatch ? decodeHTML(altMatch[1]) : \"\";\n  return { src, alt };\n});\n\n// --- css + js refs ---\nconst css = [...html.matchAll(/<link[^>]+href=\"([^\"]+\\.css(?:\\?[^\"]*)?)\"[^>]*>/gi)].map(m => toAbs(m[1]));\nconst js  = [...html.matchAll(/<script[^>]+src=\"([^\"]+)\"[^>]*>\\s*<\\/script>/gi)].map(m => toAbs(m[1]));\n\n// --- meta tags ---\nconst title =\n  html.match(/<title>([\\s\\S]*?)<\\/title>/i)?.[1]?.trim() ||\n  html.match(/<meta[^>]+property=\"og:title\"[^>]+content=\"([^\"]*)\"/i)?.[1] ||\n  html.match(/<meta[^>]+name=\"twitter:title\"[^>]+content=\"([^\"]*)\"/i)?.[1] ||\n  \"\";\n\nconst description =\n  html.match(/<meta[^>]+name=\"description\"[^>]+content=\"([^\"]*)\"/i)?.[1] ||\n  html.match(/<meta[^>]+property=\"og:description\"[^>]+content=\"([^\"]*)\"/i)?.[1] ||\n  html.match(/<meta[^>]+name=\"twitter:description\"[^>]+content=\"([^\"]*)\"/i)?.[1] ||\n  \"\";\n\nconst canonical = toAbs(html.match(/<link[^>]+rel=\"canonical\"[^>]+href=\"([^\"]+)\"/i)?.[1] || \"\");\nconst robots = html.match(/<meta[^>]+name=\"robots\"[^>]+content=\"([^\"]*)\"/i)?.[1] || \"\";\n\nconst ogImage = toAbs(html.match(/<meta[^>]+property=\"og:image\"[^>]+content=\"([^\"]+)\"/i)?.[1] || \"\");\nconst twImage = toAbs(html.match(/<meta[^>]+name=\"twitter:image\"[^>]+content=\"([^\"]+)\"/i)?.[1] || \"\");\n\n// --- tables -> rows of cell text ---\nconst tables = [...html.matchAll(/<table[^>]*>([\\s\\S]*?)<\\/table>/gi)].map(t => {\n  const tableHTML = t[1];\n  const rows = [...tableHTML.matchAll(/<tr[^>]*>([\\s\\S]*?)<\\/tr>/gi)].map(r => {\n    const rowHTML = r[1];\n    const cells = [...rowHTML.matchAll(/<(td|th)[^>]*>([\\s\\S]*?)<\\/\\1>/gi)].map(c => textOnly(c[2]));\n    return cells;\n  });\n  return { rows };\n});\n\n// --- structured payload ---\nconst structured = {\n  base_url: baseUrl || null,\n  meta: {\n    title: decodeHTML(title),\n    description: decodeHTML(description),\n    canonical,\n    robots,\n    og_image: ogImage,\n    twitter_image: twImage,\n  },\n  headings,\n  links,\n  images,\n  css,\n  js,\n  tables,\n};\n\n// return both raw and stringified\nreturn [\n  {\n    json: {\n      structured,\n      scraped_data: JSON.stringify(structured, null, 2),\n    },\n  },\n];\n"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        960,
        -592
      ],
      "id": "4a5cceeb-6d65-4d08-a4ec-430b758e4dc6",
      "name": "normalize",
      "onError": "continueErrorOutput"
    },
    {
      "parameters": {
        "operation": "appendOrUpdate",
        "documentId": {
          "__rl": true,
          "value": "1yA7rRZkgJAGrs6SiGVqHJmwcORunAmM2Mw1KecXcS4A",
          "mode": "list",
          "cachedResultName": "underfoot_google_maps_serp_cache",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1yA7rRZkgJAGrs6SiGVqHJmwcORunAmM2Mw1KecXcS4A/edit?usp=drivesdk"
        },
        "sheetName": {
          "__rl": true,
          "value": "gid=0",
          "mode": "list",
          "cachedResultName": "Sheet1",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1yA7rRZkgJAGrs6SiGVqHJmwcORunAmM2Mw1KecXcS4A/edit#gid=0"
        },
        "columns": {
          "mappingMode": "autoMapInputData",
          "value": {},
          "matchingColumns": [
            "url"
          ],
          "schema": [
            {
              "id": "url",
              "displayName": "url",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "title",
              "displayName": "title",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "description",
              "displayName": "description",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "rank",
              "displayName": "rank",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "global_rank",
              "displayName": "global_rank",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "query",
              "displayName": "query",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "language",
              "displayName": "language",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "location",
              "displayName": "location",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "latitude",
              "displayName": "latitude",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "longitude",
              "displayName": "longitude",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "original_url",
              "displayName": "original_url",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "cached_at",
              "displayName": "cached_at",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "user_intent",
              "displayName": "user_intent",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "user_location",
              "displayName": "user_location",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {
          "handlingExtraData": "ignoreIt",
          "useAppend": true
        }
      },
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        1408,
        -592
      ],
      "id": "75ba4e97-b3cc-410d-9c2c-b14cdcc48293",
      "name": "update_cache",
      "retryOnFail": false,
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "onError": "continueErrorOutput"
    },
    {
      "parameters": {
        "url": "=https://api.geoapify.com/v1/geocode/search",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpQueryAuth",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "text",
              "value": "={{ $json.location }}"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        288,
        -400
      ],
      "id": "fb5f1e60-8b0c-44a1-9e46-aeee3360a1fa",
      "name": "normalize_location_with_geoapify",
      "credentials": {
        "httpCustomAuth": {
          "name": "<your credential>"
        },
        "httpQueryAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "useCustomSchema": true,
        "schema": "underfoot",
        "tableId": "scrape_results",
        "dataToSend": "autoMapInputData",
        "inputsToIgnore": "error"
      },
      "type": "n8n-nodes-base.supabase",
      "typeVersion": 1,
      "position": [
        1184,
        -880
      ],
      "id": "c0ff642c-7cc4-4548-8535-ddafbe2fd0c4",
      "name": "create_cache_rows",
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      },
      "onError": "continueErrorOutput"
    },
    {
      "parameters": {
        "useCustomSchema": true,
        "schema": "underfoot",
        "operation": "update",
        "tableId": "scrape_results",
        "filters": {
          "conditions": [
            {
              "keyName": "url",
              "condition": "eq",
              "keyValue": "={{ $json.url }}"
            }
          ]
        }
      },
      "type": "n8n-nodes-base.supabase",
      "typeVersion": 1,
      "position": [
        1408,
        -784
      ],
      "id": "626ef149-ed81-4a1d-840f-9556c570638f",
      "name": "update_existing_rows",
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      },
      "onError": "continueErrorOutput"
    },
    {
      "parameters": {
        "authentication": "webhook",
        "content": "I think it passed....",
        "options": {}
      },
      "type": "n8n-nodes-base.discord",
      "typeVersion": 2,
      "position": [
        1632,
        -864
      ],
      "id": "dcdbc010-1339-40b8-b544-0327c37f5fe0",
      "name": "discord_notifier",
      "credentials": {
        "discordWebhookApi": {
          "name": "<your credential>"
        }
      }
    }
  ],
  "connections": {
    "brightdata_serp_api": {
      "main": [
        [
          {
            "node": "normalize",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "call_error_notifier",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "workflow_trigger": {
      "main": [
        [
          {
            "node": "normalize_location_with_geoapify",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "call_error_notifier": {
      "main": [
        []
      ]
    },
    "format_search_url": {
      "main": [
        [
          {
            "node": "brightdata_serp_api",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "call_error_notifier",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "normalize": {
      "main": [
        [
          {
            "node": "update_cache",
            "type": "main",
            "index": 0
          },
          {
            "node": "create_cache_rows",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "call_error_notifier",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "update_cache": {
      "main": [
        [
          {
            "node": "discord_notifier",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "call_error_notifier",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "normalize_location_with_geoapify": {
      "main": [
        [
          {
            "node": "format_search_url",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "create_cache_rows": {
      "main": [
        [
          {
            "node": "discord_notifier",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "update_existing_rows",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "update_existing_rows": {
      "main": [
        [
          {
            "node": "discord_notifier",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "call_error_notifier",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "callerPolicy": "workflowsFromSameOwner",
    "errorWorkflow": "g0RA1ILyuSMHbt6q"
  },
  "versionId": "c2a92acc-d2e6-4e94-8efd-ff44e191a668",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "id": "TjJFvBLuhBlEO3c8",
  "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.

Pro

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

About this workflow

wf_maps_serp_backup_data. Uses httpRequest, executeWorkflowTrigger, googleSheets, supabase. Event-driven trigger; 10 nodes.

Source: https://gist.github.com/anchildress1/cab1237affe75f0bed6629faeb940f2c — original creator credit. Request a take-down →

More Data & Sheets workflows → · Browse all categories →

Related workflows

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

Data & Sheets

This enables webhooks for nearly realtime updates (every 5 seconds) from Notion Databases.

Notion, Supabase, Execute Workflow Trigger +1
Data & Sheets

wf_serp_search_tool. Uses executeWorkflowTrigger, supabase, @brightdata/n8n-nodes-brightdata, httpRequest. Event-driven trigger; 10 nodes.

Execute Workflow Trigger, Supabase, @Brightdata/N8N Nodes Brightdata +1
Data & Sheets

cv-elaborator. Uses chatTrigger, executeWorkflowTrigger, agent, lmChatGoogleGemini. Chat trigger; 18 nodes.

Chat Trigger, Execute Workflow Trigger, Agent +7
Data & Sheets

Reagendamiento_v2. Uses executeWorkflowTrigger, redis, httpRequest, n8n-nodes-evolution-api. Event-driven trigger; 89 nodes.

Execute Workflow Trigger, Redis, HTTP Request +3
Data & Sheets

This workflow acts as a junior finance research analyst for a UK boutique M&A or corporate finance team. It listens for Slack messages, classifies the request, gathers company or market data, and prod

HTTP Request, Google Drive, Google Docs +5