AutomationFlowsAI & RAG › Analyze and Summarize Google Reviews with Serpapi, Gpt-4 and Google Sheets

Analyze and Summarize Google Reviews with Serpapi, Gpt-4 and Google Sheets

Bykeisha kalra @keisha on n8n.io

This n8n template helps you analyze Google Maps reviews for a list of restaurants, summarize them with AI, and identify optimization opportunities—all in one automated workflow. Whether you're managing multiple locations, helping local restaurants improve their digital presence,…

Event trigger★★★★☆ complexityAI-powered14 nodesOpenAIHTTP RequestGoogle Sheets
AI & RAG Trigger: Event Nodes: 14 Complexity: ★★★★☆ AI nodes: yes Added:

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

This workflow follows the Google Sheets → 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
{
  "id": "pRW5xgECkynFHaUx",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "template - gmbr google review",
  "tags": [],
  "nodes": [
    {
      "id": "29631d28-1dbb-419b-979e-afd26420e020",
      "name": "When clicking \u2018Test workflow\u2019",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        -960,
        -420
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "eeba4db4-59cf-4985-9331-67bfdef3b5cb",
      "name": "If review text is NOT empty ",
      "type": "n8n-nodes-base.if",
      "position": [
        40,
        -420
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "2b60b7e3-29e9-48a3-8b97-badb7dd247c3",
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              },
              "leftValue": "={{ $json.reviewText }}",
              "rightValue": "={{ $json.restaurant }}"
            }
          ]
        },
        "looseTypeValidation": true
      },
      "typeVersion": 2.2
    },
    {
      "id": "633b7ddf-416d-40cb-b3ec-bf5dd4ec50de",
      "name": "Analyze Review Sentiment",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        460,
        -500
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4-turbo",
          "cachedResultName": "GPT-4-TURBO"
        },
        "options": {},
        "messages": {
          "values": [
            {
              "content": "=Analyze the following restaurant review for the restaurant \"{{ $json.restaurant }}\".\n\n1. What is the sentiment (positive, neutral, or negative)?\n2. Extract 3\u20135 keywords or themes from the review.\n\nReview: {{ $json.reviewText }}\nInclude Restaraunt: {{ $json.restaurant }}\n\nRespond only in JSON format like:\n{\n  \"sentiment\": \"...\",\n  \"keywords\": [\"...\", \"...\", \"...\"]\n  \"restaraunt\": \"...\"\n}\n"
            }
          ]
        }
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.8
    },
    {
      "id": "cef1af19-e7d0-4c50-8afd-dcb41547e5df",
      "name": "Get Data",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -460,
        -420
      ],
      "parameters": {
        "url": "https://serpapi.com/search.json",
        "options": {},
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "engine",
              "value": "google_maps"
            },
            {
              "name": "type",
              "value": "search"
            },
            {
              "name": "q",
              "value": "={{ $json['Restaraunt Name'] }}"
            },
            {
              "name": "api_key"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "7624572b-1abb-4dc9-958d-87648b09aead",
      "name": "Cleans It Up",
      "type": "n8n-nodes-base.code",
      "position": [
        -220,
        -420
      ],
      "parameters": {
        "jsCode": "return $input.all().flatMap(item => {\n  // Check if 'place_results' exists on the item.json object\n  const placeResults = item.json.place_results;\n\n  if (!placeResults) {\n    console.log(\"No 'place_results' found for item.\");\n    return [];\n  }\n\n  const restaurant = placeResults.title || \"Unknown\";\n  const reviews = placeResults.user_reviews?.most_relevant; // Accessing the correct path for reviews\n\n  // Check if 'reviews' is an array\n  if (!Array.isArray(reviews)) {\n    console.log(\"No 'most_relevant' reviews array found for:\", restaurant);\n    return [];\n  }\n\n  return reviews.slice(0, 10).map(review => ({\n    json: {\n      restaurant,\n      reviewText: review.description || \"\", // Changed from review.snippet to review.description\n      stars: review.rating || null,\n      postedAt: review.date || \"\"\n    }\n  }));\n});"
      },
      "typeVersion": 2
    },
    {
      "id": "dedec6f9-535d-4832-81ea-de026d5b30d7",
      "name": "Pull Sample Restaurants ",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -720,
        -420
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_AWS_SECRET_KEY_HERE_D6w/edit#gid=0",
          "cachedResultName": "sample restaraunts"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_AWS_SECRET_KEY_HERE_D6w",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_AWS_SECRET_KEY_HERE_D6w/edit?usp=drivesdk",
          "cachedResultName": "GMBR: list of restaraunts "
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "b67023fc-f21c-4871-8a26-03a43fe4b35c",
      "name": "Export Data",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        820,
        -500
      ],
      "parameters": {
        "columns": {
          "value": {
            "Stars": "={{ $('Cleans It Up').item.json.stars }}",
            "Analysis": "={{ $json.message.content }}",
            "Restaraunt": "={{ $('Cleans It Up').item.json.restaurant }}",
            "Review Text": "={{ $('Cleans It Up').item.json.reviewText }}"
          },
          "schema": [
            {
              "id": "Restaraunt",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Restaraunt",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Review Text",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Review Text",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Stars",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Stars",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Analysis",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Analysis",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1346480145,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_AWS_SECRET_KEY_HERE_D6w/edit#gid=1346480145",
          "cachedResultName": "table"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_AWS_SECRET_KEY_HERE_D6w",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_AWS_SECRET_KEY_HERE_D6w/edit?usp=drivesdk",
          "cachedResultName": "GMBR: list of restaraunts "
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "f8ad050b-99f8-4d4d-b42a-4dd2a76aeb97",
      "name": "Failed Reviews (e.g. review text = empty)",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        480,
        -240
      ],
      "parameters": {
        "columns": {
          "value": {
            "Stars": "={{ $json.stars }}",
            "Keywords": "Skipped (no reviewText)",
            "Sentiment": "Skipped (no reviewText)",
            "Restaraunt": "={{ $json.restaurant }}",
            "Review Text": "={{ $json.reviewText }}"
          },
          "schema": [
            {
              "id": "Restaraunt",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Restaraunt",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Review Text",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Review Text",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Stars",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Stars",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Sentiment",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Sentiment",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Keywords",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Keywords",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1253412439,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_AWS_SECRET_KEY_HERE_D6w/edit#gid=1253412439",
          "cachedResultName": "skipped reviews"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_AWS_SECRET_KEY_HERE_D6w",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_AWS_SECRET_KEY_HERE_D6w/edit?usp=drivesdk",
          "cachedResultName": "GMBR: list of restaraunts "
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "281659ab-03e9-4ada-8f23-d35389f5909e",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1560,
        -800
      ],
      "parameters": {
        "width": 460,
        "height": 980,
        "content": "## Try It Out!\nThis n8n template helps you analyze Google Maps reviews for a list of restaurants, summarize them with AI, and identify optimization opportunities\u2014all in one automated workflow.\nWhether you're managing multiple locations, helping local restaurants improve their digital presence, or conducting a competitor analysis, this workflow helps you extract insights from dozens of reviews in minutes.\n\n  How It Works?\n- Start with a pre-filled list of restaurants in Google Sheets.\n- The workflow uses SerpAPI to scrape Google Maps reviews for each listing.\n- Reviews with content are passed to ChatGPT for summarization.\n- Empty or failed reviews are logged in a separate tab for easy follow-up.\n- Results are stored back in your Google Sheet for analysis or sharing\n\nHow To Use\n- Customize the input list in Google Sheets with your own restaurants.\n- Update the OpenAI prompt if you want a different style of summary.\n- You can trigger this manually or swap in a schedule, webhook, or other event.\n\nRequirements\n- A SerpAPI account to fetch reviews\n- An OpenAI account for ChatGPT summarization\n- Access to Google Sheets and n8n\n\nWho Is It For? \n- This is helpful for people looking to analyze a large batch of Google reviews in a short amount of time. Additionally, it can be used to compare restaurants and see where each can be optimized. \n\n\nHow To Set-Up? \n- Use a SerpAPI endpoint to include in the HTTP request node. Refer to this n8n documentation for more help! https://docs.n8n.io/integrations/builtin/cluster-nodes/sub-nodes/n8n-nodes-langchain.toolserpapi/. \n\nHappy Automating! "
      },
      "typeVersion": 1
    },
    {
      "id": "e3c1e3a8-905c-4fae-8ea7-b02a306d7139",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -780,
        -700
      ],
      "parameters": {
        "width": 500,
        "height": 760,
        "content": "## 1. Scrape Google Maps Business Listings\n\nUsing the Google Sheets information gathered beforehand, the restaurant information (e.g. name, Google Maps URL) is passed into SerpAPI one by one.  "
      },
      "typeVersion": 1
    },
    {
      "id": "34ca561c-8b25-422c-bdca-c296ed1bdb30",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -240,
        -700
      ],
      "parameters": {
        "width": 500,
        "height": 760,
        "content": "## 2. Clean the Data and Identify Gaps \n\nThe data from SerpAPI is structured messily and hard to understand. So, we use a code node to clean up un-necessary information. Afterwards, we can more easily identify gaps in the review. For example, if there is no review text we cannot analyze the review sentiment or extract keywords. \n\nFor that reason, the if node is set to if reviewText = not empty (TRUE), it goes onto analysis by GPT-4. But, if reviewText = not empty (FALSE), it goes into a separate sheet called 'failed reviews'. "
      },
      "typeVersion": 1
    },
    {
      "id": "e10d0516-893e-48e5-a864-faba09ea2381",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        420,
        -700
      ],
      "parameters": {
        "width": 520,
        "height": 760,
        "content": "## 3. Analysis and Export\n\nThe GPT-4 summarizes the review, analyzes the sentiment, and extracts keywords from each. All of this information is exported into a Google Sheets. "
      },
      "typeVersion": 1
    },
    {
      "id": "17d4fe34-5930-4d5e-b7e0-0e57e86c8f82",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -780,
        -1100
      ],
      "parameters": {
        "width": 580,
        "height": 320,
        "content": "## Step 1 -> Sample Input\n\nHere\u2019s what the Google Sheets input looks like:\n\n![Review Summary](https://i.imgur.com/nzu5N6k.png)\n"
      },
      "typeVersion": 1
    },
    {
      "id": "ccdc7228-ba6c-4194-90e4-93273c4d4e4f",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1060,
        -700
      ],
      "parameters": {
        "width": 660,
        "height": 360,
        "content": "## Step 3 -> Example Output\n\nThis screenshot shows the output as shown in Google Sheets\n\n![Google Sheets Output](https://i.imgur.com/Dtg5AFc.png)\n"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "1cb71d95-1b82-487d-97b5-12c38d4afb20",
  "connections": {
    "Get Data": {
      "main": [
        [
          {
            "node": "Cleans It Up",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Export Data": {
      "main": [
        []
      ]
    },
    "Cleans It Up": {
      "main": [
        [
          {
            "node": "If review text is NOT empty ",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Analyze Review Sentiment": {
      "main": [
        [
          {
            "node": "Export Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Pull Sample Restaurants ": {
      "main": [
        [
          {
            "node": "Get Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If review text is NOT empty ": {
      "main": [
        [
          {
            "node": "Analyze Review Sentiment",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Failed Reviews (e.g. review text = empty)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When clicking \u2018Test workflow\u2019": {
      "main": [
        [
          {
            "node": "Pull Sample Restaurants ",
            "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 n8n template helps you analyze Google Maps reviews for a list of restaurants, summarize them with AI, and identify optimization opportunities—all in one automated workflow. Whether you're managing multiple locations, helping local restaurants improve their digital presence,…

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

More AI & RAG workflows → · Browse all categories →

Related workflows

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

AI & RAG

Clone_Viral_TikToks_with_AI_Avatars___Auto_Post_to_9_Platforms_using_Perplexity___Blotato. Uses httpRequest, telegramTrigger, openAi, googleSheets. Event-driven trigger; 42 nodes.

HTTP Request, Telegram Trigger, OpenAI +2
AI & RAG

1-Clone_Viral_TikToks_with_AI_Avatars___Auto_Post_to_9_Platforms_using_Perplexity___Blotato. Uses httpRequest, telegramTrigger, openAi, googleSheets. Event-driven trigger; 42 nodes.

HTTP Request, Telegram Trigger, OpenAI +2
AI & RAG

1-Clone_Viral_TikToks_with_AI_Avatars___Auto_Post_to_9_Platforms_using_Perplexity___Blotato. Uses httpRequest, telegramTrigger, openAi, googleSheets. Event-driven trigger; 42 nodes.

HTTP Request, Telegram Trigger, OpenAI +2
AI & RAG

💥Clone a viral TikTok and auto-post it to 9 platforms using Perplexity & Blotato vide. Uses httpRequest, telegramTrigger, openAi, googleSheets. Event-driven trigger; 41 nodes.

HTTP Request, Telegram Trigger, OpenAI +2
AI & RAG

💥Clone a viral TikTok and auto-post it to 9 platforms using Perplexity & Blotato vide. Uses httpRequest, telegramTrigger, openAi, googleSheets. Event-driven trigger; 41 nodes.

HTTP Request, Telegram Trigger, OpenAI +2