AutomationFlowsWeb Scraping › Enrich Linkedin Profiles in Nocodb CRM with Apify Scraper

Enrich Linkedin Profiles in Nocodb CRM with Apify Scraper

ByGuido X Jansen @gxjansen on n8n.io

**Manual LinkedIn data collection is time-consuming, error-prone, and results in inconsistent data quality across CRM/database records.**

Event trigger★★★★☆ complexity21 nodesNoco DbHTTP Request
Web Scraping Trigger: Event Nodes: 21 Complexity: ★★★★☆ Added:

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

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": "WczQxQgtvjjk1HC1",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "LinkedIn Profile Enrichment With Error Handling",
  "tags": [],
  "nodes": [
    {
      "id": "node_0",
      "name": "Manual Trigger",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        160,
        80
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "node_1",
      "name": "Get Guests with LinkedIn",
      "type": "n8n-nodes-base.nocoDb",
      "position": [
        380,
        180
      ],
      "parameters": {
        "limit": 15,
        "table": "NOCODB_TABLEID",
        "options": {
          "where": "(LinkedIn,isnot,null)~and(linkedin_headline,is,null)"
        },
        "operation": "getAll",
        "projectId": "NOCODB_PROJECTID",
        "authentication": "nocoDbApiToken"
      },
      "credentials": {
        "nocoDbApiToken": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "node_2",
      "name": "Run Apify LinkedIn Scraper",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        680,
        180
      ],
      "parameters": {
        "url": "https://api.apify.com/v2/acts/dev_fusion~linkedin-profile-scraper/runs",
        "method": "POST",
        "options": {},
        "jsonBody": "={\"profileUrls\": [\"{{$json.LinkedIn}}\"]}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpQueryAuth"
      },
      "credentials": {
        "httpQueryAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "node_3",
      "name": "Wait for Completion",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        900,
        180
      ],
      "parameters": {
        "url": "=https://api.apify.com/v2/acts/dev_fusion~linkedin-profile-scraper/runs/{{$json.data.id}}",
        "options": {
          "timeout": 300000
        },
        "sendQuery": true,
        "authentication": "genericCredentialType",
        "genericAuthType": "httpQueryAuth",
        "queryParameters": {
          "parameters": [
            {
              "name": "waitForFinish",
              "value": "240"
            }
          ]
        }
      },
      "credentials": {
        "httpQueryAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "node_4",
      "name": "Check Run Status",
      "type": "n8n-nodes-base.if",
      "position": [
        1120,
        180
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "93d97f85-c98a-4c0d-b7a4-e7c1c9a5e0a1",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.data.status }}",
              "rightValue": "SUCCEEDED"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "node_5",
      "name": "Get Scraper Results",
      "type": "n8n-nodes-base.code",
      "onError": "continueErrorOutput",
      "position": [
        1340,
        80
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Get the dataset ID from the previous node\nconst datasetId = $json.data.defaultDatasetId;\n\ntry {\n  // Get the Apify token\n  const apifyToken = '[YOUR APIFY TOKEN]';\n  \n  // Make the HTTP request to get the dataset\n  const response = await this.helpers.httpRequest({\n    method: 'GET',\n    url: `https://api.apify.com/v2/datasets/${datasetId}/items?token=YOUR_TOKEN_HERE\n    json: true\n  });\n\n  // Check if we have valid data\n  if (!Array.isArray(response) || response.length === 0) {\n    // No data found - this is a 404 profile\n    throw new Error('Empty dataset - LinkedIn profile not found or inaccessible');\n  }\n\n  // Check if the profile data is actually valid\n  const firstItem = response[0];\n  if (!firstItem || !firstItem.linkedinUrl || !firstItem.fullName) {\n    throw new Error('Invalid LinkedIn profile data');\n  }\n\n  // IMPORTANT: For runOnceForEachItem mode, we must return a single object\n  // n8n will handle the array data by flattening it\n  // We need to pass the array as a property of an object\n  return {\n    linkedinData: response\n  };\n} catch (error) {\n  // For HTTP errors (like 404 on dataset endpoint)\n  if (error.response && error.response.statusCode === 404) {\n    throw new Error('Dataset not found - LinkedIn profile not accessible');\n  }\n  // Re-throw other errors to route to error output\n  throw error;\n}"
      },
      "typeVersion": 2
    },
    {
      "id": "node_7",
      "name": "Transform Data",
      "type": "n8n-nodes-base.code",
      "position": [
        1640,
        -100
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Extract the LinkedIn data from the wrapped response\nconst dataArray = $json.linkedinData;\nif (!dataArray || !Array.isArray(dataArray) || dataArray.length === 0) {\n  throw new Error('No LinkedIn data found');\n}\n\n// Get the first (and should be only) item from the array\nconst linkedinData = dataArray[0];\n\n// Get the guest ID from the original NocoDB data\nlet guestId = null;\n\ntry {\n  guestId = $('Get Guests with LinkedIn').item.json.Id;\n} catch (error) {\n  throw new Error('Could not find guest ID. Error: ' + error.message);\n}\n\nif (!guestId) {\n  throw new Error('Guest ID is null or undefined');\n}\n\n// Get the current timestamp\nconst currentTimestamp = new Date().toISOString();\n\n// Transform the data to match our NocoDB fields\nconst transformedData = {\n  Id: guestId,\n  linkedin_url: linkedinData.linkedinUrl || '',\n  linkedin_full_name: linkedinData.fullName || '',\n  linkedin_first_name: linkedinData.firstName || '',\n  linkedin_headline: linkedinData.headline || '',\n  linkedin_email: linkedinData.email || '',\n  linkedin_bio: linkedinData.about || '',\n  linkedin_profile_pic: linkedinData.profilePicHighQuality || linkedinData.profilePic || '',\n  linkedin_current_role: linkedinData.jobTitle || '',\n  linkedin_current_company: linkedinData.companyName || '',\n  linkedin_country: linkedinData.addressCountryOnly || '',\n  linkedin_skills: linkedinData.topSkillsByEndorsements || '',\n  linkedin_company_website: linkedinData.companyWebsite || '',\n  linkedin_experiences: JSON.stringify(linkedinData.experiences || []),\n  linkedin_personal_website: JSON.stringify(linkedinData.creatorWebsite || {}),\n  linkedin_publications: JSON.stringify(linkedinData.publications || []),\n  linkedin_last_modified: currentTimestamp,\n  linkedin_scrape_status: 'success',\n  linkedin_scrape_last_attempt: currentTimestamp\n};\n\nreturn transformedData;"
      },
      "typeVersion": 2
    },
    {
      "id": "node_8",
      "name": "Update Guest Success",
      "type": "n8n-nodes-base.nocoDb",
      "position": [
        1860,
        -100
      ],
      "parameters": {
        "id": "={{$json.Id}}",
        "table": "NOCODB_TABLEID",
        "operation": "update",
        "projectId": "NOCODB_PROJECTID",
        "dataToSend": "autoMapInputData",
        "authentication": "nocoDbApiToken"
      },
      "credentials": {
        "nocoDbApiToken": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "node_9",
      "name": "Clear Broken LinkedIn URL",
      "type": "n8n-nodes-base.code",
      "position": [
        1660,
        260
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Get the guest ID from the original NocoDB data\nlet guestId = null;\nlet linkedinUrl = null;\n\ntry {\n  guestId = $('Get Guests with LinkedIn').item.json.Id;\n  linkedinUrl = $('Get Guests with LinkedIn').item.json.LinkedIn;\n} catch (error) {\n  throw new Error('Could not find guest data. Error: ' + error.message);\n}\n\nif (!guestId) {\n  throw new Error('Guest ID is null or undefined');\n}\n\n// Get the current timestamp\nconst currentTimestamp = new Date().toISOString();\n\n// Return data to clear the broken LinkedIn URL\nreturn {\n  Id: guestId,\n  LinkedIn: null, // Clear the LinkedIn field\n  linkedin_scrape_status: 'invalid_url',\n  linkedin_scrape_error_reason: 'Profile not found or inaccessible (404)',\n  linkedin_last_modified: currentTimestamp\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "node_10",
      "name": "Update Guest - Clear URL",
      "type": "n8n-nodes-base.nocoDb",
      "position": [
        1880,
        260
      ],
      "parameters": {
        "id": "={{$json.Id}}",
        "table": "NOCODB_TABLEID",
        "operation": "update",
        "projectId": "NOCODB_PROJECTID",
        "dataToSend": "autoMapInputData",
        "authentication": "nocoDbApiToken"
      },
      "credentials": {
        "nocoDbApiToken": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "node_11",
      "name": "Handle Scraper Error",
      "type": "n8n-nodes-base.code",
      "position": [
        1640,
        620
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Get the guest ID and error info\nlet guestId = null;\nlet errorMessage = 'Unknown error';\n\ntry {\n  guestId = $('Get Guests with LinkedIn').item.json.Id;\n  \n  // Try to get error details from the failed request\n  if ($json.error) {\n    errorMessage = $json.error.message || $json.error;\n  } else if ($json.data && $json.data.status === 'FAILED') {\n    errorMessage = 'Apify scraper failed';\n  }\n} catch (error) {\n  errorMessage = error.message;\n}\n\n// Get the current timestamp\nconst currentTimestamp = new Date().toISOString();\n\n// Return error update data\nreturn {\n  Id: guestId,\n  linkedin_scrape_status: 'error',\n  linkedin_scrape_error_reason: errorMessage,\n  linkedin_scrape_last_attempt: currentTimestamp\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "node_12",
      "name": "Update Guest - Error Status",
      "type": "n8n-nodes-base.nocoDb",
      "position": [
        1860,
        620
      ],
      "parameters": {
        "id": "={{$json.Id}}",
        "table": "NOCODB_TABLEID",
        "operation": "update",
        "projectId": "NOCODB_PROJECTID",
        "dataToSend": "autoMapInputData",
        "authentication": "nocoDbApiToken"
      },
      "credentials": {
        "nocoDbApiToken": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "6356f2e6-a8d3-4d0f-8ac9-22793999d1bf",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        160,
        280
      ],
      "parameters": {
        "rule": {
          "interval": [
            {}
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "ccaaf658-e84e-47cb-8519-f9547c9bf6eb",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1540,
        -220
      ],
      "parameters": {
        "color": 4,
        "width": 580,
        "height": 320,
        "content": "## Data enrichment flow"
      },
      "typeVersion": 1
    },
    {
      "id": "142b5d5f-82fe-4f6d-b891-dd2eb1de646f",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1540,
        140
      ],
      "parameters": {
        "width": 580,
        "height": 320,
        "content": "## Remove 404 linkedin URL"
      },
      "typeVersion": 1
    },
    {
      "id": "63b77aec-30df-4ee3-843e-57170c36b840",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1540,
        500
      ],
      "parameters": {
        "color": 3,
        "width": 580,
        "height": 320,
        "content": "## Apify error/timeout"
      },
      "typeVersion": 1
    },
    {
      "id": "ed6d5639-4a83-44de-9b38-a83a9ba3d342",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        320,
        120
      ],
      "parameters": {
        "color": 5,
        "width": 220,
        "height": 240,
        "content": "## Get LinkedIn URL"
      },
      "typeVersion": 1
    },
    {
      "id": "9d3e30ef-b19a-4a89-b968-a3dcba8e9efa",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        620,
        120
      ],
      "parameters": {
        "color": 5,
        "width": 420,
        "height": 240,
        "content": "## Get LinkedIn profile data"
      },
      "typeVersion": 1
    },
    {
      "id": "5de302ee-f413-4b95-b14e-677d9d3e793d",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1680,
        280
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "74ff9c6b-b162-4c0c-a33b-2129b4babe8d",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1680,
        280
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "1ce6b31d-bdc3-40e1-ab07-4368b410da6e",
      "name": "Sticky Note7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2180,
        20
      ],
      "parameters": {
        "color": 7,
        "width": 440,
        "height": 600,
        "content": "## Required fields in NocoDB table:\n\n**Input field:**\n* LinkedIn\n\n**Output fields:**\n* linkedin_url\n* linkedin_full_name\n* linkedin_first_name: \n* linkedin_headline:\n* linkedin_email:\n* linkedin_bio:\n* linkedin_profile_pic\n* linkedin_current_role\n* linkedin_current_company\n* linkedin_country\n* linkedin_skills\n* linkedin_company_website\n* linkedin_experiences\n* linkedin_personal_website\n* linkedin_publications\n* linkedin_scrape_error_reason\n* linkedin_scrape_last_attempt\n* linkedin_scrape_status\n* linkedin_last_modified"
      },
      "typeVersion": 1
    }
  ],
  "active": true,
  "settings": {
    "executionOrder": "v1",
    "saveManualExecutions": true
  },
  "versionId": "3357614b-e310-44b8-97af-eac2106b28f4",
  "connections": {
    "Manual Trigger": {
      "main": [
        [
          {
            "node": "Get Guests with LinkedIn",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Transform Data": {
      "main": [
        [
          {
            "node": "Update Guest Success",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Run Status": {
      "main": [
        [
          {
            "node": "Get Scraper Results",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Handle Scraper Error",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Get Guests with LinkedIn",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Scraper Results": {
      "main": [
        [
          {
            "node": "Transform Data",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Clear Broken LinkedIn URL",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait for Completion": {
      "main": [
        [
          {
            "node": "Check Run Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Handle Scraper Error": {
      "main": [
        [
          {
            "node": "Update Guest - Error Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Guests with LinkedIn": {
      "main": [
        [
          {
            "node": "Run Apify LinkedIn Scraper",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Clear Broken LinkedIn URL": {
      "main": [
        [
          {
            "node": "Update Guest - Clear URL",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Run Apify LinkedIn Scraper": {
      "main": [
        [
          {
            "node": "Wait for Completion",
            "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

**Manual LinkedIn data collection is time-consuming, error-prone, and results in inconsistent data quality across CRM/database records.**

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

More Web Scraping workflows → · Browse all categories →

Related workflows

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

Web Scraping

This workflow automates lead generation by scraping business data from Google Maps using Apify, enriching it with verified email addresses via Anymailfinder, and storing the results in a NocoDB databa

Form Trigger, HTTP Request, Noco Db
Web Scraping

This workflow uses the Zyte API to automatically detect and extract structured data from E-commerce sites, Articles, Job Boards, and Search Engine Results (SERP) - no custom CSS selectors required.

Form Trigger, HTTP Request, Form
Web Scraping

Automate LinkedIn lead generation by scraping comments from targeted posts and enriching profiles with detailed data

Form Trigger, HTTP Request, Google Sheets
Web Scraping

This workflow contains community nodes that are only compatible with the self-hosted version of n8n.

Notion, @Apify/N8N Nodes Apify, HTTP Request
Web Scraping

This workflow runs a spider job in the background via Scrapyd, using a YAML config that defines selectors and parsing rules. When triggered, it schedules the spider with parameters (query, project ID,

HTTP Request