{
  "meta": {
    "templateCredsSetupCompleted": false
  },
  "nodes": [
    {
      "id": "a1000000-0000-0000-0000-000000000001",
      "name": "When clicking 'Execute workflow'",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        -2000,
        400
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "a1000000-0000-0000-0000-000000000002",
      "name": "Set Contact List",
      "type": "n8n-nodes-base.set",
      "position": [
        -1750,
        400
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "cfg-001",
              "name": "contacts",
              "type": "string",
              "value": "[{\"first_name\":\"Jane\",\"last_name\":\"Smith\",\"domain\":\"example.com\"},{\"first_name\":\"John\",\"last_name\":\"Doe\",\"domain\":\"acme.com\"}]"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "a1000000-0000-0000-0000-000000000003",
      "name": "Submit Email Finder Job",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -1500,
        400
      ],
      "parameters": {
        "url": "https://scrapercity.com/api/v1/scrape/email-finder",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"contacts\": {{ $json.contacts }}\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "a1000000-0000-0000-0000-000000000004",
      "name": "Store Run ID",
      "type": "n8n-nodes-base.set",
      "position": [
        -1250,
        400
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "cfg-002",
              "name": "runId",
              "type": "string",
              "value": "={{ $json.runId }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "a1000000-0000-0000-0000-000000000005",
      "name": "Wait Before First Poll",
      "type": "n8n-nodes-base.wait",
      "position": [
        -1000,
        400
      ],
      "parameters": {
        "amount": 30
      },
      "typeVersion": 1.1
    },
    {
      "id": "a1000000-0000-0000-0000-000000000006",
      "name": "Polling Loop",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        -750,
        400
      ],
      "parameters": {
        "options": {
          "reset": false
        },
        "batchSize": 1
      },
      "typeVersion": 3
    },
    {
      "id": "a1000000-0000-0000-0000-000000000007",
      "name": "Check Job 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": "a1000000-0000-0000-0000-000000000008",
      "name": "Is Job 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": "a1000000-0000-0000-0000-000000000009",
      "name": "Wait 60 Seconds Before Retry",
      "type": "n8n-nodes-base.wait",
      "position": [
        -250,
        592
      ],
      "parameters": {
        "amount": 60
      },
      "typeVersion": 1.1
    },
    {
      "id": "a1000000-0000-0000-0000-000000000010",
      "name": "Download Email 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": {},
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "a1000000-0000-0000-0000-000000000011",
      "name": "Parse and Format Results",
      "type": "n8n-nodes-base.code",
      "position": [
        250,
        400
      ],
      "parameters": {
        "jsCode": "/**\n * Parse ScraperCity email-finder results.\n * Expects the response body to contain a JSON array of contact objects.\n * Returns one item per contact with clean, flat fields ready for Sheets.\n */\nconst raw = $input.first().json;\n\n// ScraperCity may return the data directly as an array or nested under a key\nconst rows = Array.isArray(raw) ? raw : (raw.data || raw.results || raw.contacts || []);\n\nconst output = [];\n\nfor (const row of rows) {\n  output.push({\n    json: {\n      first_name: row.first_name || '',\n      last_name: row.last_name || '',\n      domain: row.domain || '',\n      email: row.email || '',\n      email_status: row.email_status || row.status || '',\n      confidence: row.confidence !== undefined ? row.confidence : ''\n    }\n  });\n}\n\nif (output.length === 0) {\n  return [{ json: { first_name: '', last_name: '', domain: '', email: '', email_status: 'no_results', confidence: '' } }];\n}\n\nreturn output;"
      },
      "typeVersion": 2
    },
    {
      "id": "a1000000-0000-0000-0000-000000000012",
      "name": "Filter Emails Found",
      "type": "n8n-nodes-base.filter",
      "position": [
        500,
        400
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "caseSensitive": false,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "filt-001",
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              },
              "leftValue": "={{ $json.email }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "a1000000-0000-0000-0000-000000000013",
      "name": "Write Results to Google Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        750,
        400
      ],
      "parameters": {
        "columns": {
          "value": {
            "email": "={{ $json.email }}",
            "domain": "={{ $json.domain }}",
            "last_name": "={{ $json.last_name }}",
            "confidence": "={{ $json.confidence }}",
            "first_name": "={{ $json.first_name }}",
            "email_status": "={{ $json.email_status }}"
          },
          "schema": [
            {
              "id": "first_name",
              "required": false,
              "displayName": "first_name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "last_name",
              "required": false,
              "displayName": "last_name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "domain",
              "required": false,
              "displayName": "domain",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "email",
              "required": false,
              "displayName": "email",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "email_status",
              "required": false,
              "displayName": "email_status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "confidence",
              "required": false,
              "displayName": "confidence",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow"
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "id",
          "value": "="
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "="
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.6
    },
    {
      "id": "a1000000-0000-0000-0000-000000000020",
      "name": "Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2600,
        100
      ],
      "parameters": {
        "color": 3,
        "width": 500,
        "height": 700,
        "content": "## Find business emails from names and domains using ScraperCity\n\n**Who is this for**\n\nSales development reps, growth marketers, and recruiters who need to find verified business email addresses at scale from a contact list -- without manual lookups.\n\n**What it does**\n\nThis workflow submits a list of contacts (first name, last name, company domain) to the ScraperCity email-finder API, waits for the job to complete asynchronously, downloads the results, and writes found email addresses to Google Sheets.\n\n**How it works**\n\n1. Set Contact List -- define your contacts as JSON.\n2. Submit Email Finder Job -- POST contacts to ScraperCity.\n3. Store Run ID -- capture the job ID for polling.\n4. Polling Loop -- wait and check job status until SUCCEEDED.\n5. Download Email Results -- fetch the completed data.\n6. Parse and Format Results -- clean and structure each row.\n7. Filter Emails Found -- keep only contacts with a found email.\n8. Write Results to Google Sheets -- append clean rows to your sheet.\n\n**Setup**\n\n1. Create an HTTP Header Auth credential in n8n named `ScraperCity API Key`. Set the header name to `Authorization` and the value to `Bearer YOUR_SCRAPERCITY_KEY`.\n2. Set your Google Sheets document ID and sheet name in the Write Results node.\n3. Update the contacts in the Set Contact List node.\n4. Click Execute workflow."
      },
      "typeVersion": 1
    },
    {
      "id": "a1000000-0000-0000-0000-000000000021",
      "name": "Section: Contact Configuration",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2040,
        -160
      ],
      "parameters": {
        "color": 5,
        "width": 730,
        "height": 330,
        "content": "## Contact Configuration\n\nThe `Set Contact List` node is where you define the contacts to look up. Each contact needs a `first_name`, `last_name`, and `domain` (e.g. `acme.com`). The list is formatted as a JSON array string and passed directly to the ScraperCity API. Replace the sample values with your real contacts before running the workflow."
      },
      "typeVersion": 1
    },
    {
      "id": "a1000000-0000-0000-0000-000000000022",
      "name": "Section: Submit and Store Job",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1290,
        -160
      ],
      "parameters": {
        "color": 6,
        "width": 730,
        "height": 330,
        "content": "## Submit and Store Job\n\n`Submit Email Finder Job` sends a POST request to the ScraperCity email-finder API with your contact list and receives a `runId`. The `Store Run ID` node captures this ID and makes it available throughout the rest of the workflow via `$('Store Run ID').item.json.runId`."
      },
      "typeVersion": 1
    },
    {
      "id": "a1000000-0000-0000-0000-000000000023",
      "name": "Section: Async Polling Loop",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -540,
        -160
      ],
      "parameters": {
        "color": 7,
        "width": 730,
        "height": 530,
        "content": "## Async Polling Loop\n\nScraperCity email-finder jobs run in the background and typically complete in 1-10 minutes depending on list size. This section waits 30 seconds on the first pass, then enters a loop: `Check Job Status` polls the status endpoint via GET, `Is Job Complete?` checks whether status equals `SUCCEEDED`. If not done, `Wait 60 Seconds Before Retry` pauses before looping back. Once complete, the TRUE branch continues to download."
      },
      "typeVersion": 1
    },
    {
      "id": "a1000000-0000-0000-0000-000000000024",
      "name": "Section: Download and Output",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        210,
        -160
      ],
      "parameters": {
        "color": 5,
        "width": 730,
        "height": 330,
        "content": "## Download and Output\n\n`Download Email Results` fetches the completed results from ScraperCity using the stored `runId`. `Parse and Format Results` normalizes each contact row into flat fields. `Filter Emails Found` removes contacts where no email was returned. Finally, `Write Results to Google Sheets` appends all found emails to your sheet."
      },
      "typeVersion": 1
    },
    {
      "id": "a1000000-0000-0000-0000-000000000025",
      "name": "Add ScraperCity API key here",
      "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": "a1000000-0000-0000-0000-000000000026",
      "name": "Set your Google Sheets ID here",
      "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
    }
  ],
  "settings": {
    "executionOrder": "v1"
  },
  "connections": {
    "Polling Loop": {
      "main": [
        [
          {
            "node": "Check Job Status",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    },
    "Store Run ID": {
      "main": [
        [
          {
            "node": "Wait Before First Poll",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Job Status": {
      "main": [
        [
          {
            "node": "Is Job Complete?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Is Job Complete?": {
      "main": [
        [
          {
            "node": "Download Email Results",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Wait 60 Seconds Before Retry",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Contact List": {
      "main": [
        [
          {
            "node": "Submit Email Finder Job",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter Emails Found": {
      "main": [
        [
          {
            "node": "Write Results to Google Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download Email Results": {
      "main": [
        [
          {
            "node": "Parse and Format Results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait Before First Poll": {
      "main": [
        [
          {
            "node": "Polling Loop",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Submit Email Finder Job": {
      "main": [
        [
          {
            "node": "Store Run ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse and Format Results": {
      "main": [
        [
          {
            "node": "Filter Emails Found",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait 60 Seconds Before Retry": {
      "main": [
        [
          {
            "node": "Polling Loop",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When clicking 'Execute workflow'": {
      "main": [
        [
          {
            "node": "Set Contact List",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}