Apify + Google Sheets: LinkedIn leads, ready drafts
Your lead list looks fine. Then you open the LinkedIn URLs and reality hits: every “quick” prospect check turns into 10 tabs, messy notes, and a half-written email you’ll probably never send. This is where Apify lead enrichment pays for itself.
SDRs feel it in their daily activity targets. Recruiters feel it when a “simple outreach” becomes an hour of profile digging. And founders doing their own sales? Honestly, they feel it the worst because it steals focus from everything else.
This workflow takes LinkedIn URLs from Google Sheets, enriches each profile with Apify, then uses Gemini to generate a subject line, a sharp opening hook, and a personalized email draft. You’ll learn what it automates, what you need, and how to run it reliably.
How This Automation Works
Here’s the complete workflow you’ll be setting up:
n8n Workflow Template: Apify + Google Sheets: LinkedIn leads, ready drafts
flowchart LR
subgraph sg0["Schedule Flow"]
direction LR
n0@{ icon: "mdi:database", form: "rounded", label: "Get row(s) in sheet", pos: "b", h: 48 }
n1@{ icon: "mdi:cog", form: "rounded", label: "Limit", pos: "b", h: 48 }
n2@{ icon: "mdi:swap-horizontal", form: "rounded", label: "Get dataset items", pos: "b", h: 48 }
n3@{ icon: "mdi:database", form: "rounded", label: "Append or update row in sheet", pos: "b", h: 48 }
n4@{ icon: "mdi:database", form: "rounded", label: "Get row(s) in sheet1", pos: "b", h: 48 }
n5@{ icon: "mdi:robot", form: "rounded", label: "Structured Output Parser", pos: "b", h: 48 }
n6@{ icon: "mdi:brain", form: "rounded", label: "Google Gemini Chat Model", pos: "b", h: 48 }
n7@{ icon: "mdi:play-circle", form: "rounded", label: "Schedule Trigger", pos: "b", h: 48 }
n8@{ icon: "mdi:swap-horizontal", form: "rounded", label: "Check Profiles with no Data", pos: "b", h: 48 }
n9@{ icon: "mdi:swap-horizontal", form: "rounded", label: "Runs Profile Extraction Actor", pos: "b", h: 48 }
n10@{ icon: "mdi:swap-horizontal", form: "rounded", label: "Check Profiles with No Email..", pos: "b", h: 48 }
n11@{ icon: "mdi:robot", form: "rounded", label: "Hyper Personalised Email Gen..", pos: "b", h: 48 }
n12@{ icon: "mdi:database", form: "rounded", label: "Final Output", pos: "b", h: 48 }
n1 --> n9
n7 --> n0
n2 --> n3
n0 --> n8
n4 --> n10
n6 -.-> n11
n5 -.-> n11
n8 --> n1
n3 --> n4
n9 --> n2
n11 --> n12
n10 --> n11
end
%% Styling
classDef trigger fill:#e8f5e9,stroke:#388e3c,stroke-width:2px
classDef ai fill:#e3f2fd,stroke:#1976d2,stroke-width:2px
classDef aiModel fill:#e8eaf6,stroke:#3f51b5,stroke-width:2px
classDef decision fill:#fff8e1,stroke:#f9a825,stroke-width:2px
classDef database fill:#fce4ec,stroke:#c2185b,stroke-width:2px
classDef api fill:#fff3e0,stroke:#e65100,stroke-width:2px
classDef code fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px
classDef disabled stroke-dasharray: 5 5,opacity: 0.5
class n7 trigger
class n5,n11 ai
class n6 aiModel
class n2,n8,n9,n10 decision
class n0,n3,n4,n12 database
Why This Matters: LinkedIn research is a time sink
Personalized outreach is one of those things everyone agrees is “important” right up until it’s time to do it at scale. A single LinkedIn profile can take a few minutes to scan, but the real cost is the switching: open the link, skim the About section, scroll experience, pull out one relevant detail, paste notes somewhere, then try to turn that into a first line that doesn’t sound like a template. Do that for 30 leads and you’ve burned most of your morning. And if two people on your team research the same lead on different days, you’re paying twice for the same work.
It adds up fast. The usual cracks show up in the same places every time:
- Lead research lives in browser tabs and half-finished docs, so nothing is reusable next week.
- Manual copy-paste into a CRM or spreadsheet invites small errors that break follow-ups later.
- Outreach quality becomes inconsistent because you write under time pressure, not with good context.
- You end up sending “personalized” emails that reference generic facts, which hurts replies.
What You’ll Build: LinkedIn enrichment + email drafts inside Sheets
This workflow runs on a schedule and starts by pulling rows from your Google Sheet that contain LinkedIn profile URLs. It filters out leads you’ve already enriched, then sends each unprocessed URL to an Apify actor that extracts the profile’s key sections, including the About summary and detailed work experience. That enriched profile data gets written back into your Sheet so your research is stored right next to the lead. Next, the workflow reloads the Sheet, finds leads that still don’t have a draft, and sends the full profile context into a Gemini chat model through LangChain. Gemini is prompted to act like an experienced B2B copywriter, pulling a real “nugget” from the person’s background and turning it into a subject line and email body you can review and send.
The workflow starts with a scheduled run, then moves through enrichment in Apify, then generation in Gemini. Finally, everything lands back in Google Sheets so you can QA quickly, edit lightly, and push to your outreach tool when you’re ready.
What You’re Building
| What Gets Automated | What You’ll Achieve |
|---|---|
|
|
Expected Results
Say you add 30 new LinkedIn URLs to your Sheet each week. Manually, it’s easy to spend about 6 minutes per lead reading the profile, grabbing notes, and writing a first draft, which is roughly 3 hours weekly. With this workflow, you spend about 10 minutes setting up the sheet and checking outputs, then let the scheduled run process the batch in the background. You still review before sending, but the blank-page work is gone.
Before You Start
- n8n instance (try n8n Cloud free)
- Self-hosting option if you prefer (Hostinger works well)
- Google Sheets for your lead list and draft storage.
- Apify to scrape and enrich LinkedIn profiles.
- Google Gemini API key (get it from Google Cloud after enabling Vertex AI).
Skill level: Beginner. You’ll connect accounts, map a few fields, and test with a small batch.
Want someone to build this for you? Talk to an automation expert (free 15-minute consultation).
Step by Step
A scheduled run kicks things off. You decide the interval in n8n, then the workflow automatically fetches your latest rows from Google Sheets.
Leads get filtered and batched. An “if” check skips rows that already have enrichment, and a batch cap prevents you from accidentally scraping hundreds of profiles in one go.
Apify enriches each LinkedIn profile. The workflow launches the profile scraper actor, retrieves the dataset records, then writes the profile details into your “Profile Data” column back in Google Sheets.
Gemini writes outreach drafts using the enriched context. The workflow reloads the sheet, selects leads missing drafts, then uses LangChain with the Gemini chat model plus a structured parser so the output reliably contains a subject and an email body.
You can easily modify the schedule frequency and the batch size based on your list volume. See the full implementation guide below for customization options.
Step-by-Step Implementation Guide
Step 1: Configure the Schedule Trigger
Set the workflow to run on a recurring schedule using the built-in trigger.
- Add and open Scheduled Run Initiator.
- Set the schedule rule to run every 2 minutes by configuring Rule → Interval with Field as
minutesand Minutes Interval as2. - Verify the connection from Scheduled Run Initiator to Fetch Sheet Rows.
Step 2: Connect Google Sheets
Configure the spreadsheet sources and make sure all Google Sheets nodes point to the same spreadsheet and sheet.
- Open Fetch Sheet Rows and set Document ID to
[YOUR_ID]and Sheet Name to[YOUR_ID]. - Repeat the same Document ID and Sheet Name values in Write Profile Enrichment, Reload Sheet Entries, and Store Email Drafts.
- Credential Required: Connect your Google Sheets credentials in Fetch Sheet Rows, Write Profile Enrichment, Reload Sheet Entries, and Store Email Drafts (credentials are not preconfigured).
- Optionally keep Flowpast Branding as a reference note in your canvas; it does not affect execution.
Step 3: Set Up Profile Filtering and Enrichment
Filter records that need enrichment, cap the batch size, scrape LinkedIn profiles, and write enrichment back to the sheet.
- In Filter Empty Profiles, confirm the two conditions are set to empty: Profile Data is empty using
{{ $json["Profile Data"] }}, and Subject is empty using{{ $json.Subject }}. - Open Cap Batch Size and set a limit if you want to restrict the number of profiles processed per run.
- In Launch Profile Scraper, set Custom Body to
{ "includeEmail": false, "username": "{{ $json.LinkedIn }}" }. - Credential Required: Connect your Apify credentials in Launch Profile Scraper and Retrieve Dataset Records (credentials are not preconfigured).
- In Retrieve Dataset Records, set Resource to
Datasetsand Dataset ID to{{ $json.defaultDatasetId }}. - In Write Profile Enrichment, keep Operation set to
appendOrUpdateand map fields: LinkedIn to{{ $('Cap Batch Size').item.json.LinkedIn }}and Profile Data to the multi-line expression shown in the node.
username values.Step 4: Set Up AI Email Drafting
Use the LLM chain to generate structured email drafts from enriched profile data.
- Open Select Missing Drafts and confirm it filters for records where Profile Data is not empty and both Subject and Email Body are empty using
{{ $json["Profile Data"] }},{{ $json.Subject }}, and{{ $json["Email Body"] }}. - In Compose Outreach Email, keep Prompt Type set to
defineand leave the full prompt text as provided. - Ensure Compose Outreach Email has Gemini Chat Engine connected as the language model and Structured Response Parser connected as the output parser.
- Credential Required: Connect your Google Gemini credentials in Gemini Chat Engine (credentials are not preconfigured).
- Note: Structured Response Parser is a sub-node; add credentials to the parent Gemini Chat Engine, not the parser.
Step 5: Configure Output and Data Persistence
Store the generated subject lines and email bodies back into Google Sheets.
- Open Store Email Drafts and keep Operation set to
appendOrUpdate. - Map Subject to
{{ $json.output.subject }}, Email Body to{{ $json.output.email_body }}, and LinkedIn to{{ $('Select Missing Drafts').item.json.LinkedIn }}. - Confirm the node connects from Compose Outreach Email to Store Email Drafts in the main flow.
Step 6: Test & Activate
Validate the workflow with a manual run before enabling the schedule.
- Click Execute Workflow and watch data move from Fetch Sheet Rows through Write Profile Enrichment and Store Email Drafts.
- Confirm that profiles missing data are enriched, and new subject/email body values are appended in the sheet.
- If execution succeeds, toggle the workflow to Active to allow Scheduled Run Initiator to run every 2 minutes.
Troubleshooting Tips
- Google Sheets credentials can expire or need specific permissions. If things break, check the connected Google account access inside n8n’s Credentials first.
- If Apify scraping returns an empty dataset, it’s often a run still in progress or a throttled scrape. Re-run with a smaller batch size and confirm the Apify run status in your Apify console.
- Default prompts in Gemini are generic. Add your brand voice early or you’ll be editing outputs forever.
Quick Answers
About 30 minutes if your accounts and Sheet columns are ready.
No. You’ll mostly connect accounts, pick your Sheet, and map where outputs should be written.
Yes. n8n has a free self-hosted option and a free trial on n8n Cloud. Cloud plans start at $20/month for higher volume. You’ll also need to factor in Apify runs and Gemini API usage.
Two options: n8n Cloud (managed, easiest setup) or self-hosting on a VPS. For self-hosting, Hostinger VPS is affordable and handles n8n well. Self-hosting gives you unlimited executions but requires basic server management.
Yes, and you should. You can swap the schedule frequency in the Scheduled Run Initiator, change the batch size in Cap Batch Size, and adjust what gets written into Sheets in Write Profile Enrichment and Store Email Drafts. Common tweaks include generating LinkedIn connection requests instead of emails, writing different drafts for different campaigns, or adding a “Do Not Contact” flag that the If filters can respect.
Usually it’s an expired token or the actor permissions in your Apify account. Reconnect the Apify credential in n8n, then confirm the actor run shows up in Apify and produces a dataset. If the run completes but records are empty, the LinkedIn URL format in your Sheet is often the culprit (extra tracking params can break scrapers). Rate limits can also bite when you push large batches.
If you self-host n8n there’s no execution cap, so volume mostly depends on your server, Apify limits, and how large your batch size is. On n8n Cloud, the limit depends on your plan’s monthly executions. Practically, most teams run this in small batches (like 20–100 leads) to keep scraping stable and drafts easy to review.
Often, yes, because this isn’t just “move data from A to B.” You’re batching, filtering, enriching with an external actor, then generating structured AI output and writing it back to the same dataset, which is where n8n tends to shine. Zapier and Make can do parts of this, but the moment you need looping, branching, and tight control over what gets processed, it gets fiddly and sometimes expensive. If your process is a simple two-step enrichment, those tools can be quicker to start. If you want a workflow you can grow into (and potentially self-host), n8n is the safer bet. Talk to an automation expert if you’re not sure which fits.
Once this is running, your sheet becomes a living research-and-drafts database instead of a list of links. Set it up, review in batches, and get back to real selling.
Need Help Setting This Up?
Our automation experts can build and customize this workflow for your specific needs. Free 15-minute consultation—no commitment required.