AutomationFlowsData & Sheets › Daily Competitor Research Automation Using Serpapi, Google Sheets & Airtable

Daily Competitor Research Automation Using Serpapi, Google Sheets & Airtable

ByAvkash Kakdiya @itechnotion on n8n.io

This workflow automatically collects a list of companies from Google Sheets, searches for their competitors using SerpAPI, extracts up to 10 relevant competitor names with source links, and logs the results into both Google Sheets and Airtable. It runs on a set schedule, cleans…

Cron / scheduled trigger★★★★☆ complexity15 nodesGoogle SheetsHTTP RequestAirtable
Data & Sheets Trigger: Cron / scheduled Nodes: 15 Complexity: ★★★★☆ Added:

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

This workflow follows the Airtable → Google Sheets 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": "10 - Business Landscape Tracker",
  "tags": [],
  "nodes": [
    {
      "id": "1be5e364-b985-4ce4-bb52-5f4d4afc8481",
      "name": "\ud83d\udd52 Auto Run (Scheduled)",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -2040,
        0
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "triggerAtHour": 9
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "7e5d5597-0e13-477d-af55-2847cdd4b89c",
      "name": "\ud83d\udcc4 Read Companies Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -1820,
        0
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_GOOGLE_SHEET_ID_HERE/edit#gid=0",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_GOOGLE_SHEET_ID_HERE",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_GOOGLE_SHEET_ID_HERE/edit?usp=drivesdk",
          "cachedResultName": "Companies List"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4
    },
    {
      "id": "0942f8f7-220c-48fc-bda0-43b250981475",
      "name": "\ud83e\uddf9 Clean & Format Company List",
      "type": "n8n-nodes-base.code",
      "position": [
        -1600,
        0
      ],
      "parameters": {
        "jsCode": "const companies = [];\n\nfor (const item of $input.all()) {\n  // Get company name from the correct field key: \"List\"\n  const companyName = item.json.List;\n\n  if (companyName && companyName.trim() !== '') {\n    companies.push({\n      company: companyName.trim(),\n      row: item.json.row_number || companies.length + 2\n    });\n  }\n}\n\n// Return each company as a separate item\nreturn companies.map(company => ({ json: company }));"
      },
      "typeVersion": 2
    },
    {
      "id": "dd0127ef-28ef-4928-8cdc-6cf0627ac365",
      "name": "\ud83d\udd01 Loop Over Companies",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        -1380,
        0
      ],
      "parameters": {
        "options": {},
        "batchSize": 100
      },
      "typeVersion": 3
    },
    {
      "id": "ee903349-6643-4c97-862c-bd50d2abcd8d",
      "name": "\ud83c\udf0d Search Company Competitors (SerpAPI)",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -1160,
        0
      ],
      "parameters": {
        "url": "https://serpapi.com/search.json",
        "options": {
          "response": {
            "response": {
              "responseFormat": "json"
            }
          }
        },
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "q",
              "value": "={{ $json.company }} competitors"
            },
            {
              "name": "hl",
              "value": "en"
            },
            {
              "name": "gl",
              "value": "us"
            },
            {
              "name": "api_key",
              "value": "YOUR_SERPAPI_KEY_HERE"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "a5f9d7d7-a7b2-4325-bbe9-c2feb053f98a",
      "name": "\ud83e\udde0 Extract Competitor Data from Search",
      "type": "n8n-nodes-base.code",
      "position": [
        -940,
        0
      ],
      "parameters": {
        "jsCode": "const inputData = $input.all();\nconst results = [];\n\nfor (const item of inputData) {\n  const searchResults = item.json;\n\n  // === Helper Functions (same as before) ===\n\n  function extractCompanyName(searchData) {\n    if (searchData.search_parameters?.q) {\n      const query = searchData.search_parameters.q.toLowerCase();\n      const cleanQuery = query\n        .replace(/competitors?/gi, '')\n        .replace(/competition/gi, '')\n        .replace(/vs/gi, '')\n        .replace(/alternatives?/gi, '')\n        .trim();\n      const companyName = cleanQuery.split(' ')[0];\n      return companyName.charAt(0).toUpperCase() + companyName.slice(1);\n    }\n\n    if (searchData.organic_results?.[0]?.title) {\n      const title = searchData.organic_results[0].title;\n      const match = title.match(/(\\w+)\\s+competitors?/i);\n      if (match) return match[1];\n    }\n\n    return \"Unknown Company\";\n  }\n\n  function extractCompetitors(searchData) {\n    const competitors = new Set();\n\n    if (searchData.related_questions) {\n      searchData.related_questions.forEach(q => {\n        q.list?.forEach(item => {\n          const clean = item.replace(/\\.$/, '').trim();\n          if (clean.length > 1) competitors.add(clean);\n        });\n      });\n    }\n\n    if (searchData.ai_overview?.text_blocks) {\n      searchData.ai_overview.text_blocks.forEach(block => {\n        block.list?.forEach(item => {\n          const clean = item.title?.replace(/:$/, '').trim();\n          if (clean?.length > 1) competitors.add(clean);\n        });\n      });\n    }\n\n    if (searchData.organic_results) {\n      searchData.organic_results.forEach(result => {\n        const snippet = result.snippet;\n        if (snippet) {\n          const patterns = [\n            /competitors?[^.]*?include[^.]*?([A-Z][a-zA-Z\\s,&.]+)/gi,\n            /rivals?[^.]*?include[^.]*?([A-Z][a-zA-Z\\s,&.]+)/gi,\n            /competition[^.]*?from[^.]*?([A-Z][a-zA-Z\\s,&.]+)/gi\n          ];\n          patterns.forEach(pattern => {\n            const matches = snippet.match(pattern);\n            if (matches) {\n              matches.forEach(match => {\n                match\n                  .split(/,|\\sand\\s|&/)\n                  .map(name => name.replace(/[^a-zA-Z\\s]/g, '').trim())\n                  .forEach(name => {\n                    if (name.length > 2 && !name.toLowerCase().includes('competitor'))\n                      competitors.add(name);\n                  });\n              });\n            }\n          });\n        }\n      });\n    }\n\n    if (searchData.answer_box?.expanded_list) {\n      searchData.answer_box.expanded_list.forEach(item => {\n        if (item.title) competitors.add(item.title);\n      });\n    }\n\n    return Array.from(competitors).slice(0, 10);\n  }\n\n  function getTopSource(searchData) {\n    return searchData.organic_results?.[0]?.link || null;\n  }\n\n  // === Main Logic ===\n  try {\n    const company = extractCompanyName(searchResults);\n    const competitorsList = extractCompetitors(searchResults);\n    const topSource = getTopSource(searchResults);\n\n    results.push({\n      json: {\n        company,\n        competitors: competitorsList.join(', ') || 'No competitors found',\n        competitor_count: competitorsList.length,\n        top_source: topSource,\n        search_query: searchResults.search_parameters?.q || 'N/A',\n        total_results: searchResults.search_information?.total_results || 0,\n        extraction_timestamp: new Date().toISOString()\n      }\n    });\n  } catch (err) {\n    results.push({\n      json: {\n        error: `Extraction failed: ${err.message}`,\n        company: \"Unknown\",\n        competitors: \"\",\n        competitor_count: 0,\n        top_source: null\n      }\n    });\n  }\n}\n\nreturn results;"
      },
      "typeVersion": 2
    },
    {
      "id": "20193daa-c689-4311-ad39-9f696794712d",
      "name": "\ud83e\uddd0 Has Competitors?",
      "type": "n8n-nodes-base.if",
      "position": [
        -720,
        0
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "8a7b6c5d-4321-0987-6543-210fedcba987",
              "operator": {
                "type": "string",
                "operation": "notEquals"
              },
              "leftValue": "={{ $json.competitors }}",
              "rightValue": "No competitors found"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "1f513736-3963-4045-9eaa-4df3cebfc0e3",
      "name": "\ud83d\udcca Log to Result Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -500,
        -100
      ],
      "parameters": {
        "columns": {
          "value": {
            "Source": "={{ $json.top_source }}",
            "Company": "={{ $json.company }}",
            "Competitors": "={{ $json.competitors }}",
            "Total Results": "={{ $json.total_results }}"
          },
          "schema": [
            {
              "id": "Company",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Company",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Competitors",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Competitors",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Total Results",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Total Results",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Source",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Source",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "Company"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {
          "useAppend": true,
          "cellFormat": "USER_ENTERED"
        },
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_RESULTS_GOOGLE_SHEET_ID_HERE/edit#gid=0",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_RESULTS_GOOGLE_SHEET_ID_HERE",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_RESULTS_GOOGLE_SHEET_ID_HERE/edit?usp=drivesdk",
          "cachedResultName": "Companies Result"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4
    },
    {
      "id": "d39b91c7-a3c0-45eb-b926-2d668262a899",
      "name": "\u274c Log Companies Without Results",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -500,
        100
      ],
      "parameters": {
        "columns": {
          "value": {
            "Source": "Null",
            "Company": "={{ $json.company }}",
            "Competitors": "No Competetitors Found",
            "Total Results": "0"
          },
          "schema": [
            {
              "id": "Company",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Company",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Competitors",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Competitors",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Total Results",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Total Results",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Source",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Source",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {
          "useAppend": true,
          "cellFormat": "USER_ENTERED"
        },
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_RESULTS_GOOGLE_SHEET_ID_HERE/edit#gid=0",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_RESULTS_GOOGLE_SHEET_ID_HERE",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_RESULTS_GOOGLE_SHEET_ID_HERE/edit?usp=drivesdk",
          "cachedResultName": "Companies Result"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4
    },
    {
      "id": "7910cb3e-88e7-4716-82ad-89bec10409a8",
      "name": "\ud83d\uddc3\ufe0f Sync to Airtable",
      "type": "n8n-nodes-base.airtable",
      "position": [
        -280,
        0
      ],
      "parameters": {
        "base": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_AIRTABLE_BASE_ID_HERE",
          "cachedResultUrl": "https://airtable.com/YOUR_AIRTABLE_BASE_ID_HERE",
          "cachedResultName": "Test"
        },
        "table": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_AIRTABLE_TABLE_ID_HERE",
          "cachedResultUrl": "https://airtable.com/YOUR_AIRTABLE_BASE_ID_HERE/YOUR_AIRTABLE_TABLE_ID_HERE",
          "cachedResultName": "Table 1"
        },
        "columns": {
          "value": {
            "Source": "={{ $json.Source }}",
            "Company": "={{ $json.Company }}",
            "Competitors": "={{ $json.Competitors }}",
            "Total Results": "={{ $json[\"Total Results\"] }}"
          },
          "schema": [
            {
              "id": "id",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": true,
              "required": false,
              "displayName": "id",
              "defaultMatch": true
            },
            {
              "id": "Company",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "Company",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Competitors",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "Competitors",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Total Results",
              "type": "number",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "Total Results",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Source",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "Source",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "Company"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "upsert"
      },
      "credentials": {
        "airtableTokenApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "b299cf8f-6b5e-48f2-adb4-2794ad4220aa",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2140,
        -340
      ],
      "parameters": {
        "color": 5,
        "width": 280,
        "height": 640,
        "content": "## Auto Run (Scheduled)\n\n**Description:**\n\n\u23f0 This is the starting point of the workflow. It runs automatically based on your configured schedule\u2014no manual trigger needed."
      },
      "typeVersion": 1
    },
    {
      "id": "e21ba623-5589-4781-a131-31afbee80431",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1840,
        -340
      ],
      "parameters": {
        "color": 3,
        "width": 600,
        "height": 640,
        "content": "## Loop Over Companies\n\n**Description:**\n\n\ud83d\udccb Fetch companies from a Google Sheet (List column).\n\n\ud83e\uddf9 Clean and format data, removing empty entries and attaching row numbers.\n\n\ud83d\udd01 Send companies into the flow using a Lopp Over node for processing.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "38ce6a00-5b57-47a2-9cce-e2deaa67e285",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1220,
        -340
      ],
      "parameters": {
        "color": 4,
        "width": 220,
        "height": 640,
        "content": "## Search Company Competitors (SerpAPI)\n\n**Description:**\n\n\ud83d\udd0d For each company, this node sends a GET request to SerpAPI with the query {company} competitors, returning structured search result JSON for further parsing."
      },
      "typeVersion": 1
    },
    {
      "id": "78b206db-221d-426c-aa15-1115c2266cc8",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -980,
        -340
      ],
      "parameters": {
        "width": 420,
        "height": 640,
        "content": "## Extract & Check Competitors\n\n**Description:**\n\nExtracts the company name, up to 10 competitors, and top source from SerpAPI results.\nThen checks if any competitors were found:\n\n\u2705 Yes \u2192 Send to Google Sheet + Airtable\n\n\u274c No \u2192 Log as failed search"
      },
      "typeVersion": 1
    },
    {
      "id": "6ef49329-b2a7-44ad-9226-d2c61dc996e7",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -540,
        -340
      ],
      "parameters": {
        "color": 6,
        "width": 460,
        "height": 640,
        "content": "## Log Results to Sheets & Airtable\n\n**Description:**\n\nLogs successful competitor data to Google Sheets \u2705\n\nLogs failed searches separately \u274c\n\nBoth flows sync to Airtable for unified storage \ud83d\udce5"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "connections": {
    "\ud83e\uddd0 Has Competitors?": {
      "main": [
        [
          {
            "node": "\ud83d\udcca Log to Result Sheet",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "\u274c Log Companies Without Results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udcca Log to Result Sheet": {
      "main": [
        [
          {
            "node": "\ud83d\uddc3\ufe0f Sync to Airtable",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udd01 Loop Over Companies": {
      "main": [
        [],
        [
          {
            "node": "\ud83c\udf0d Search Company Competitors (SerpAPI)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udcc4 Read Companies Sheet": {
      "main": [
        [
          {
            "node": "\ud83e\uddf9 Clean & Format Company List",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udd52 Auto Run (Scheduled)": {
      "main": [
        [
          {
            "node": "\ud83d\udcc4 Read Companies Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83e\uddf9 Clean & Format Company List": {
      "main": [
        [
          {
            "node": "\ud83d\udd01 Loop Over Companies",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u274c Log Companies Without Results": {
      "main": [
        [
          {
            "node": "\ud83d\uddc3\ufe0f Sync to Airtable",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83e\udde0 Extract Competitor Data from Search": {
      "main": [
        [
          {
            "node": "\ud83e\uddd0 Has Competitors?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83c\udf0d Search Company Competitors (SerpAPI)": {
      "main": [
        [
          {
            "node": "\ud83e\udde0 Extract Competitor Data from Search",
            "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 automatically collects a list of companies from Google Sheets, searches for their competitors using SerpAPI, extracts up to 10 relevant competitor names with source links, and logs the results into both Google Sheets and Airtable. It runs on a set schedule, cleans…

Source: https://n8n.io/workflows/7313/ — 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 workflow automates tax compliance by aggregating multi-channel revenue data, calculating jurisdiction-specific tax obligations, detecting anomalies, and generating submission-ready reports for ta

Gmail, Google Sheets, Airtable +1
Data & Sheets

This n8n workflow automates the end-to-end client onboarding process: capturing client details, validating emails, assigning tiers, generating welcome packs, creating tasks, notifying teams, archiving

Google Sheets, Gmail, Airtable +5
Data & Sheets

Automate B2B order invoicing by fetching orders from Airtable, validating paid B2B entries, creating Stripe customers and invoices, finalizing invoices, and logging structured invoice data into Google

Airtable, Stripe, HTTP Request +1
Data & Sheets

This workflow runs daily to collect the latest funding round data from Crunchbase. It retrieves up to 100 recent funding events, including company, investors, funding amount, and industry details. The

Google Sheets, Airtable, HTTP Request
Data & Sheets

Automates Singapore COE price tracking with AI forecasts and buy/wait recommendations. Weekly scraping collects LTA data, enriches with economic indicators, predicts 6-month trends, and alerts users v

HTTP Request, Google Sheets, Agent +4