This workflow corresponds to n8n.io template #12014 — we link there as the canonical source.
This workflow follows the Agent → Gmail 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 →
{
"id": "Mmt9NSn1Zs9h8u8q",
"name": "5 Smart Job Screener with AI Resume Matching",
"tags": [],
"nodes": [
{
"id": "d652e883-d88b-4404-a1f9-2b97f461d0d9",
"name": "Download file",
"type": "n8n-nodes-base.googleDrive",
"position": [
-2384,
16
],
"parameters": {
"fileId": {
"__rl": true,
"mode": "list",
"value": "1W-6jGxQJ2-f-hqfYP5pwAmJRou0fTXWS"
},
"options": {},
"operation": "download"
},
"credentials": {
"googleDriveOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 3
},
{
"id": "c89fd2f1-602d-4688-9cf2-4d827d4626a2",
"name": "Extract from File",
"type": "n8n-nodes-base.extractFromFile",
"position": [
-2176,
16
],
"parameters": {
"options": {},
"operation": "pdf"
},
"typeVersion": 1
},
{
"id": "a4005286-69e1-4ebe-abd9-61e716a4d345",
"name": "LinkedIn Search URL",
"type": "n8n-nodes-base.code",
"position": [
-1152,
16
],
"parameters": {
"jsCode": "let url = \"https://www.linkedin.com/jobs/search/?f_TPR=r86400\"\n\nconst keyword = $input.first().json.Keyword\nconst location = $input.first().json.Location\nconst experienceLevel = $input.first().json['Experience Level']\nconst remote = $input.first().json.Remote\nconst easyApply = $input.first().json['Easy Apply']\n\nif (keyword != \"\") {\n url += `&keywords=${keyword}`;\n}\n\nif (location != \"\") {\n url += `&location=${location}`;\n}\n\nif (experienceLevel !== \"\") {\n const transformedExperiences = experienceLevel\n .split(\",\")\n .map((exp) => {\n switch (exp.trim()) {\n case \"Internship\": return \"1\";\n case \"Entry level\": return \"2\";\n case \"New Grad\": return \"3\";\n default: return \"\";\n }\n })\n .filter(Boolean);\n url += `&f_E=${transformedExperiences.join(\",\")}`;\n}\n\n// put f_EA *here* to match LinkedIn's order: f_E \u2192 f_EA \u2192 f_WT\nurl += \"&f_EA=true\";\n\nif (remote && remote !== \"\") {\n const transformedRemote = remote\n .split(\",\")\n .map((e) => {\n switch (e.trim()) {\n case \"Remote\": return \"2\";\n case \"Hybrid\": return \"3\";\n case \"On-Site\": return \"1\";\n default: return \"\";\n }\n })\n .filter(Boolean);\n url += `&f_WT=${transformedRemote.join(\",\")}`;\n}\n\nreturn { url };\n"
},
"typeVersion": 2
},
{
"id": "2ace02ea-0eb9-43cd-9391-c534ddf274d1",
"name": "Fetch jobs from LinkedIn",
"type": "n8n-nodes-base.httpRequest",
"position": [
-944,
16
],
"parameters": {
"url": "={{ $json.url }}",
"options": {}
},
"typeVersion": 4.2
},
{
"id": "fdb843b4-e521-4c7a-a6ea-aa9273bdd675",
"name": "HTML",
"type": "n8n-nodes-base.html",
"position": [
-784,
16
],
"parameters": {
"options": {},
"operation": "extractHtmlContent",
"extractionValues": {
"values": [
{
"key": "jobs",
"attribute": "href",
"cssSelector": "ul.jobs-search__results-list li div a[class*=\"base-card\"]",
"returnArray": true,
"returnValue": "attribute"
}
]
}
},
"typeVersion": 1.2
},
{
"id": "fb491af8-9d83-4cd9-acc9-3d1853d72d19",
"name": "Split Out",
"type": "n8n-nodes-base.splitOut",
"position": [
-432,
16
],
"parameters": {
"options": {},
"fieldToSplitOut": "jobs"
},
"typeVersion": 1
},
{
"id": "32ee5ba2-64ee-4763-8ee0-7d0e3277f160",
"name": "Loop Over Items",
"type": "n8n-nodes-base.splitInBatches",
"position": [
-240,
16
],
"parameters": {
"options": {}
},
"typeVersion": 3
},
{
"id": "ed4eddfa-31fc-4afd-8d72-dcfdbe6841b8",
"name": "Wait",
"type": "n8n-nodes-base.wait",
"position": [
-2816,
336
],
"parameters": {
"amount": 2
},
"typeVersion": 1.1
},
{
"id": "11533498-46d4-4715-acf3-e1c0daefcb57",
"name": "Google Gemini Chat Model",
"type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
"position": [
-1680,
576
],
"parameters": {
"options": {}
},
"credentials": {
"googlePalmApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "9558ed3a-8a2b-4146-a299-ed546c46700f",
"name": "Google Gemini Chat Model1",
"type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
"position": [
-976,
560
],
"parameters": {
"options": {}
},
"credentials": {
"googlePalmApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "7b730b39-7ea0-4082-b11a-b862dbf15086",
"name": "Send a message",
"type": "n8n-nodes-base.gmail",
"position": [
0,
0
],
"parameters": {
"sendTo": "<YOUR-EMAIL-ID>",
"message": "Hey, your job search results with resume changes are ready in your sheet for today.\n\nHere's Gsheet Link: https://docs.google.com/spreadsheets/d/1YwGBu2A9APwEh_b9Yt_Y8WiSOLp0g7BF46qD1lehYx0/edit?usp=sharing",
"options": {
"senderName": "Job Automation Agent",
"appendAttribution": true
},
"subject": "Job search results",
"emailType": "text"
},
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
},
{
"id": "5e2e0d5d-fddd-4dbb-90fe-ac0e33baa49e",
"name": "Schedule Trigger",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
-2848,
16
],
"parameters": {
"rule": {
"interval": [
{
"triggerAtHour": 5
}
]
}
},
"typeVersion": 1.2
},
{
"id": "69adbd73-a7ab-48b8-b259-9f0a26e55133",
"name": "Parse Job Links",
"type": "n8n-nodes-base.httpRequest",
"position": [
-2432,
336
],
"parameters": {
"url": "={{ $json.jobs }}",
"options": {}
},
"typeVersion": 4.2
},
{
"id": "5a213aa6-a8f6-46c0-9a0f-a6dc2780c83d",
"name": "Extract HTML Job Details",
"type": "n8n-nodes-base.html",
"position": [
-1984,
336
],
"parameters": {
"options": {},
"operation": "extractHtmlContent",
"extractionValues": {
"values": [
{
"key": "Title",
"cssSelector": "div h1"
},
{
"key": "Company",
"cssSelector": "div span a"
},
{
"key": "Location",
"cssSelector": "div span[class*='topcard__flavor topcard__flavor--bullet']"
},
{
"key": "Description",
"cssSelector": "div.description__text.description__text--rich"
},
{
"key": "Job ID",
"attribute": "data-semaphore-content-urn",
"cssSelector": "a[data-item-type='semaphore']",
"returnValue": "attribute"
}
]
}
},
"typeVersion": 1.2
},
{
"id": "35c308fb-7936-4bf7-9068-1977b8d07e99",
"name": "Set Job Details",
"type": "n8n-nodes-base.set",
"position": [
-1776,
336
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "240418dc-3349-48d5-ba59-3aa590d71950",
"name": "Description",
"type": "string",
"value": "={{ $json.Description.replaceAll(/\\s+/g, \" \")}}"
},
{
"id": "7b24938f-8b47-488d-9f65-54d036dcffd5",
"name": "Job ID",
"type": "string",
"value": "={{ $json['Job ID'].split(\":\").last() }}"
},
{
"id": "d6addada-9e01-464f-a768-c19f6224c491",
"name": "Apply Link",
"type": "string",
"value": "={{ \"https://www.linkedin.com/jobs/view/\"+ $json['Job ID'].split(\":\").last() }}"
},
{
"id": "21c89d3a-c8b6-44eb-9719-9ae5716a7c76",
"name": "Title",
"type": "string",
"value": "={{ $json.Title }}"
},
{
"id": "da713845-9a81-486e-bff2-1613105e424d",
"name": "Company",
"type": "string",
"value": "={{ $json.Company }}"
},
{
"id": "1a9a31dd-8d51-4f35-b6b6-ca8a348de5d7",
"name": "Location",
"type": "string",
"value": "={{ $json.Location }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "e2ff5c34-caca-4a07-9dfd-b05121cbe024",
"name": "Prepare Values for GSheet",
"type": "n8n-nodes-base.set",
"position": [
-1024,
336
],
"parameters": {
"mode": "raw",
"options": {},
"jsonOutput": "={{ (() => {\n // 0) Read model output from AI Agent node\n const raw = String(($('Job Matching Agent').item.json.output ?? ''));\n\n // 1) Strip code fences\n let s = raw\n .replace(/`{2,3}(?:json)?\\s*/gi, '')\n .replace(/\\s*`{3}\\s*$/gi, '');\n\n // 1b) Normalize curly quotes (Gemini sometimes emits them)\n s = s.replace(/[\\u201C\\u201D]/g, '\"').replace(/\\u2019/g, \"'\");\n\n // 2) Cut at END_OF_JSON if present\n const endIdx = s.indexOf('END_OF_JSON');\n if (endIdx !== -1) s = s.slice(0, endIdx);\n\n // 3) Extract first complete { ... } by brace counting\n const start = s.indexOf('{');\n if (start < 0) return {};\n let depth = 0, inStr = false, esc = false, end = -1;\n for (let i = start; i < s.length; i++) {\n const ch = s[i];\n if (inStr) {\n if (esc) { esc = false; }\n else if (ch === '\\\\') { esc = true; }\n else if (ch === '\"') { inStr = false; }\n } else {\n if (ch === '\"') inStr = true;\n else if (ch === '{') depth++;\n else if (ch === '}') { depth--; if (depth === 0) { end = i + 1; break; } }\n }\n }\n if (end < 0) return {};\n\n const cleaned = s.slice(start, end).trim();\n\n try {\n return JSON.parse(cleaned); // JSON mode expects an OBJECT\n } catch (e) {\n // As a last resort, return an empty object to keep the run alive\n return {};\n }\n})() }}\n"
},
"typeVersion": 3.4
},
{
"id": "0515f8f7-529f-4bca-8f7d-8a86e0796a46",
"name": "Update Rows with Job Detials",
"type": "n8n-nodes-base.googleSheets",
"position": [
-464,
336
],
"parameters": {
"columns": {
"value": {
"Link": "={{ $('Set Job Details').item.json['Apply Link'] }}",
"Score": "={{ $('Prepare Values for GSheet').item.json.match_score }}",
"Title": "={{ $('Set Job Details').item.json.Title }}",
"Skills": "={{ $('Prepare Values for GSheet').item.json.resume_analysis.core_skills }}",
"Locaton": "={{ $('Set Job Details').item.json.Location }}",
"Company ": "={{ $('Set Job Details').item.json.Company }}",
"Cover Letter": "={{ $('Prepare Values for GSheet').item.json.cover_letter }}",
"Improvements": "={{ $json.output }}"
},
"schema": [
{
"id": "Title",
"type": "string",
"display": true,
"required": false,
"displayName": "Title",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Company ",
"type": "string",
"display": true,
"required": false,
"displayName": "Company ",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Locaton",
"type": "string",
"display": true,
"required": false,
"displayName": "Locaton",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Link",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Link",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Score",
"type": "string",
"display": true,
"required": false,
"displayName": "Score",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Cover Letter",
"type": "string",
"display": true,
"required": false,
"displayName": "Cover Letter",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Skills",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Skills",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Improvements",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Improvements",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [
"Link"
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "appendOrUpdate",
"sheetName": {
"__rl": true,
"mode": "list",
"value": 11035642,
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1YwGBu2A9APwEh_b9Yt_Y8WiSOLp0g7BF46qD1lehYx0/edit#gid=11035642",
"cachedResultName": "Result"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "1YwGBu2A9APwEh_b9Yt_Y8WiSOLp0g7BF46qD1lehYx0",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1YwGBu2A9APwEh_b9Yt_Y8WiSOLp0g7BF46qD1lehYx0/edit?usp=drivesdk",
"cachedResultName": "Job Search N8N"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.7
},
{
"id": "a4894e3d-e837-40db-ab68-9503ecc3ea39",
"name": "Job Matching Agent",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
-1504,
336
],
"parameters": {
"text": "=You are a precise job-matching assistant.\n\nReturn ONE JSON object wrapped in ```json fences, followed by the line END_OF_JSON.\nNo extra prose. No Markdown inside the JSON. No comments.\n\nINPUTS\njob_description: {{ $json.Description }}\nmy_resume: {{ $node[\"Extract from File\"].data.text }}\n\nTASKS\n1) Parse job_description \u2192 job_analysis with keys:\n title (string), company (string), must_have_skills (string[]), nice_to_have_skills (string[]),\n responsibilities (string[]), years_of_experience (string), education_certifications (string),\n location_constraints (string), domain_industry_focus (string), tech_stack (string[]), measurable_kpis (string[])\n\n2) Parse my_resume \u2192 resume_analysis:\n core_skills (string[]),\n tools_tech { programming_languages[], frontend_technologies[], backend_technologies[], databases_devops[] },\n years_of_experience_key_areas (object of short strings),\n accomplishments_with_metrics (string[]),\n education_certs (string[]), domains (string[]), roles_titles (string[]),\n leadership_collaboration (string[]), location_work_auth (string)\n\n3) Scoring (integer 0\u2013100):\n - Skills/Tools overlap: 40\n - Relevant experience & seniority: 25\n - Responsibilities alignment: 15\n - Education/Certs fit: 10\n - Domain/industry fit: 5\n - Logistics (location/work auth/availability): 5\n Allow partial credit; deduct up to 10 via red_flags. Clamp to [0,100], integer.\n\n4) Explain the score:\n For each bucket, provide 1\u20133 concise evidence bullets. Cite \"JD\" or \"Resume\" and include short quoted fragments (escape quotes).\n\n5) Gaps & Suggestions:\n List missing/weak requirements with 1\u20132 concrete upskilling steps per gap.\n\n6) Cover letter:\n 150\u2013220 words (2\u20134 short paragraphs), tailored to the role/company.\n Concrete impacts; no greeting/signature. JSON-safe: escape all \" as \\\", use \\n for newlines.\n\nSTRICT CONTENT RULES (to prevent invalid JSON)\n- Do NOT paste raw paragraphs, markdown (**bold**, lists), headings, or multi-line blocks into any array fields.\n- Every array element must be a short phrase (\u2264 140 characters), single line, no line breaks, no asterisks, no bullets.\n- If a JD section is long, summarize into short phrases before placing into arrays.\n- Do NOT include unrelated job text inside arrays or objects. Keep each value semantically atomic.\n- Never invent company/title; use \"\" if unknown.\n- No trailing commas anywhere.\n\nSTRICT OUTPUT RULES\n- Output exactly the following schema (keys and types). No extra keys.\n\nSCHEMA\n```json\n{\n \"job_analysis\": {\n \"title\": \"\",\n \"company\": \"\",\n \"must_have_skills\": [],\n \"nice_to_have_skills\": [],\n \"responsibilities\": [],\n \"years_of_experience\": \"\",\n \"education_certifications\": \"\",\n \"location_constraints\": \"\",\n \"domain_industry_focus\": \"\",\n \"tech_stack\": [],\n \"measurable_kpis\": []\n },\n \"resume_analysis\": {\n \"core_skills\": [],\n \"tools_tech\": {\n \"programming_languages\": [],\n \"frontend_technologies\": [],\n \"backend_technologies\": [],\n \"databases_devops\": []\n },\n \"years_of_experience_key_areas\": {},\n \"accomplishments_with_metrics\": [],\n \"education_certs\": [],\n \"domains\": [],\n \"roles_titles\": [],\n \"leadership_collaboration\": [],\n \"location_work_auth\": \"\"\n },\n \"match_score\": 0,\n \"score_explanation\": [\n { \"category\": \"Skills/Tools overlap (40 points)\", \"score\": 0, \"evidence\": [] },\n { \"category\": \"Relevant experience depth & seniority (25 points)\", \"score\": 0, \"evidence\": [] },\n { \"category\": \"Responsibilities alignment (15 points)\", \"score\": 0, \"evidence\": [] },\n { \"category\": \"Education/Certs fit (10 points)\", \"score\": 0, \"evidence\": [] },\n { \"category\": \"Domain/industry fit (5 points)\", \"score\": 0, \"evidence\": [] },\n { \"category\": \"Logistics (location, work auth, availability) (5 points)\", \"score\": 0, \"evidence\": [] }\n ],\n \"red_flags\": [],\n \"gaps_and_suggestions\": [\n { \"gap\": \"\", \"suggestion\": \"\" }\n ],\n \"cover_letter\": \"\"\n}\n",
"options": {},
"promptType": "define"
},
"typeVersion": 2.2
},
{
"id": "875ab178-a76c-4d9d-a232-393aacb701aa",
"name": "Resume Editor Agent",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
-784,
336
],
"parameters": {
"text": "You are a ruthless resume editor. Compare the inputs and output ONLY crisp, point-wise changes to improve job fit.\n\nInputs:\n- job_description: {{ $json.Description }}\n- my_resume: {{ $('Extract from File').item.json.text }}\n\nInstructions:\n- Output a numbered list; highest-impact first.\n- One line per point; <= 14 words.\n- Start each line with a tag: [ADD], [REMOVE], [REWRITE], [ORDER], [QUANTIFY], [KEYWORDS], [FORMAT], [FOCUS].\n- Base every point on gaps vs. the job_description; do not invent experience.\n- Prefer concrete actions: skills to add, bullets to rewrite, sections to reorder/remove.\n- Include one line: 'Missing keywords: term1, term2, ...' (only if any).\n- No intros, explanations, code fences, or extra text - points only.\n\nOutput: points only, exactly as specified above.",
"options": {},
"promptType": "define"
},
"typeVersion": 2.2
},
{
"id": "ac5fb74b-c250-4449-a825-fb6d91dcddea",
"name": "Config",
"type": "n8n-nodes-base.set",
"position": [
-2608,
16
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "fc27f286-84e9-48a2-bd32-61ec30a43b41",
"name": "maxJobsPerSearch",
"type": "string",
"value": "2"
},
{
"id": "6ac9f389-d1d6-449a-aa8a-b09250edc5dc",
"name": "preferredRemote",
"type": "string",
"value": "Remote"
},
{
"id": "d8af5579-f864-4a0d-8392-194970dfe7f6",
"name": "preferredEasyApply",
"type": "string",
"value": "No"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "ba3e791b-0916-4d6e-ad95-a857b9538dd4",
"name": "Gemini Model",
"type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
"position": [
-1888,
176
],
"parameters": {
"options": {}
},
"credentials": {
"googlePalmApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "4003acdf-aaca-4e5f-95f6-49361ad4222b",
"name": "Resume Analyzer Agent",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
-1872,
16
],
"parameters": {
"text": "=You are a resume analysis assistant that creates LinkedIn job search filters.\n\nRead this resume text:\n\n{{ $node[\"Extract from File\"].data.text }}\n\nBased on this resume, create up to 5 suitable job search filters for LinkedIn.\n\nReturn JSON ONLY in this format:\n\n{\n \"searches\": [\n {\n \"keyword\": \"Job title or main skill\",\n \"location\": \"Country or city (e.g. India, United Kingdom)\",\n \"experience_level\": \"Internship or Entry level or New Grad\"\n }\n ]\n}\n\nRules:\n- Always give between 3 and 5 searches.\n- Use only these exact values:\n - experience_level: \"Internship\", \"Entry level\", \"New Grad\"\n - remote: \"Remote\", \"Hybrid\", \"On-Site\"\n - easy_apply: \"Yes\" or \"No\"\n- Pick realistic keywords based on the resume (e.g. \"Gen AI Engineer\", \"Automation Engineer\", etc).\n- Do not include any explanation text, just valid JSON.\n",
"options": {},
"promptType": "define"
},
"typeVersion": 3
},
{
"id": "9d2e5d51-9094-4dd3-b8ab-c6bedcc69c6a",
"name": "Build Search Rows",
"type": "n8n-nodes-base.code",
"position": [
-1536,
16
],
"parameters": {
"jsCode": "// CONFIG values\nconst preferredRemote = $node[\"Config\"].json.preferredRemote || \"\";\nconst preferredEasyApply = $node[\"Config\"].json.preferredEasyApply || \"\";\nconst maxJobs = $node[\"Config\"].json.maxJobsPerSearch || 10;\n\n// Get AI Raw Output\nif (!items.length || !items[0].json.output) {\n throw new Error('Resume Analyzer Agent returned no data.');\n}\n\nlet raw = items[0].json.output.toString().trim();\n\nraw = raw\n .replace(/^```json/i, '')\n .replace(/^```/, '')\n .replace(/```$/, '')\n .trim();\n\nlet data;\ntry {\n data = JSON.parse(raw);\n} catch (error) {\n throw new Error('JSON parse error: ' + error.message + \"\\nRaw: \" + raw);\n}\n\nconst searches = data.searches || [];\n\n// MAP OUTPUT + APPLY CONFIG PREFERENCES\nreturn searches.map(s => ({\n json: {\n Keyword: s.keyword || '',\n Location: s.location || '',\n 'Experience Level': s.experience_level || '',\n\n // Apply Preferred Remote if config set else use AI\n Remote: preferredRemote !== \"\" ? preferredRemote : (s.remote || ''),\n\n // Apply Preferred Easy Apply if config set else use AI\n 'Easy Apply': preferredEasyApply !== \"\" ? preferredEasyApply : (s.easy_apply || ''),\n\n // Pass max jobs forward to next nodes\n maxJobsPerSearch: maxJobs\n }\n}));"
},
"typeVersion": 2
},
{
"id": "d9627174-abd8-485b-b353-37d894f39506",
"name": "Append row in sheet",
"type": "n8n-nodes-base.googleSheets",
"position": [
-1344,
16
],
"parameters": {
"columns": {
"value": {
"Remote": "={{ $json.Remote }}",
"Keyword": "={{ $json.Keyword }}",
"Location": "={{ $json.Location }}",
"Easy Apply": "={{ $json[\"Easy Apply\"] }}",
"Experience Level": "={{ $json[\"Experience Level\"] }}"
},
"schema": [
{
"id": "Keyword",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Keyword",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Location",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Location",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Experience Level",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Experience Level",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Remote",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Remote",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Easy Apply",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Easy Apply",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1YwGBu2A9APwEh_b9Yt_Y8WiSOLp0g7BF46qD1lehYx0/edit#gid=0",
"cachedResultName": "Filter"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "1YwGBu2A9APwEh_b9Yt_Y8WiSOLp0g7BF46qD1lehYx0",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1YwGBu2A9APwEh_b9Yt_Y8WiSOLp0g7BF46qD1lehYx0/edit?usp=drivesdk",
"cachedResultName": "Job Search N8N"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.7
},
{
"id": "80a1814a-4e9b-4187-991d-fcf876860c11",
"name": "Limit Jobs",
"type": "n8n-nodes-base.code",
"position": [
-608,
16
],
"parameters": {
"jsCode": "const limit = $node[\"Config\"].json.maxJobsPerSearch || 10;\n\n// \"items\" is the array of job items coming into this node\nreturn items.slice(0, limit);"
},
"typeVersion": 2
},
{
"id": "3a96c5ed-ac86-408b-a2ae-583820be9ee0",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-3600,
-176
],
"parameters": {
"width": 656,
"height": 672,
"content": "## How it works\n\nThis workflow automates job discovery, resume matching, and application preparation using LinkedIn job listings and AI agents.\n\nOn a schedule, the workflow downloads your resume, analyzes it, and generates multiple LinkedIn job search filters tailored to your profile. These filters are saved to Google Sheets and then used to fetch recent job listings from LinkedIn.\n\nEach job listing is processed individually. The workflow extracts job details, compares the job description with your resume using AI, and calculates a match score. It also generates a tailored cover letter and clear resume improvement suggestions for that specific role.\n\nAll results are saved to Google Sheets so you can review, prioritize, and apply efficiently. Once the run completes, you receive an email notification with a link to the updated sheet.\n\nThis setup removes manual searching, screening, and comparison work, allowing you to focus only on high-fit opportunities.\n\n## Setup steps\n\n1. Make a copy of the Google Sheets template: \n https://docs.google.com/spreadsheets/d/1ia_82B7GMRdd896vo1md6a5VqXGxjma-QoBwBWDr77o/copy\n2. Upload your resume PDF to Google Drive.\n3. Connect Google Drive, Google Sheets, Gmail, and Gemini credentials.\n4. Update the **Config** node (remote preference, Easy Apply, job limit).\n5. Replace the Sheet IDs in the workflow with your copied sheet.\n6. Enable the Schedule Trigger to run automatically.\n"
},
"typeVersion": 1
},
{
"id": "63dec2e2-e2a1-4896-9ba4-98edd8f93c9b",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2848,
-176
],
"parameters": {
"color": 2,
"width": 352,
"height": 144,
"content": "## Trigger & Configuration\n\nControls when the workflow runs and defines global preferences such as remote type, Easy Apply preference, and maximum jobs per search.\n"
},
"typeVersion": 1
},
{
"id": "2ed477e9-9efd-463f-9f55-deac3bc54111",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2384,
-128
],
"parameters": {
"color": 3,
"width": 768,
"height": 96,
"content": "## Resume Intake & Analysis\n\nDownloads the resume, extracts text, and uses AI to understand skills, experience level, and suitable job roles.\n"
},
"typeVersion": 1
},
{
"id": "b89b2ab5-3ec3-41cc-badb-a2def5f249ba",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1536,
-144
],
"parameters": {
"color": 3,
"width": 704,
"height": 112,
"content": "## Job Search Generation\n\nBuilds LinkedIn job search filters from resume insights and stores them in Google Sheets for tracking and reuse.\n"
},
"typeVersion": 1
},
{
"id": "335b302a-af90-4cad-9bf0-0a16997c9360",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
-784,
-144
],
"parameters": {
"color": 4,
"width": 656,
"height": 112,
"content": "## Job Fetching & Parsing\n\nFetches job listings from LinkedIn, extracts job links, and parses detailed job information from each listing.\n"
},
"typeVersion": 1
},
{
"id": "363ac4a5-19ee-4258-8ae1-3393ca6aa5ab",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1536,
528
],
"parameters": {
"color": 7,
"width": 368,
"height": 144,
"content": "## Job Matching & Scoring\n\nCompares each job description with the resume to calculate a match score, identify gaps, and generate a tailored cover letter.\n"
},
"typeVersion": 1
},
{
"id": "337f3635-4a8c-4a55-9b36-8f52f99a29b2",
"name": "Sticky Note6",
"type": "n8n-nodes-base.stickyNote",
"position": [
-848,
544
],
"parameters": {
"color": 6,
"width": 512,
"height": 128,
"content": "## Resume Improvement Suggestions\n\nGenerates concise, actionable resume edits to improve alignment with each job role.\n"
},
"typeVersion": 1
},
{
"id": "ec42898d-9dad-4ece-a741-06814778f9b8",
"name": "Sticky Note7",
"type": "n8n-nodes-base.stickyNote",
"position": [
-80,
-48
],
"parameters": {
"color": 5,
"width": 272,
"height": 304,
"content": "## Results & Notification\n\n\n\n\n\n\n\n\n\n\n\n\nWrites job results, scores, and suggestions to Google Sheets and notifies the user by email when processing completes.\n"
},
"typeVersion": 1
}
],
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "4e81837a-db55-47f8-93d2-f2b35d1fea44",
"connections": {
"HTML": {
"main": [
[
{
"node": "Limit Jobs",
"type": "main",
"index": 0
}
]
]
},
"Wait": {
"main": [
[
{
"node": "Parse Job Links",
"type": "main",
"index": 0
}
]
]
},
"Config": {
"main": [
[
{
"node": "Download file",
"type": "main",
"index": 0
}
]
]
},
"Split Out": {
"main": [
[
{
"node": "Loop Over Items",
"type": "main",
"index": 0
}
]
]
},
"Limit Jobs": {
"main": [
[
{
"node": "Split Out",
"type": "main",
"index": 0
}
]
]
},
"Gemini Model": {
"ai_languageModel": [
[
{
"node": "Resume Analyzer Agent",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Download file": {
"main": [
[
{
"node": "Extract from File",
"type": "main",
"index": 0
}
]
]
},
"Loop Over Items": {
"main": [
[
{
"node": "Send a message",
"type": "main",
"index": 0
}
],
[
{
"node": "Wait",
"type": "main",
"index": 0
}
]
]
},
"Parse Job Links": {
"main": [
[
{
"node": "Extract HTML Job Details",
"type": "main",
"index": 0
}
]
]
},
"Set Job Details": {
"main": [
[
{
"node": "Job Matching Agent",
"type": "main",
"index": 0
}
]
]
},
"Schedule Trigger": {
"main": [
[
{
"node": "Config",
"type": "main",
"index": 0
}
]
]
},
"Build Search Rows": {
"main": [
[
{
"node": "Append row in sheet",
"type": "main",
"index": 0
}
]
]
},
"Extract from File": {
"main": [
[
{
"node": "Resume Analyzer Agent",
"type": "main",
"index": 0
}
]
]
},
"Job Matching Agent": {
"main": [
[
{
"node": "Prepare Values for GSheet",
"type": "main",
"index": 0
}
]
]
},
"Append row in sheet": {
"main": [
[
{
"node": "LinkedIn Search URL",
"type": "main",
"index": 0
}
]
]
},
"LinkedIn Search URL": {
"main": [
[
{
"node": "Fetch jobs from LinkedIn",
"type": "main",
"index": 0
}
]
]
},
"Resume Editor Agent": {
"main": [
[
{
"node": "Update Rows with Job Detials",
"type": "main",
"index": 0
}
]
]
},
"Resume Analyzer Agent": {
"main": [
[
{
"node": "Build Search Rows",
"type": "main",
"index": 0
}
]
]
},
"Extract HTML Job Details": {
"main": [
[
{
"node": "Set Job Details",
"type": "main",
"index": 0
}
]
]
},
"Fetch jobs from LinkedIn": {
"main": [
[
{
"node": "HTML",
"type": "main",
"index": 0
}
]
]
},
"Google Gemini Chat Model": {
"ai_languageModel": [
[
{
"node": "Job Matching Agent",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Google Gemini Chat Model1": {
"ai_languageModel": [
[
{
"node": "Resume Editor Agent",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Prepare Values for GSheet": {
"main": [
[
{
"node": "Resume Editor Agent",
"type": "main",
"index": 0
}
]
]
},
"Update Rows with Job Detials": {
"main": [
[
{
"node": "Loop Over Items",
"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.
gmailOAuth2googleDriveOAuth2ApigooglePalmApigoogleSheetsOAuth2Api
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow helps you find and evaluate job opportunities automatically, without spending hours searching and comparing roles. It uses your resume to look for relevant jobs on LinkedIn, checks how well each role matches your profile, and organises everything neatly in Google…
Source: https://n8n.io/workflows/12014/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
The Multi-Model Agency Content Engine is a high-performance editorial system designed for agencies. It solves the "blank page" problem by alternating between real-world social proof and strategic expe
When I was applying for roles, I learned the hard way that a one-size-fits-all resume never wins. I obsessively tailored my resume for each posting, reflecting the exact scope, impact, and stack the t
LinkedIn_Job_Hunt_and_Cover_Letter. Uses outputParserStructured, outputParserAutofixing, googleDrive, agent. Scheduled trigger; 85 nodes.
This n8n automation workflow automates the creation, scripting, production, and posting of YouTube videos. It leverages AI (OpenAI), image generation (PIAPI), video rendering (Shotstack), and platform
This workflow automates the creation, rendering, approval, and posting of TikTok-style POV (Point of View) videos to Instagram, with cross-posting to Facebook and YouTube. It eliminates manual video p