AutomationFlowsAI & RAG › Personalized Outreach for Lawyers with Linkedin Scraping, Gpt-4o, Google Sheets

Personalized Outreach for Lawyers with Linkedin Scraping, Gpt-4o, Google Sheets

ByIbrahim Malick @imalick on n8n.io

⚠️ This template uses only official n8n nodes. No community nodes required.

Cron / scheduled trigger★★★★☆ complexityAI-powered11 nodesGoogle SheetsOpenRouter ChatAgentHTTP RequestOpenAI
AI & RAG Trigger: Cron / scheduled Nodes: 11 Complexity: ★★★★☆ AI nodes: yes Added:

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

This workflow follows the Agent → 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
{
  "id": "3gnnZ2YIkY7KtWGV",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Linkedin Leads Automation",
  "tags": [
    {
      "id": "3wP6uU6SFbrRYs5q",
      "name": "youtube",
      "createdAt": "2025-05-17T22:21:11.653Z",
      "updatedAt": "2025-05-17T22:21:11.653Z"
    }
  ],
  "nodes": [
    {
      "id": "62e62e1f-e71d-4869-bceb-370e4ab853e2",
      "name": "Set Fields",
      "type": "n8n-nodes-base.set",
      "position": [
        -1280,
        80
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "cc27b2d9-8de7-43ca-a741-2d150084f78e",
              "name": "currentStartIndex",
              "type": "number",
              "value": 1
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "c762fb3b-5574-4a25-ac2a-aa6f007bf79e",
      "name": "Add to Google",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        0,
        0
      ],
      "parameters": {
        "columns": {
          "value": {
            "Name": "={{ $json.name }}",
            "title": "={{ $json.position }}",
            "profile_url": "={{ $json.link }}"
          },
          "schema": [
            {
              "id": "Name",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "title",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "title",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "profile_url",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "profile_url",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "about",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "about",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "outreach message 1",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "outreach message 1",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1jajDVL6_sMlrK6cQNkKPqPpUVOPUst_A7hyMg0cuyrM/edit#gid=0",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1jajDVL6_sMlrK6cQNkKPqPpUVOPUst_A7hyMg0cuyrM",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1jajDVL6_sMlrK6cQNkKPqPpUVOPUst_A7hyMg0cuyrM/edit?usp=drivesdk",
          "cachedResultName": "linkedin"
        }
      },
      "credentials": {},
      "typeVersion": 4.5
    },
    {
      "id": "b9ce466d-8080-40ec-8f3d-99e446ddee54",
      "name": "OpenRouter Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "position": [
        260,
        180
      ],
      "parameters": {
        "model": "perplexity/sonar-pro",
        "options": {}
      },
      "credentials": {},
      "typeVersion": 1
    },
    {
      "id": "9451edb8-a7a6-4840-9eea-e799f4bf0721",
      "name": "Research Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        280,
        0
      ],
      "parameters": {
        "text": "=Name: {{ $json.name }}\nTitle: {{ $json.title }}\nAbout: {{ $json.about }}",
        "options": {
          "systemMessage": "Your job is to research this lawyer or law firm founder as much as possible. The goal is to find relevant public information to help generate personalized outreach. This workflow is targeting solo practice lawyers and small law firms \u2014 not large firms or government attorneys. Avoid irrelevant sources. Focus on anything that reflects their legal focus (e.g., family law, criminal defense, small business law), local community involvement, or their firm\u2019s branding. Include source references (e.g., LinkedIn posts, website articles, etc.). No intros or conclusions."
        },
        "promptType": "define"
      },
      "typeVersion": 1.7
    },
    {
      "id": "b5855a64-d62c-401e-8596-99888ff3a8b6",
      "name": "Google Search",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -780,
        80
      ],
      "parameters": {
        "url": "https://www.googleapis.com/customsearch/v1",
        "options": {},
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "key",
              "value": "YOUR_API_KEY_OR_CX_ID"
            },
            {
              "name": "cx",
              "value": "YOUR_API_KEY_OR_CX_ID"
            },
            {
              "name": "q",
              "value": "(\"solo practitioner\" OR \"founder\" OR \"managing partner\") lawyer \"law firm\" site:linkedin.com/in -government -gov -biglaw"
            },
            {
              "name": "start",
              "value": "={{ $json.startIndex || 1 }}"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "f36fb9ae-dcd3-44dd-8096-5ae6efd0f83b",
      "name": "Results ",
      "type": "n8n-nodes-base.code",
      "position": [
        -440,
        80
      ],
      "parameters": {
        "jsCode": "// Get the response data\nconst response = $input.first().json;\nconst items = response.items || [];\n\n// Track pagination info\nlet nextStartIndex = 1;\nif (response.queries && response.queries.nextPage && response.queries.nextPage[0]) {\n  nextStartIndex = response.queries.nextPage[0].startIndex;\n}\n\n// Calculate if we should continue (Google only allows up to 100 results)\nconst hasMoreResults = nextStartIndex <= 100;\n\n// Process the items and include pagination info in each item\nconst results = items.map(item => {\n  const titleParts = item.title.split(\" - \");\n  return {\n    name: titleParts[0] || null,\n    position: titleParts.slice(1).join(\" - \") || null,\n    link: item.link || null,\n    about: item.snippet || null,\n    image: item.pagemap?.cse_thumbnail?.[0]?.src || null,\n    // Add pagination info to each item\n    startIndex: nextStartIndex,\n    hasMoreResults: hasMoreResults\n  };\n});\n\n// If there are no results, return at least one item with pagination info\nif (results.length === 0) {\n  return [{ \n    json: {\n      name: null,\n      position: null,\n      link: null,\n      about: null,\n      image: null,\n      startIndex: nextStartIndex,\n      hasMoreResults: false\n    }\n  }];\n}\n\n// Return the processed results\nreturn results.map(r => ({ json: r }));\n"
      },
      "typeVersion": 2
    },
    {
      "id": "e3cbad5a-2022-4c67-bde1-87ac77687539",
      "name": "Pages",
      "type": "n8n-nodes-base.code",
      "position": [
        0,
        200
      ],
      "parameters": {
        "jsCode": "// Get all input items\nconst inputItems = $input.all();\nconsole.log(\"Input items count:\", inputItems.length);\n\n// Extract pagination values with correct field names\nlet nextStartIndex = 1;\nlet hasMoreResults = false;\n\nif (inputItems && inputItems.length > 0) {\n  const firstItem = inputItems[0];\n\n  if (firstItem.json) {\n    console.log(\"First item JSON:\", JSON.stringify(firstItem.json));\n\n    // Look for \"startIndex\" instead of \"index\"\n    if (firstItem.json.startIndex !== undefined) {\n      nextStartIndex = firstItem.json.startIndex;\n      console.log(\"Found startIndex:\", nextStartIndex);\n    }\n\n    // Look for \"hasMoreResults\" instead of \"results\"\n    if (firstItem.json.hasMoreResults !== undefined) {\n      hasMoreResults = firstItem.json.hasMoreResults;\n      console.log(\"Found hasMoreResults:\", hasMoreResults);\n    }\n  }\n}\n\n// Return pagination control info\nreturn {\n  json: {\n    continueLoop: hasMoreResults,\n    startIndex: nextStartIndex\n  }\n};\n"
      },
      "typeVersion": 2
    },
    {
      "id": "94b87b0b-0bff-4e13-a710-b2f2b9702cd9",
      "name": "Pages Check",
      "type": "n8n-nodes-base.if",
      "position": [
        -1040,
        200
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "faef2862-80a4-465b-9e0b-be5b9753dcbd",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $('Pages').item.json.continueLoop }}",
              "rightValue": "true"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "e41547c3-ed14-4882-906a-d0005f85ca4b",
      "name": "Google Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1340,
        0
      ],
      "parameters": {
        "columns": {
          "value": {
            "name": "={{ $('Results ').all()[ $itemIndex ].json.name }}",
            "outreach message 1": "={{ $json.message.content }}"
          },
          "schema": [
            {
              "id": "name",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "title",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "title",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "profile_url",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "profile_url",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "about",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "about",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "outreach message 1",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "outreach message 1",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "name"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1smDBk1Eh50sRyTEzEhzWkDnaWaexZHiHTxYlp2M6_J0/edit#gid=0",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1smDBk1Eh50sRyTEzEhzWkDnaWaexZHiHTxYlp2M6_J0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1smDBk1Eh50sRyTEzEhzWkDnaWaexZHiHTxYlp2M6_J0/edit?usp=drivesdk",
          "cachedResultName": "linkedin"
        }
      },
      "credentials": {},
      "typeVersion": 4.5
    },
    {
      "id": "5dccf520-fe74-458a-8e9d-34bc69cd5f54",
      "name": "Outreach Agent",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        700,
        0
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o",
          "cachedResultName": "GPT-4O"
        },
        "options": {},
        "messages": {
          "values": [
            {
              "role": "system",
              "content": "Your job is to create a short, personalized outreach icebreaker for solo lawyers or small law firm owners. Keep it genuine, professional, and brief \u2014 one line max. You may refer to their practice area, years in practice, recent content, community involvement, or anything that reflects their personal brand. Avoid generic statements. Do not ask about location. Do not mention large law firms or government service. Use a business-casual tone, and start with: \"Hey [name] ...\""
            },
            {
              "content": "=Name: {{ $('Results ').all()[ $itemIndex ].json.name }}\nPosition: {{ $('Results ').all()[ $itemIndex ].json.position }}\nAbout: {{ $('Results ').all()[ $itemIndex ].json.about }}\n"
            }
          ]
        }
      },
      "credentials": {},
      "typeVersion": 1.8
    },
    {
      "id": "8340fc87-c844-4861-a4ff-75f7da7274a3",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -1480,
        80
      ],
      "parameters": {
        "rule": {
          "interval": [
            {}
          ]
        }
      },
      "typeVersion": 1.2
    }
  ],
  "active": true,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "69a6fdbc-0653-4c7a-8258-17146dd20530",
  "connections": {
    "Pages": {
      "main": [
        [
          {
            "node": "Pages Check",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Results ": {
      "main": [
        [
          {
            "node": "Add to Google",
            "type": "main",
            "index": 0
          },
          {
            "node": "Pages",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Fields": {
      "main": [
        [
          {
            "node": "Google Search",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Pages Check": {
      "main": [
        [
          {
            "node": "Google Search",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Add to Google": {
      "main": [
        [
          {
            "node": "Research Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Search": {
      "main": [
        [
          {
            "node": "Results ",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Outreach Agent": {
      "main": [
        [
          {
            "node": "Google Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Research Agent": {
      "main": [
        [
          {
            "node": "Outreach Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Set Fields",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenRouter Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "Research Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    }
  }
}
Pro

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

About this workflow

⚠️ This template uses only official n8n nodes. No community nodes required.

Source: https://n8n.io/workflows/4879/ — 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

This n8n workflow turns a script and character/setting description from Google Sheets into a complete stitched UGC-style video ad, fully automated from intake to final delivery.

Google Sheets, HTTP Request, OpenRouter Chat +7
AI & RAG

This workflow automates the creation, rendering, approval, and posting of TikTok-style POV (Point of View) videos to Instagram, with cross-posting to Facebook and YouTube. It eliminates manual video p

OpenAI Chat, Output Parser Item List, HTTP Request +10
AI & RAG

Complete PostgreSQL-backed system: Keyword scoring → AI research → Multi-part content generation → fal.ai Nano Banana image generation → WordPress publishing

WordPress, OpenAI, Perplexity +8
AI & RAG

This workflow is designed for: Content creators and marketers E-commerce and product-based businesses Agencies producing social media visuals and videos Automation builders looking for AI-powered crea

HTTP Request, Edit Image, Google Drive +7
AI & RAG

Generate product images with NanoBanana Pro to Veo videos and Blotato - vide 2 ok. Uses httpRequest, editImage, googleDrive, googleSheets. Scheduled trigger; 76 nodes.

HTTP Request, Edit Image, Google Drive +7