AutomationFlowsData & Sheets › Collect and Score Job Listings From Multiple Boards with Apify and Sheets

Collect and Score Job Listings From Multiple Boards with Apify and Sheets

ByRishi @rishiii on n8n.io

This workflow collects job search preferences via an n8n Form, pulls listings from Remotive, Arbeitnow, Jobicy, and Apify (LinkedIn Jobs and Naukri actors), then normalizes, scores, categorizes, and writes the ranked results to Google Sheets. Receives job role, tech stack, and…

Event trigger★★★★☆ complexity20 nodesForm TriggerHTTP RequestGoogle Sheets
Data & Sheets Trigger: Event Nodes: 20 Complexity: ★★★★☆ Added:

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

This workflow follows the Form Trigger → 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": "O4m0fA6PJq3Qwm6e",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Job Search and Categorization",
  "tags": [],
  "nodes": [
    {
      "id": "4e86262f-bbf8-45cd-bae6-ca5cb910d41d",
      "name": "Form Trigger",
      "type": "n8n-nodes-base.formTrigger",
      "position": [
        9696,
        9888
      ],
      "parameters": {
        "options": {},
        "formTitle": "Job Search",
        "formFields": {
          "values": [
            {
              "fieldLabel": "Job Role",
              "placeholder": "e.g. Frontend Developer",
              "requiredField": true
            },
            {
              "fieldLabel": "Tech Stack",
              "placeholder": "e.g. React, Node.js, TypeScript",
              "requiredField": true
            },
            {
              "fieldType": "dropdown",
              "fieldLabel": "Country",
              "fieldOptions": {
                "values": [
                  {
                    "option": "India"
                  },
                  {
                    "option": "United States"
                  },
                  {
                    "option": "United Kingdom"
                  },
                  {
                    "option": "Germany"
                  },
                  {
                    "option": "Canada"
                  },
                  {
                    "option": "Australia"
                  },
                  {
                    "option": "Netherlands"
                  },
                  {
                    "option": "Singapore"
                  },
                  {
                    "option": "UAE"
                  },
                  {
                    "option": "Remote / Anywhere"
                  }
                ]
              },
              "requiredField": true
            }
          ]
        },
        "formDescription": "Enter your job preferences to search across multiple job boards"
      },
      "typeVersion": 2.2
    },
    {
      "id": "f9a05be5-e694-4154-b3c4-a26f2e893ea3",
      "name": "Store Form Input",
      "type": "n8n-nodes-base.code",
      "position": [
        9888,
        9888
      ],
      "parameters": {
        "jsCode": "// Store form input for downstream nodes\nconst formData = $input.first().json;\nreturn [{ json: { _jobRole: formData['Job Role'] || '', _techStack: formData['Tech Stack'] || '', _country: formData['Country'] || '' } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "9e218d61-ddad-45a7-a674-168d898b1e9c",
      "name": "Scrape Remotive",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        10144,
        9584
      ],
      "parameters": {
        "url": "=https://remotive.com/api/remote-jobs?search={{ $json._jobRole }}&limit=25",
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        }
      },
      "typeVersion": 4
    },
    {
      "id": "6d1ac6f0-81d6-4b94-b94f-65daf25aebec",
      "name": "Scrape Arbeitnow",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        10144,
        9792
      ],
      "parameters": {
        "url": "=https://www.arbeitnow.com/api/job-board-api?search={{ $json._jobRole }}",
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        }
      },
      "typeVersion": 4
    },
    {
      "id": "dd6db0f8-bdc0-4bb9-9519-6b05e6573fa1",
      "name": "Scrape Jobicy",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        10144,
        9984
      ],
      "parameters": {
        "url": "=https://jobicy.com/api/v2/remote-jobs?tag={{ $json._jobRole }}",
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        }
      },
      "typeVersion": 4
    },
    {
      "id": "e0b2d890-209c-4627-8fc8-52eb6a883a67",
      "name": "Apify LinkedIn Jobs",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        10048,
        10368
      ],
      "parameters": {
        "url": "https://api.apify.com/v2/acts/hMvNSpz3JnHgl5jkh/run-sync-get-dataset-items?token=<your api key>&timeout=120",
        "method": "POST",
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        },
        "jsonBody": "={{ JSON.stringify({ title: $json._jobRole, location: $json._country, maxItems: 30, publishedAt: 'r86400', sort: 'date' }) }}",
        "sendBody": true,
        "specifyBody": "json"
      },
      "typeVersion": 4
    },
    {
      "id": "27b89ea9-18bb-40a6-81f8-40d653090201",
      "name": "Apify Naukri Jobs",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        9792,
        10368
      ],
      "parameters": {
        "url": "https://api.apify.com/v2/acts/codemaverick~naukri-job-scraper-latest/run-sync-get-dataset-items?token=<your api key>&timeout=120",
        "method": "POST",
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        },
        "jsonBody": "={{ JSON.stringify({ searchQuery: $json._jobRole, maxItems: 30 }) }}",
        "sendBody": true,
        "specifyBody": "json"
      },
      "typeVersion": 4
    },
    {
      "id": "3c9db3ed-b043-4545-a4cb-2ca69c078538",
      "name": "Merge Free APIs",
      "type": "n8n-nodes-base.merge",
      "position": [
        10384,
        9680
      ],
      "parameters": {},
      "typeVersion": 3
    },
    {
      "id": "45b9e7df-f97b-4875-a4c8-0299d236b3f7",
      "name": "Merge Free + Jobicy",
      "type": "n8n-nodes-base.merge",
      "position": [
        10592,
        9840
      ],
      "parameters": {},
      "typeVersion": 3
    },
    {
      "id": "d4ce9352-42f7-4ab8-ba2b-cff479415dba",
      "name": "Merge + LinkedIn",
      "type": "n8n-nodes-base.merge",
      "position": [
        10784,
        9984
      ],
      "parameters": {},
      "typeVersion": 3
    },
    {
      "id": "16a844a5-2784-4311-b06a-224caf84a9c2",
      "name": "Merge All",
      "type": "n8n-nodes-base.merge",
      "position": [
        10992,
        10080
      ],
      "parameters": {},
      "typeVersion": 3
    },
    {
      "id": "ca754bde-5cc7-4354-8804-58ff8d72a526",
      "name": "Normalize Score and Sort",
      "type": "n8n-nodes-base.code",
      "position": [
        11184,
        9888
      ],
      "parameters": {
        "jsCode": "const items = $input.all();\n\n// Extract stored form data from the first item that has it\nlet jobRole = '';\nlet techStack = '';\nlet country = '';\nfor (const item of items) {\n  if (item.json._jobRole) { jobRole = item.json._jobRole; techStack = item.json._techStack || ''; country = item.json._country || ''; break; }\n}\n\nfunction clean(html) {\n  return (html || '').replace(/<[^>]*>/g, '').replace(/&amp;/g, '&').replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&quot;/g, '\"').replace(/&#39;/g, \"'\").replace(/\\s+/g, ' ').trim();\n}\n\nfunction extractSalary(desc, data) {\n  if (data.salary) return String(data.salary);\n  if (data.salaryMin || data.salaryMax) {\n    const parts = [];\n    if (data.salaryCurrency) parts.push(data.salaryCurrency);\n    if (data.salaryMin) parts.push(Number(data.salaryMin).toLocaleString());\n    if (data.salaryMax) parts.push('- ' + Number(data.salaryMax).toLocaleString());\n    if (data.salaryPeriod) parts.push(data.salaryPeriod);\n    return parts.join(' ');\n  }\n  const m = desc.match(/[\\$\\\u00a3\\\u20ac\\\u20b9]\\s?[\\d,]+(?:\\s?[-\u2013]\\s?[\\$\\\u00a3\\\u20ac\\\u20b9]?\\s?[\\d,]+)?(?:\\s?(?:k|K|LPA|lpa|pa|p\\.a\\.|per year|per annum|yearly|annually))?/);\n  if (m) return m[0].trim();\n  return 'Not mentioned';\n}\n\nfunction extractVisa(desc) {\n  const lower = desc.toLowerCase();\n  if (lower.includes('visa sponsor') || lower.includes('visa support') || lower.includes('we sponsor') || lower.includes('sponsorship available') || lower.includes('will sponsor')) return 'Yes';\n  if (lower.includes('no visa') || lower.includes('no sponsorship') || lower.includes('must be authorized') || lower.includes('must be eligible') || lower.includes('right to work') || lower.includes('authorized to work') || lower.includes('work permit required')) return 'No';\n  return 'Not mentioned';\n}\n\nfunction extractCompanyInfo(desc) {\n  const patterns = [/about\\s+(?:us|the\\s+company|our\\s+company)[:\\s]*([^.]+\\.)/i, /(?:we\\s+are|we're)\\s+(?:a|an)\\s+([^.]+\\.)/i];\n  for (const p of patterns) {\n    const m = desc.match(p);\n    if (m && m[1] && m[1].length > 20) return m[1].trim().substring(0, 200);\n  }\n  const fundingMatch = desc.match(/(?:raised|funding|series\\s+[a-e]|seed\\s+round|venture|backed\\s+by)[^.]+\\./i);\n  if (fundingMatch) return fundingMatch[0].trim().substring(0, 200);\n  return '';\n}\n\nfunction extractCompanySize(desc) {\n  const empMatch = desc.match(/(\\d[\\d,]*)\\+?\\s*(?:employees|team members|people|staff)/i);\n  if (empMatch) {\n    const num = parseInt(empMatch[1].replace(/,/g, ''));\n    if (num < 50) return 'Startup (<50)';\n    if (num < 200) return 'Small (50-200)';\n    if (num < 1000) return 'Medium (200-1K)';\n    if (num < 5000) return 'Large (1K-5K)';\n    return 'Enterprise (5K+)';\n  }\n  const lower = desc.toLowerCase();\n  if (lower.includes('startup') || lower.includes('early stage')) return 'Startup';\n  if (lower.includes('fortune 500') || lower.includes('multinational')) return 'Enterprise (5K+)';\n  return '';\n}\n\n// Category rules based on keywords\nfunction categorize(title, desc) {\n  const t = (title + ' ' + desc).toLowerCase();\n  if (t.includes('frontend') || t.includes('front-end') || t.includes('front end') || t.includes('react') || t.includes('angular') || t.includes('vue')) return 'Frontend';\n  if (t.includes('backend') || t.includes('back-end') || t.includes('back end') || t.includes('django') || t.includes('spring') || t.includes('express') || t.includes('fastapi')) return 'Backend';\n  if (t.includes('fullstack') || t.includes('full-stack') || t.includes('full stack')) return 'Fullstack';\n  if (t.includes('devops') || t.includes('sre') || t.includes('infrastructure') || t.includes('kubernetes') || t.includes('terraform') || t.includes('ci/cd') || t.includes('platform engineer')) return 'DevOps';\n  if (t.includes('data scientist') || t.includes('data engineer') || t.includes('data analyst') || t.includes('machine learning') || t.includes('ml engineer') || t.includes('ai ') || t.includes('analytics')) return 'Data';\n  if (t.includes('mobile') || t.includes('ios') || t.includes('android') || t.includes('flutter') || t.includes('react native') || t.includes('swift') || t.includes('kotlin')) return 'Mobile';\n  if (t.includes('design') || t.includes('ux') || t.includes('ui ') || t.includes('figma') || t.includes('product design')) return 'Design';\n  if (t.includes('qa') || t.includes('quality') || t.includes('test') || t.includes('sdet') || t.includes('automation test')) return 'QA';\n  if (t.includes('product manager') || t.includes('program manager') || t.includes('project manager') || t.includes('scrum') || t.includes('agile lead')) return 'PM';\n  if (t.includes('marketing') || t.includes('seo') || t.includes('growth') || t.includes('content')) return 'Marketing';\n  if (t.includes('sales') || t.includes('business development') || t.includes('account exec')) return 'Sales';\n  if (t.includes('security') || t.includes('cybersec') || t.includes('infosec') || t.includes('penetration')) return 'Security';\n  if (t.includes('cloud') || t.includes('aws') || t.includes('azure') || t.includes('gcp')) return 'Cloud';\n  return 'Other';\n}\n\n// Synonym map - expand role to include similar titles\nconst SYNONYMS = {\n  'engineer': ['developer', 'programmer', 'coder', 'dev', 'sde', 'swe'],\n  'developer': ['engineer', 'programmer', 'coder', 'dev', 'sde', 'swe'],\n  'programmer': ['developer', 'engineer', 'coder', 'dev'],\n  'software': ['web', 'application', 'app', 'platform'],\n  'frontend': ['front-end', 'front end', 'ui', 'react', 'angular', 'vue', 'client-side'],\n  'backend': ['back-end', 'back end', 'server', 'api', 'server-side'],\n  'fullstack': ['full-stack', 'full stack', 'frontend', 'backend'],\n  'devops': ['sre', 'infrastructure', 'platform', 'cloud', 'reliability'],\n  'data': ['analytics', 'bi', 'machine learning', 'ml', 'ai'],\n  'mobile': ['ios', 'android', 'flutter', 'react native', 'app'],\n  'qa': ['test', 'quality', 'sdet', 'automation'],\n  'test': ['qa', 'quality', 'sdet', 'automation'],\n  'design': ['ux', 'ui', 'figma', 'product design', 'graphic'],\n  'manager': ['lead', 'head', 'director', 'principal', 'senior'],\n  'lead': ['manager', 'head', 'principal', 'senior', 'staff'],\n  'senior': ['sr', 'lead', 'principal', 'staff', 'experienced'],\n  'junior': ['jr', 'entry', 'associate', 'trainee', 'intern', 'fresher'],\n  'architect': ['principal', 'staff', 'lead engineer', 'technical lead'],\n  'analyst': ['associate', 'consultant', 'specialist'],\n  'consultant': ['analyst', 'advisor', 'specialist'],\n  'security': ['cybersecurity', 'infosec', 'appsec', 'penetration'],\n  'cloud': ['aws', 'azure', 'gcp', 'infrastructure', 'devops'],\n  'product': ['pm', 'program', 'project'],\n  'marketing': ['growth', 'seo', 'content', 'digital'],\n  'sales': ['business development', 'account', 'revenue']\n};\n\nfunction expandRole(role) {\n  const words = role.toLowerCase().split(/[\\s,\\/]+/).filter(w => w.length > 1);\n  const expanded = new Set(words);\n  for (const w of words) {\n    if (SYNONYMS[w]) {\n      SYNONYMS[w].forEach(s => expanded.add(s));\n    }\n  }\n  return [...expanded];\n}\n\n// Relevance scoring with synonym expansion\nfunction scoreJob(title, desc, role, stack) {\n  let score = 0;\n  const titleLower = title.toLowerCase();\n  const descLower = desc.toLowerCase();\n  const expandedRole = expandRole(role);\n  const stackWords = stack.toLowerCase().split(/[\\s,\\/]+/).filter(w => w.length > 1);\n\n  // Title matches (highest weight)\n  for (const w of expandedRole) {\n    if (titleLower.includes(w)) score += 25;\n  }\n  for (const w of stackWords) {\n    if (titleLower.includes(w)) score += 20;\n  }\n\n  // Description matches\n  for (const w of expandedRole) {\n    if (descLower.includes(w)) score += 8;\n  }\n  for (const w of stackWords) {\n    if (descLower.includes(w)) score += 12;\n  }\n\n  // Normalize to 0-100\n  const maxPossible = (expandedRole.length * 33) + (stackWords.length * 32);\n  return maxPossible > 0 ? Math.min(100, Math.round((score / maxPossible) * 100)) : 50;\n}\n\nconst normalized = items.map(item => {\n  const d = item.json;\n  if (d._jobRole || d.error || d.statusCode >= 400 || (typeof d === 'string')) return [];\n\n  // Remotive\n  if (d.jobs && !Array.isArray(d.data) && !d.jobTitle && !d.companyName) {\n    return d.jobs.map(j => {\n      const desc = clean(j.description || '');\n      return { json: {\n        title: j.title || '', company: j.company_name || '', link: j.url || '',\n        description: desc.substring(0, 800), posted_date: j.publication_date || '',\n        source: 'Remotive', location: j.candidate_required_location || 'Remote',\n        salary: extractSalary(desc, j), job_type: (j.job_type || '').replace(/_/g, ' ') || 'Not mentioned',\n        visa_sponsored: extractVisa(desc), company_size: extractCompanySize(desc), company_info: extractCompanyInfo(desc)\n      }};\n    });\n  }\n\n  // Arbeitnow\n  if (d.data && Array.isArray(d.data)) {\n    return d.data.map(j => {\n      const desc = clean(j.description || '');\n      return { json: {\n        title: j.title || '', company: j.company_name || '', link: j.url || '',\n        description: desc.substring(0, 800), posted_date: j.created_at || '',\n        source: 'Arbeitnow', location: j.location || (j.remote ? 'Remote' : 'Not mentioned'),\n        salary: extractSalary(desc, j), job_type: j.remote ? 'Remote' : 'On-site',\n        visa_sponsored: extractVisa(desc), company_size: extractCompanySize(desc), company_info: extractCompanyInfo(desc)\n      }};\n    });\n  }\n\n  // Jobicy\n  if (d.jobs && Array.isArray(d.jobs) && d.jobs[0] && d.jobs[0].jobTitle) {\n    return d.jobs.map(j => {\n      const desc = clean(j.jobDescription || '');\n      return { json: {\n        title: j.jobTitle || '', company: j.companyName || '', link: j.url || '',\n        description: desc.substring(0, 800), posted_date: j.pubDate || '',\n        source: 'Jobicy', location: j.jobGeo || 'Remote',\n        salary: extractSalary(desc, j), job_type: (Array.isArray(j.jobType) ? j.jobType.join(', ') : j.jobType) || 'Not mentioned',\n        visa_sponsored: extractVisa(desc), company_size: extractCompanySize(desc), company_info: extractCompanyInfo(desc)\n      }};\n    });\n  }\n\n  // LinkedIn (positionName)\n  if (d.positionName || (d.title && d.companyName && d.link)) {\n    const desc = clean(d.descriptionText || d.description || '');\n    return [{ json: {\n      title: d.positionName || d.title || '', company: d.companyName || d.company || '',\n      link: d.link || d.applyUrl || d.url || '',\n      description: desc.substring(0, 800), posted_date: d.postedAt || d.publishedAt || d.postedTime || '',\n      source: 'LinkedIn', location: d.location || d.formattedLocation || 'Not mentioned',\n      salary: d.salary || extractSalary(desc, d), job_type: d.contractType || d.employmentType || d.workType || 'Not mentioned',\n      visa_sponsored: extractVisa(desc), company_size: d.companySize || extractCompanySize(desc),\n      company_info: d.companyDescription ? clean(d.companyDescription).substring(0, 200) : extractCompanyInfo(desc)\n    }}];\n  }\n\n  // Naukri (Job Title)\n  if (d.jobId || d['Job Title'] || (d.title && d.jdURL)) {\n    const desc = clean(d.Description || d.description || d.jobDescription || d['Job Description'] || '');\n    return [{ json: {\n      title: d['Job Title'] || d.title || d.designation || '',\n      company: d.Company || d['Company Name'] || d.companyName || d.company || '',\n      link: d['Job URL'] || d.jdURL || d.url || '', description: desc.substring(0, 800),\n      posted_date: d['Posted Time'] || d.createdDate || d.postedDate || d['Posted Date'] || '',\n      source: 'Naukri',\n      location: d['Location'] || d.placeholders?.find(p => p.type === 'location')?.label || d.location || 'Not mentioned',\n      salary: d['Salary'] || d.placeholders?.find(p => p.type === 'salary')?.label || d.salary || extractSalary(desc, d),\n      job_type: d['Experience'] || d.placeholders?.find(p => p.type === 'experience')?.label || 'Not mentioned',\n      visa_sponsored: extractVisa(desc),\n      company_size: d.ambitionBoxData?.CompanySize || d['Company Size'] || extractCompanySize(desc),\n      company_info: d.ambitionBoxData?.About ? clean(d.ambitionBoxData.About).substring(0, 200) : extractCompanyInfo(desc)\n    }}];\n  }\n\n  // Fallback\n  const desc = clean(d.description || d.jobDescription || '');\n  if (!d.title && !d.jobTitle && !d.positionName) return [];\n  return [{ json: {\n    title: d.title || d.jobTitle || d.positionName || '',\n    company: d.company || d.company_name || d.companyName || '',\n    link: d.url || d.link || '', description: desc.substring(0, 800),\n    posted_date: d.publication_date || d.created_at || d.pubDate || d.postedAt || '',\n    source: 'Other', location: d.candidate_required_location || d.location || d.jobGeo || 'Not mentioned',\n    salary: extractSalary(desc, d), job_type: d.job_type || d.jobType || 'Not mentioned',\n    visa_sponsored: extractVisa(desc), company_size: extractCompanySize(desc), company_info: extractCompanyInfo(desc)\n  }}];\n});\n\n// Flatten, score, categorize, filter, sort\nconst flat = normalized.flat().filter(i => i.json && i.json.title);\nconst scored = flat.map(item => {\n  const j = item.json;\n  const relevance = scoreJob(j.title, j.description, jobRole, techStack);\n  const category = categorize(j.title, j.description);\n  return { json: { ...j, relevance: relevance + '%', category } };\n});\n\n// Filter: must have at least 1 expanded role keyword in title or description\nconst expandedRoleWords = expandRole(jobRole);\nconst filtered = scored.filter(item => {\n  const rel = parseInt(item.json.relevance);\n  if (rel >= 10) return true;\n  const titleLower = item.json.title.toLowerCase();\n  const descLower = (item.json.description || '').toLowerCase();\n  return expandedRoleWords.some(w => titleLower.includes(w) || descLower.includes(w));\n});\n\n// Sort by relevance (highest first), then by date\nfiltered.sort((a, b) => {\n  const ra = parseInt(a.json.relevance);\n  const rb = parseInt(b.json.relevance);\n  if (rb !== ra) return rb - ra;\n  return new Date(b.json.posted_date || 0) - new Date(a.json.posted_date || 0);\n});\n\nreturn filtered;"
      },
      "typeVersion": 2
    },
    {
      "id": "5586ed3e-4466-4873-9d3f-3a74a9f082fb",
      "name": "Google Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        11696,
        9888
      ],
      "parameters": {
        "columns": {
          "value": {
            "Link": "={{ $json.link }}",
            "Salary": "={{ $json.salary }}",
            "Source": "={{ $json.source }}",
            "Company": "={{ $json.company }}",
            "Category": "={{ $json.category }}",
            "Job Type": "={{ $json.job_type }}",
            "Location": "={{ $json.location }}",
            "Job Title": "={{ $json.title }}",
            "Relevance": "={{ $json.relevance }}",
            "Date Posted": "={{ $json.posted_date }}",
            "Company Size": "={{ $json.company_size }}",
            "Visa Sponsored": "={{ $json.visa_sponsored }}",
            "Company Info / Funding": "={{ $json.company_info }}"
          },
          "mappingMode": "defineBelow"
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_SHEET_ID_HERE"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4
    },
    {
      "id": "bc6daee0-891e-48da-af5d-e45d5f8721d1",
      "name": "Sticky Note: Trigger",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        9600,
        9648
      ],
      "parameters": {
        "color": 3,
        "width": 432,
        "height": 424,
        "content": "### User Inputs & Trigger\n- **Form Trigger**: Collects user job search preferences:\n  1. **Job Role** (e.g. *Software Engineer*)\n  2. **Tech Stack** (e.g. *React, Node.js*)\n  3. **Country** (Dropdown selection)\n- **Store Form Input**: Saves these values to variables (`_jobRole`, `_techStack`, `_country`) for downstream APIs."
      },
      "typeVersion": 1
    },
    {
      "id": "7a1fc04d-f93c-49a5-8f05-3298960746fe",
      "name": "Sticky Note: Scrapers",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        10064,
        9360
      ],
      "parameters": {
        "color": 2,
        "width": 268,
        "height": 1176,
        "content": "### Parallel Job Scrapers\nFetches job listings from 5 sources:\n- **Remotive** (Free API, Remote)\n- **Arbeitnow** (Free API, Germany/Europe)\n- **Jobicy** (Free API, Remote Focus)\n- **Apify LinkedIn** (Paid Scraper, Country-specific)\n- **Apify Naukri** (Paid Scraper, India/Global)"
      },
      "typeVersion": 1
    },
    {
      "id": "6e27df1e-cd80-44b4-b489-cbd77e4d36df",
      "name": "Sticky Note: Merge",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        10368,
        9552
      ],
      "parameters": {
        "color": 7,
        "width": 736,
        "height": 678,
        "content": "### Data Consolidation (Merge Chain)\n- Sequentially appends arrays from all 5 scraper nodes.\n- Ensures all job listings are merged into a single downstream stream before normalization and scoring."
      },
      "typeVersion": 1
    },
    {
      "id": "ad6f0ee0-626d-4109-b96c-3e66b81a8035",
      "name": "Sticky Note: Scoring",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        11104,
        9664
      ],
      "parameters": {
        "color": 4,
        "width": 384,
        "height": 362,
        "content": "### Normalize, Score & Sort\nRuns custom JavaScript to:\n- **Normalize**: Parse fields like salary, visa support, company size across all API schemas.\n- **Score Relevance**: Rank matching based on user-requested role and tech stack (using synonym expansion).\n- **Filter**: Drop highly irrelevant jobs.\n- **Sort**: Order by highest relevance and post date."
      },
      "typeVersion": 1
    },
    {
      "id": "a6235494-8420-4a7b-8046-d09e71bbb0da",
      "name": "Sticky Note: Sheets",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        11584,
        9696
      ],
      "parameters": {
        "color": 2,
        "width": 280,
        "height": 344,
        "content": "### Google Sheets Output\n- Enter your Google Sheet ID .\n\nhttps://docs.google.com/spreadsheets/d/<your google sheet ID>/edit?gid=0#gid=0"
      },
      "typeVersion": 1
    },
    {
      "id": "f8af41f0-ee2e-4086-b15b-975f0eed96d0",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        9008,
        9472
      ],
      "parameters": {
        "width": 400,
        "height": 384,
        "content": "## Pre requisites\n\nThis is a simple way to get job details for the job listings directly in your spreadsheet\n\n- go to Apify and create a free account .\n-  link - https://console.apify.com/\n- to get your api key go to - https://console.apify.com/settings/integrations\n\n- approve the naukri actor to work using this link-https://console.apify.com/actors/GLb4E7UrStD7XLJxO?approvePermissions=true\n"
      },
      "typeVersion": 1
    },
    {
      "id": "e3cb9f0f-c4d6-47fa-940d-346169b2632d",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        9600,
        10128
      ],
      "parameters": {
        "color": 4,
        "width": 688,
        "height": 400,
        "content": "## Configure this two nodes\n\n- replace the URL to link -\n\nlinkedin -  https://api.apify.com/v2/acts/hMvNSpz3JnHgl5jkh/run-sync-get-dataset-items?token=<Your API key>&timeout=120\n\nnaukri - https://api.apify.com/v2/acts/codemaverick~naukri-job-scraper-latest/run-sync-get-dataset-items?token=<your API key>&timeout=120"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "executionOrder": "v1"
  },
  "versionId": "244e3b51-0b2b-4c67-8646-79fd2006e460",
  "connections": {
    "Merge All": {
      "main": [
        [
          {
            "node": "Normalize Score and Sort",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Form Trigger": {
      "main": [
        [
          {
            "node": "Store Form Input",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Scrape Jobicy": {
      "main": [
        [
          {
            "node": "Merge Free + Jobicy",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Merge Free APIs": {
      "main": [
        [
          {
            "node": "Merge Free + Jobicy",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Scrape Remotive": {
      "main": [
        [
          {
            "node": "Merge Free APIs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge + LinkedIn": {
      "main": [
        [
          {
            "node": "Merge All",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Scrape Arbeitnow": {
      "main": [
        [
          {
            "node": "Merge Free APIs",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Store Form Input": {
      "main": [
        [
          {
            "node": "Scrape Remotive",
            "type": "main",
            "index": 0
          },
          {
            "node": "Scrape Arbeitnow",
            "type": "main",
            "index": 0
          },
          {
            "node": "Scrape Jobicy",
            "type": "main",
            "index": 0
          },
          {
            "node": "Apify LinkedIn Jobs",
            "type": "main",
            "index": 0
          },
          {
            "node": "Apify Naukri Jobs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Apify Naukri Jobs": {
      "main": [
        [
          {
            "node": "Merge All",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Apify LinkedIn Jobs": {
      "main": [
        [
          {
            "node": "Merge + LinkedIn",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Merge Free + Jobicy": {
      "main": [
        [
          {
            "node": "Merge + LinkedIn",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize Score and Sort": {
      "main": [
        [
          {
            "node": "Google Sheets",
            "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 collects job search preferences via an n8n Form, pulls listings from Remotive, Arbeitnow, Jobicy, and Apify (LinkedIn Jobs and Naukri actors), then normalizes, scores, categorizes, and writes the ranked results to Google Sheets. Receives job role, tech stack, and…

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

More Data & Sheets workflows → · Browse all categories →

Related workflows

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

Data & Sheets

Overview 🌐

Form Trigger, HTTP Request, Google Sheets
Data & Sheets

Splitout Code. Uses splitOut, httpRequest, googleSheets, stickyNote. Event-driven trigger; 36 nodes.

HTTP Request, Google Sheets, Form Trigger +1
Data & Sheets

This n8n workflow is designed for Customer Success Managers (CSM), marketers, sales teams, and data administrators who need to automate the process of uploading and processing CSV data in HubSpot. It

HTTP Request, Google Sheets, Form Trigger +1
Data & Sheets

The SEO On Page API is a powerful tool for keyword research, competitor analysis, backlink insights, and overall SEO optimization. With multiple endpoints, you can instantly gather actionable SEO data

Form Trigger, HTTP Request, Google Sheets
Data & Sheets

Demonstration video

Form Trigger, HTTP Request, Google Sheets