AutomationFlowsMarketing & Ads › Scrape Apollo.io Leads and Sync Them to Google Sheets with Scrapercity

Scrape Apollo.io Leads and Sync Them to Google Sheets with Scrapercity

ByAlex Berman @alexberman on n8n.io

This workflow is for B2B sales teams, growth hackers, and revenue operators who need a reliable, low-cost pipeline of verified leads from Apollo.io -- without manually exporting CSVs or hitting Apollo's export limits. If you are prospecting into a specific industry, job title,…

Event trigger★★★★☆ complexity18 nodesHTTP RequestGoogle Sheets
Marketing & Ads Trigger: Event Nodes: 18 Complexity: ★★★★☆ Added:

This workflow corresponds to n8n.io template #13856 — 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
{
  "meta": {
    "templateCredsSetupCompleted": false
  },
  "nodes": [
    {
      "id": "11111111-0000-0000-0000-000000000001",
      "name": "Sticky Note - Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2600,
        100
      ],
      "parameters": {
        "color": 3,
        "width": 500,
        "height": 700,
        "content": "## Scrape Apollo.io leads and sync to Google Sheets via ScraperCity\n\n**Who is this for:** B2B sales teams and growth operators who want verified contact lists from Apollo.io at $0.0039/contact -- automatically, without manual CSV exports.\n\n**What it does:**\n1. Accepts your target filters (job titles, industry, company size) from the Configure node.\n2. Sends an async scrape job to ScraperCity's Apollo filter endpoint.\n3. Polls every 60 seconds until the scrape completes (jobs take 10-60 min).\n4. Downloads the result, parses CSV rows, deduplicates by email, and writes clean leads to Google Sheets.\n\n**Setup steps:**\n1. Sign up at scrapercity.com and copy your API key.\n2. In n8n -> Credentials, create a \"Header Auth\" credential:\n   - Name: ScraperCity API Key\n   - Header Name: Authorization\n   - Header Value: Bearer YOUR_KEY_HERE\n3. Connect Google Sheets OAuth2 credential.\n4. Open \"Save Leads to Google Sheets\" and set your document ID and sheet name.\n5. Edit \"Configure Search Parameters\" with your ideal customer profile.\n6. Click Execute and wait -- leads appear in Sheets when the job is done.\n\n**Cost estimate:** 1,000 contacts = ~$3.90 USD via ScraperCity."
      },
      "typeVersion": 1
    },
    {
      "id": "11111111-0000-0000-0000-000000000002",
      "name": "Sticky Note - Configuration Section",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2040,
        -160
      ],
      "parameters": {
        "color": 5,
        "width": 980,
        "height": 330,
        "content": "## Configuration\n\nThe \"When clicking Execute workflow\" trigger starts the run manually. \"Configure Search Parameters\" holds all user-editable variables -- job titles, industry, company size, and lead count -- in one place so you never need to dig into HTTP nodes. Edit this node before your first run. The \"Start Apollo Lead Scrape\" node reads these values and sends them to the ScraperCity filter endpoint."
      },
      "typeVersion": 1
    },
    {
      "id": "11111111-0000-0000-0000-000000000003",
      "name": "Sticky Note - Async Polling Loop",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1040,
        -160
      ],
      "parameters": {
        "color": 6,
        "width": 980,
        "height": 330,
        "content": "## Async Polling Loop\n\nScraperCity scrapes run in the background and typically take 10-60 minutes. \"Store Run ID\" saves the job identifier returned by the scrape request. \"Wait 60 Seconds\" pauses execution before each status check. \"Check Scrape Status\" calls the status endpoint, and \"Is Scrape Complete?\" checks whether status equals SUCCEEDED. If not complete, the loop iterates back to the wait node via \"Poll Again\". The splitInBatches node caps iterations to prevent infinite loops."
      },
      "typeVersion": 1
    },
    {
      "id": "11111111-0000-0000-0000-000000000004",
      "name": "Sticky Note - Download and Output Section",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -40,
        -160
      ],
      "parameters": {
        "color": 7,
        "width": 980,
        "height": 330,
        "content": "## Download and Output\n\nOnce the scrape status is SUCCEEDED, \"Download Scraped Results\" fetches the CSV file from the ScraperCity download endpoint. \"Parse CSV and Format Leads\" transforms raw CSV text into structured JSON rows, one item per contact. \"Remove Duplicate Leads\" deduplicates by email address. Finally, \"Save Leads to Google Sheets\" appends each unique lead as a new row in your target spreadsheet."
      },
      "typeVersion": 1
    },
    {
      "id": "11111111-0000-0000-0000-000000000005",
      "name": "Sticky Note - Set API Key",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1516,
        288
      ],
      "parameters": {
        "color": 4,
        "width": 280,
        "height": 70,
        "content": "Add your ScraperCity API key credential here"
      },
      "typeVersion": 1
    },
    {
      "id": "11111111-0000-0000-0000-000000000006",
      "name": "Sticky Note - Set Google Sheet ID",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        734,
        288
      ],
      "parameters": {
        "color": 4,
        "width": 280,
        "height": 70,
        "content": "Set your Google Sheets document ID and sheet name here"
      },
      "typeVersion": 1
    },
    {
      "id": "11111111-0000-0000-0000-000000000007",
      "name": "When clicking 'Execute workflow'",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        -2000,
        400
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "11111111-0000-0000-0000-000000000008",
      "name": "Configure Search Parameters",
      "type": "n8n-nodes-base.set",
      "position": [
        -1750,
        400
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "cfg-001",
              "name": "jobTitles",
              "type": "string",
              "value": "CEO,CTO,VP of Sales"
            },
            {
              "id": "cfg-002",
              "name": "industry",
              "type": "string",
              "value": "Technology"
            },
            {
              "id": "cfg-003",
              "name": "companySize",
              "type": "string",
              "value": "11-50"
            },
            {
              "id": "cfg-004",
              "name": "leadCount",
              "type": "number",
              "value": 100
            },
            {
              "id": "cfg-005",
              "name": "exportFileName",
              "type": "string",
              "value": "Apollo Export"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "11111111-0000-0000-0000-000000000009",
      "name": "Start Apollo Lead Scrape",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -1500,
        400
      ],
      "parameters": {
        "url": "https://scrapercity.com/api/v1/scrape/apollo-filters",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"personTitles\": {{ JSON.stringify($json.jobTitles.split(',').map(t => t.trim())) }},\n  \"companyIndustry\": \"{{ $json.industry }}\",\n  \"companySize\": \"{{ $json.companySize }}\",\n  \"count\": {{ $json.leadCount }},\n  \"fileName\": \"{{ $json.exportFileName }}\"\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "11111111-0000-0000-0000-000000000010",
      "name": "Store Run ID",
      "type": "n8n-nodes-base.set",
      "position": [
        -1250,
        400
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "rid-001",
              "name": "runId",
              "type": "string",
              "value": "={{ $json.runId }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "11111111-0000-0000-0000-000000000011",
      "name": "Polling Loop Controller",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        -1000,
        400
      ],
      "parameters": {
        "options": {
          "reset": false
        },
        "batchSize": 1
      },
      "typeVersion": 3
    },
    {
      "id": "11111111-0000-0000-0000-000000000012",
      "name": "Wait 60 Seconds",
      "type": "n8n-nodes-base.wait",
      "position": [
        -750,
        400
      ],
      "parameters": {
        "amount": 60
      },
      "typeVersion": 1.1
    },
    {
      "id": "11111111-0000-0000-0000-000000000013",
      "name": "Check Scrape Status",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -500,
        400
      ],
      "parameters": {
        "url": "=https://scrapercity.com/api/v1/scrape/status/{{ $('Store Run ID').item.json.runId }}",
        "method": "GET",
        "options": {},
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "11111111-0000-0000-0000-000000000014",
      "name": "Is Scrape Complete?",
      "type": "n8n-nodes-base.if",
      "position": [
        -250,
        400
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "cond-001",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.status }}",
              "rightValue": "SUCCEEDED"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "11111111-0000-0000-0000-000000000015",
      "name": "Download Scraped Results",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        0,
        400
      ],
      "parameters": {
        "url": "=https://scrapercity.com/api/downloads/{{ $('Store Run ID').item.json.runId }}",
        "method": "GET",
        "options": {
          "response": {
            "response": {
              "responseFormat": "text"
            }
          }
        },
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "11111111-0000-0000-0000-000000000016",
      "name": "Parse CSV and Format Leads",
      "type": "n8n-nodes-base.code",
      "position": [
        250,
        400
      ],
      "parameters": {
        "jsCode": "// Parse CSV text into structured lead objects\n// Expects standard Apollo export column headers\nconst csvText = $input.first().json.data;\n\nif (!csvText || typeof csvText !== 'string') {\n  return [];\n}\n\nconst lines = csvText.split('\\n').filter(l => l.trim().length > 0);\n\nif (lines.length < 2) {\n  return [];\n}\n\n// Parse header row -- handle quoted fields\nconst parseRow = (line) => {\n  const result = [];\n  let current = '';\n  let insideQuotes = false;\n  for (let i = 0; i < line.length; i++) {\n    const ch = line[i];\n    if (ch === '\"') {\n      insideQuotes = !insideQuotes;\n    } else if (ch === ',' && !insideQuotes) {\n      result.push(current.trim());\n      current = '';\n    } else {\n      current += ch;\n    }\n  }\n  result.push(current.trim());\n  return result;\n};\n\nconst headers = parseRow(lines[0]);\nconst items = [];\n\nfor (let i = 1; i < lines.length; i++) {\n  const values = parseRow(lines[i]);\n  if (values.length < 2) continue;\n  const lead = {};\n  headers.forEach((header, idx) => {\n    lead[header] = values[idx] || '';\n  });\n  items.push({ json: lead });\n}\n\nreturn items;"
      },
      "typeVersion": 2
    },
    {
      "id": "11111111-0000-0000-0000-000000000017",
      "name": "Remove Duplicate Leads",
      "type": "n8n-nodes-base.removeDuplicates",
      "position": [
        500,
        400
      ],
      "parameters": {
        "compare": "selectedFields",
        "options": {},
        "fieldsToCompare": {
          "fields": [
            {
              "fieldName": "Email"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "11111111-0000-0000-0000-000000000018",
      "name": "Save Leads to Google Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        750,
        400
      ],
      "parameters": {
        "columns": {
          "value": {},
          "schema": [],
          "mappingMode": "autoMapInputData",
          "matchingColumns": []
        },
        "options": {
          "valueInputMode": "USER_ENTERED"
        },
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "id",
          "value": "="
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "="
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.6
    }
  ],
  "settings": {
    "executionOrder": "v1"
  },
  "connections": {
    "Store Run ID": {
      "main": [
        [
          {
            "node": "Polling Loop Controller",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait 60 Seconds": {
      "main": [
        [
          {
            "node": "Check Scrape Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Scrape Status": {
      "main": [
        [
          {
            "node": "Is Scrape Complete?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Is Scrape Complete?": {
      "main": [
        [
          {
            "node": "Download Scraped Results",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Polling Loop Controller",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Remove Duplicate Leads": {
      "main": [
        [
          {
            "node": "Save Leads to Google Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Polling Loop Controller": {
      "main": [
        [
          {
            "node": "Wait 60 Seconds",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    },
    "Download Scraped Results": {
      "main": [
        [
          {
            "node": "Parse CSV and Format Leads",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Start Apollo Lead Scrape": {
      "main": [
        [
          {
            "node": "Store Run ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse CSV and Format Leads": {
      "main": [
        [
          {
            "node": "Remove Duplicate Leads",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Configure Search Parameters": {
      "main": [
        [
          {
            "node": "Start Apollo Lead Scrape",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When clicking 'Execute workflow'": {
      "main": [
        [
          {
            "node": "Configure Search Parameters",
            "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 is for B2B sales teams, growth hackers, and revenue operators who need a reliable, low-cost pipeline of verified leads from Apollo.io -- without manually exporting CSVs or hitting Apollo's export limits. If you are prospecting into a specific industry, job title,…

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

More Marketing & Ads workflows → · Browse all categories →

Related workflows

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

Marketing & Ads

This n8n workflow automates the process of finding ecommerce seller leads, enriching them with product and business details, discovering company websites, and extracting contact information such as em

Google Sheets, N8N Nodes Mrscraper, HTTP Request
Marketing & Ads

This template is for B2B sales teams, SDRs, growth marketers, and founders who maintain a spreadsheet of prospects and need verified contact details -- emails and mobile numbers -- without manual rese

Google Sheets, HTTP Request
Marketing & Ads

This workflow finds local businesses from Google Maps and automatically enriches them with emails, social profiles, AI summaries, and personalized outreach messages — all saved to Google Sheets. Searc

HTTP Request, Google Sheets
Marketing & Ads

This workflow leverages n8n to perform automated Google Maps API queries and manage data efficiently in Google Sheets. It's designed to extract specific location data based on a given list of ZIP code

Execute Workflow Trigger, Stop And Error, HTTP Request +1
Marketing & Ads

This repository contains an SLA-based lead routing workflow built in n8n, designed to ensure fast lead response, fair sales distribution, and controlled escalation without relying on a full CRM system

Form Trigger, Google Sheets, Slack +1