AutomationFlowsGeneral › Analyze AI Overview from SERP Results

Analyze AI Overview from SERP Results

Original n8n title: Analyze AI Overview

Analyze AI Overview. Uses executeWorkflowTrigger, httpRequest. Event-driven trigger; 10 nodes.

Event trigger★★★★☆ complexity10 nodesExecute Workflow TriggerHTTP Request
General Trigger: Event Nodes: 10 Complexity: ★★★★☆ Added:

This workflow follows the Execute Workflow Trigger → 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": "Analyze AI Overview",
  "nodes": [
    {
      "parameters": {
        "workflowInputs": {
          "values": [
            {
              "name": "gl"
            },
            {
              "name": "q"
            },
            {
              "name": "location"
            },
            {
              "name": "hl"
            }
          ]
        }
      },
      "type": "n8n-nodes-base.executeWorkflowTrigger",
      "typeVersion": 1.1,
      "position": [
        -624,
        320
      ],
      "id": "1fadcfb7-077e-43f2-810c-27be1c0f6575",
      "name": "When Executed by Another Workflow"
    },
    {
      "parameters": {
        "url": "https://serpapi.com/search",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "serpApi",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "gl",
              "value": "={{ $json.gl }}"
            },
            {
              "name": "q",
              "value": "={{ $json.q }}"
            },
            {
              "name": "location",
              "value": "={{ $json.location }}"
            },
            {
              "name": "hl",
              "value": "={{ $json.hl }}"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        -400,
        320
      ],
      "id": "a66d8866-4e88-4216-8de2-d78bda030c61",
      "name": "GET SERP Results",
      "retryOnFail": true,
      "waitBetweenTries": 5000,
      "credentials": {
        "serpApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "d8cbb599-3ec7-446b-be55-9f48e21b4d11",
              "name": "=serpapi_link",
              "value": "={{ $json.ai_overview.serpapi_link }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        -176,
        224
      ],
      "id": "57e716b3-86a8-4099-857c-9cdc62d1fbb6",
      "name": "Extract AI Overview Link"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "15ccf2c6-185c-4166-9efb-b899eab4944e",
              "leftValue": "={{ $json.serpapi_link }}",
              "rightValue": "",
              "operator": {
                "type": "string",
                "operation": "exists",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        48,
        224
      ],
      "id": "eb887e5d-9c54-4177-b7c2-c1edf0a5a33a",
      "name": "Is Extra Request Required"
    },
    {
      "parameters": {
        "url": "={{ $json.serpapi_link }}",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "serpApi",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        272,
        176
      ],
      "id": "cc6d4f00-748f-4e53-ac7e-55b838f8511c",
      "name": "GET AI Overview",
      "credentials": {
        "serpApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "6dc1587a-9266-43ac-8b0f-c31ce0a1dd2f",
              "name": "ai_overview",
              "value": "={{ $json.ai_overview }}",
              "type": "object"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        496,
        176
      ],
      "id": "2fa31f44-37ed-44df-a515-ef058b6c8094",
      "name": "Get AI Overview Content"
    },
    {
      "parameters": {
        "mode": "combine",
        "combineBy": "combineAll",
        "options": {}
      },
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3.2,
      "position": [
        688,
        320
      ],
      "id": "4834b8ff-94ee-4e9a-b000-776204e00194",
      "name": "Merge Results"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "8d52f2cf-85e3-4c78-aa61-e3a05db8b718",
              "name": "ai_overview",
              "value": "={{ $json.ai_overview }}",
              "type": "object"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        864,
        320
      ],
      "id": "a74e565d-e3da-48b7-9279-8f23a7fde281",
      "name": "Get AI Overview"
    },
    {
      "parameters": {
        "jsCode": "const inputData = items[0].json;\n\n// 1. Top-Level Validation: Check for errors or missing overview data first.\nif (inputData?.ai_overview?.error || !inputData?.ai_overview?.text_blocks) {\n  return [{\n    json: {\n      categories: [],\n      mentionedEntities: [],\n      dominantSources: [],\n      aiOverview: inputData.ai_overview || {}, // Return the object even if it just contains an error\n      parsingError: inputData?.ai_overview?.error || \"No text_blocks found in the AI Overview.\"\n    }\n  }];\n}\n\nconst aiOverview = inputData.ai_overview;\nconst allReferences = aiOverview.references || [];\n\n// 2. Initialize Data Containers\nconst categories = new Set();\nconst mentionedEntities = new Set();\nconst referenceCounts = {};\n\n// --- Recursive Parsing Engine ---\n\n/**\n * Recursively processes an array of text_blocks to extract data.\n * This function can call itself to handle deeply nested structures.\n * @param {Array} blocks - An array of text_block objects.\n */\nfunction parseTextBlocks(blocks) {\n  if (!blocks || !Array.isArray(blocks)) {\n    return;\n  }\n\n  for (let i = 0; i < blocks.length; i++) {\n    const block = blocks[i];\n    if (!block) continue;\n\n    // A. Aggregate all reference indexes found at any level\n    if (block.reference_indexes) {\n      block.reference_indexes.forEach(index => {\n        referenceCounts[index] = (referenceCounts[index] || 0) + 1;\n      });\n    }\n\n    // B. Process Blocks by Type\n    switch (block.type) {\n      case 'heading':\n        if (block.snippet) categories.add(block.snippet.trim());\n        break;\n\n      case 'expandable':\n        if (block.title) categories.add(block.title.trim());\n        parseTextBlocks(block.text_blocks);\n        break;\n\n      case 'paragraph':\n        const nextBlock = blocks[i + 1];\n        if (nextBlock && ['list', 'table', 'comparison'].includes(nextBlock.type)) {\n          if (block.snippet) categories.add(block.snippet.trim());\n        }\n        break;\n\n      case 'list':\n        if (block.list) {\n          block.list.forEach(item => {\n            let entityName = '';\n            if (item.title) {\n              entityName = item.title.replace(':', '').trim();\n            } else if (item.snippet) {\n              entityName = item.snippet.split(/[:.]/)[0].trim();\n            }\n            if (entityName) mentionedEntities.add(entityName);\n\n            if (item.reference_indexes) {\n              item.reference_indexes.forEach(index => {\n                referenceCounts[index] = (referenceCounts[index] || 0) + 1;\n              });\n            }\n            \n            if (item.text_blocks) {\n              parseTextBlocks(item.text_blocks);\n            }\n          });\n        }\n        break;\n\n      case 'comparison':\n        if (block.product_labels) {\n          block.product_labels.forEach(label => mentionedEntities.add(label.trim()));\n        }\n        break;\n        \n      case 'table':\n        if (block.table?.rows?.length > 0) {\n            const firstHeader = block.table.headers?.[0];\n            if (firstHeader) {\n                for (const row of block.table.rows) {\n                    if (row[firstHeader]) {\n                        mentionedEntities.add(row[firstHeader].trim());\n                    }\n                }\n            }\n        }\n        break;\n    }\n  }\n}\n\n// 3. Initial Call to the Recursive Parser\nparseTextBlocks(aiOverview.text_blocks);\n\n// 4. Process and Sort Source Authority\nconst dominantSources = Object.entries(referenceCounts)\n  .map(([index, citations]) => {\n    const refDetails = allReferences.find(ref => ref.index == index);\n    return {\n      source: refDetails?.source || 'Unknown',\n      title: refDetails?.title || 'Unknown',\n      link: refDetails?.link || 'Unknown',\n      citations: citations,\n      index: parseInt(index)\n    };\n  })\n  .sort((a, b) => b.citations - a.citations);\n\n// --- NEW: Clean and Prepare the Original AI Overview ---\n// Create a deep copy to avoid modifying the original input object.\nconst cleanedOverview = JSON.parse(JSON.stringify(aiOverview));\n\n// Remove token-consuming fields that are not needed by the agent for its analysis.\ndelete cleanedOverview.page_token;\ndelete cleanedOverview.serpapi_link;\n\n// 5. Assemble and Return the Final, Comprehensive Data Object\nconst preparedData = {\n  categories: Array.from(categories),\n  mentionedEntities: Array.from(mentionedEntities),\n  dominantSources: dominantSources,\n  aiOverview: cleanedOverview // The cleaned, original overview is now included.\n};\n\nreturn [{\n  json: preparedData\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1056,
        320
      ],
      "id": "86f97f90-7e95-4f50-a1a9-9aa0b2517f9e",
      "name": "Analyze AI Overview",
      "onError": "continueErrorOutput"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "3637b044-e944-49d1-a983-d2103c499100",
              "name": "message",
              "value": "={{ $json.error }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1264,
        416
      ],
      "id": "f8aa096d-2ee4-4588-994d-d87bef412bc3",
      "name": "Return Error Message"
    }
  ],
  "connections": {
    "When Executed by Another Workflow": {
      "main": [
        [
          {
            "node": "GET SERP Results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "GET SERP Results": {
      "main": [
        [
          {
            "node": "Extract AI Overview Link",
            "type": "main",
            "index": 0
          },
          {
            "node": "Merge Results",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Extract AI Overview Link": {
      "main": [
        [
          {
            "node": "Is Extra Request Required",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Is Extra Request Required": {
      "main": [
        [
          {
            "node": "GET AI Overview",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Merge Results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "GET AI Overview": {
      "main": [
        [
          {
            "node": "Get AI Overview Content",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get AI Overview Content": {
      "main": [
        [
          {
            "node": "Merge Results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge Results": {
      "main": [
        [
          {
            "node": "Get AI Overview",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get AI Overview": {
      "main": [
        [
          {
            "node": "Analyze AI Overview",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Analyze AI Overview": {
      "main": [
        [],
        [
          {
            "node": "Return Error Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "0e1cf286-be3b-4818-aee8-93589437147b",
  "id": "4ViOjawp2E5PbJsu",
  "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

How this works

This workflow delivers instant analysis of AI-generated search overviews, helping content creators, researchers, and marketers quickly assess how search engines summarise their topics without sifting through endless results. It fetches SERP data via HTTP requests and extracts the AI overview content, providing a concise breakdown of key insights to inform your strategy or refine your queries. Ideal for those relying on tools like Google Search, the core step involves conditionally pulling the full overview text to merge it seamlessly with initial search results.

Use this workflow when you need rapid, event-driven insights into AI overviews for competitive analysis or trend spotting, such as evaluating search prominence for new articles. Avoid it for non-search-related tasks or when real-time data isn't essential, as it focuses solely on SERP extraction. Common variations include adding nodes to integrate with email notifications for alerts or adapting the HTTP requests for Bing instead of Google SERP.

About this workflow

Analyze AI Overview. Uses executeWorkflowTrigger, httpRequest. Event-driven trigger; 10 nodes.

Source: https://github.com/Marvomatic/n8n-templates/blob/main/ai-overview-analyzer/tool-analyze-ai-overview.json — original creator credit. Request a take-down →

More General workflows → · Browse all categories →

Related workflows

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

General

Reputation Engine — Site Refresh. Uses httpRequest, executeWorkflowTrigger. Event-driven trigger; 35 nodes.

HTTP Request, Execute Workflow Trigger
General

PRECALL. Uses executeWorkflowTrigger, httpRequest. Event-driven trigger; 23 nodes.

Execute Workflow Trigger, HTTP Request
General

Search-Criteria. Uses noOp, stopAndError, itemLists, executeWorkflowTrigger. Event-driven trigger; 14 nodes.

Stop And Error, Item Lists, Execute Workflow Trigger +1
General

Absen Otomatis. Uses ssh, executeWorkflowTrigger, scheduleTrigger, httpRequest. Event-driven trigger; 12 nodes.

Ssh, Execute Workflow Trigger, HTTP Request
General

Kb Tool Confluence Knowledge Base. Uses executeWorkflowTrigger, httpRequest, stickyNote. Event-driven trigger; 7 nodes.

Execute Workflow Trigger, HTTP Request