Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 

Repository files navigation

LinkedIn Job Radar

An automated n8n workflow that scrapes LinkedIn for Product Manager job listings in Bangalore, extracts structured data using AI, deduplicates with vector search, and surfaces similar job recommendations. All hands-free, runs every 12 hours.

I built this because I was tired of manually checking LinkedIn every day during my job search. Now it just runs in the background, picks up new PM listings, pulls out the important stuff (skills needed, experience, urgency), finds similar roles I might have missed, and dumps everything into a Google Sheet I can check whenever I want.

If you are a PM (or anyone really) looking for jobs and want to automate the grunt work, this is for you. No software background needed. I will walk you through every step.


What Does This Actually Do?

Here is the simple version. Every 12 hours, this workflow:

  1. Scrapes LinkedIn for PM job listings in Bangalore using Apify (no LinkedIn login needed)
  2. Filters out junk (project managers, supply chain roles, etc.) using keyword matching + Gemini AI
  3. Checks if the job was already processed before (dedup using Qdrant vector database)
  4. Sends new jobs to Gemini AI to extract 21 structured fields (skills, experience, salary, urgency score, etc.)
  5. Creates a skill embedding (a numerical fingerprint of the job's required skills)
  6. Saves everything to Qdrant and finds similar jobs already in the database
  7. Logs it all to Google Sheets with two tabs: Job Listings and Similar Jobs

You end up with a clean, structured spreadsheet of PM jobs that updates itself.


Tech Stack

Tool What It Does Here
n8n (self-hosted on Railway) Runs the whole workflow. Think of it as your automation engine
Apify Scrapes LinkedIn job listings without needing your LinkedIn login
Gemini 2.5 Flash Reads job descriptions and extracts structured fields using AI
Gemini embedding-001 Converts skills into numerical vectors for finding similar jobs
Qdrant Cloud Vector database that handles deduplication and similarity search
Google Sheets Your output dashboard. Two tabs: Job Listings and Similar Jobs

MCP & Skills Setup for Claude Code (You'll need to install CLaude on Desktop for this)

  1. https://github.com/czlonkowski/n8n-mcp
  2. https://github.com/czlonkowski/n8n-skills

Paste these repo links in Claude Code and ask it to install the MCP on your computer while you sit back and relax! (That easy)


Setup Guide (Detailed, Beginner-Friendly)

This is the part most tutorials skip or gloss over. I am going to walk you through every single thing you need to set up, including the parts that took me hours to figure out.

Step 1: Get Your API Keys

You need 4 services. All of them have free tiers that are more than enough.

Apify (LinkedIn Scraper)

  1. Go to apify.com and sign up (free tier gives you $5/month in credits, plenty for this)
  2. Once logged in, click on your profile icon (top right) and go to Settings
  3. In the left sidebar, click Integrations
  4. You will see your Personal API Token. Copy it and save it somewhere safe
  5. That is it. The workflow uses the harvestapi/linkedin-job-search actor which costs about $0.10-0.20 per run

Gemini API Key

  1. Go to Google AI Studio
  2. Sign in with your Google account
  3. Click Create API Key
  4. Select any Google Cloud project (or let it create one for you)
  5. Copy the API key. This one key works for both Gemini 2.5 Flash (text extraction) and Gemini embedding-001 (skill vectors)
  6. Free tier gives you 15 requests per minute which is enough for this workflow

Qdrant Cloud (Vector Database)

  1. Go to cloud.qdrant.io and sign up
  2. Click Create Cluster. Pick the free tier (1GB storage, more than enough)
  3. Choose a region close to you (I used EU Central)
  4. Once the cluster is created, go to Data Access Control (in the cluster dashboard)
  5. Create an API Key. Copy both the key and your cluster URL (looks like xxxx-xxxx.eu-central-1-0.aws.cloud.qdrant.io)
  6. Now you need to create the collection. Go to the Dashboard tab of your cluster, or use the API:
curl -X PUT "https://YOUR_CLUSTER_URL:6333/collections/job_listings" \
  -H "api-key: YOUR_QDRANT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "vectors": {
      "size": 768,
      "distance": "Cosine"
    }
  }'

Replace YOUR_CLUSTER_URL and YOUR_QDRANT_API_KEY with your actual values. This creates the collection where all your job data will be stored.

Google Sheets + Google Cloud Console (The Tricky Part)

This is where most people get stuck. n8n needs OAuth2 credentials to write to Google Sheets on your behalf. Here is the exact process:

Part A: Create the Google Sheet

  1. Go to sheets.google.com and create a new spreadsheet
  2. Name it whatever you want (e.g., "LinkedIn Job Radar")
  3. Create two tabs at the bottom:
    • Job Listings (with these column headers in row 1): post_id, role, company_name, location, primary_skills, secondary_skills, must_have_skills, years_of_experience, looking_for_college_students, intern, salary_package, email, phone, hiring_intent, author_name, author_linkedin_url, post_url, date_posted, date_processed, keyword_matched, hiring_urgency_score
    • Similar Jobs (with these headers): job_title, job_company, job_skills, job_url, similar_title, similar_company, similar_skills, similar_url, similarity_score, date_found
  4. Copy the Sheet ID from the URL. It is the long string between /d/ and /edit in the URL bar. You will need this later.

Part B: Set Up Google Cloud Console (OAuth2 Credentials)

This is the painful part. Buckle up.

  1. Go to console.cloud.google.com
  2. If you do not have a project, create one. Name it anything (e.g., "n8n Automations")
  3. In the left sidebar, go to APIs & Services then Library
  4. Search for "Google Sheets API" and click Enable
  5. Now go to APIs & Services then OAuth consent screen
  6. Choose External as user type and click Create
  7. Fill in the required fields:
    • App name: anything (e.g., "n8n LinkedIn Radar")
    • User support email: your email
    • Developer contact email: your email
    • Leave everything else as default
  8. Click Save and Continue
  9. On the Scopes page, click Add or Remove Scopes
  10. Search for https://www.googleapis.com/auth/spreadsheets and check the box
  11. Also add https://www.googleapis.com/auth/drive.file
  12. Click Update, then Save and Continue
  13. On the Test Users page, click Add Users and add your own Google email
  14. Click Save and Continue, then Back to Dashboard

Now create the actual credentials:

  1. Go to APIs & Services then Credentials
  2. Click Create Credentials then OAuth client ID
  3. Application type: Web application
  4. Name: anything (e.g., "n8n Google Sheets")
  5. Under Authorized redirect URIs, add your n8n OAuth callback URL. This is your n8n URL + /rest/oauth2-credential/callback. For example: https://your-n8n-instance.up.railway.app/rest/oauth2-credential/callback
  6. Click Create
  7. You will see your Client ID and Client Secret. Copy both. You will need these in n8n.

Part C: Connect Google Sheets in n8n

  1. In n8n, open any Google Sheets node in the workflow
  2. Click on the Credential dropdown and select Create New
  3. Choose Google Sheets OAuth2 API
  4. Paste your Client ID and Client Secret from the Google Cloud Console
  5. Click Sign in with Google. A popup will appear asking you to authorize
  6. If you see a "This app isn't verified" warning, click Advanced then Go to [your app name] (unsafe). This is fine since it is your own app
  7. Grant the permissions and you should see a green "Connected" status
  8. This credential will now be available for all Google Sheets nodes in the workflow

Step 2: Self-Host n8n on Railway

You could run n8n locally, but self-hosting on Railway means your workflows run 24/7 without keeping your laptop open. Railway gives you a $5 free trial, and the n8n instance costs about $5/month after that.

  1. Go to railway.app and sign up (GitHub login works)
  2. From the dashboard, click New Project
  3. Click Deploy a Template
  4. Search for "n8n" and select the n8n (w/ Postgres) template. This gives you n8n + a Postgres database for storing your workflows
  5. Railway will ask you to configure some variables. The important ones:
    • N8N_ENCRYPTION_KEY: Set this to any random string (this encrypts your credentials). Write it down. If you lose this, all your saved credentials break
    • WEBHOOK_URL: Set to https://${RAILWAY_PUBLIC_DOMAIN} (Railway fills this in automatically)
  6. Click Deploy. Wait a few minutes for it to build
  7. Once deployed, click on the n8n service and you will see a public URL like n8n-production-xxxx.up.railway.app. That is your n8n instance

Important Railway Environment Variables

After your first deploy, add these in the n8n service's Variables tab. These fix common issues:

Variable Value Why
N8N_TRUST_PROXY true Railway uses a reverse proxy. Without this, n8n's rate limiter breaks and your workflows get stuck
N8N_RATE_LIMIT_ENABLED false Prevents the rate limiter from blocking your own workflow executions
N8N_RUNNERS_ENABLED true Enables task runners for Code nodes
EXECUTIONS_DATA_PRUNE true Auto-cleans old execution data so your Postgres does not fill up
EXECUTIONS_DATA_MAX_AGE 168 Keeps execution data for 7 days (168 hours) then prunes

After adding these, click Redeploy on the n8n service.

If your workflows get stuck and nothing runs:

This happened to me. Check these things:

  • Go to the n8n Executions tab and delete any stuck/crashed executions
  • Check Railway Deploy Logs for errors (look for ERR_ERL_UNEXPECTED_X_FORWARDED_FOR, that means you need the N8N_TRUST_PROXY variable)
  • Restart the n8n service from Railway (three-dot menu on the service, click Redeploy)

Step 3: Import and Configure the Workflow

  1. Download the linkedin_job_radar_workflow_public.json file from this repo
  2. Open your n8n instance
  3. Go to Workflows and click the Import button (or the "..." menu, then Import from File)
  4. Select the JSON file
  5. Now you need to replace the placeholder values. Open each of these nodes and update:

In the Apify node:

  • Replace YOUR_APIFY_API_TOKEN with your Apify token in the query parameters

In all Gemini nodes (Extract Job Fields, Embed Job Skills, and inside the Filter: PM Relevance Check code):

  • Replace YOUR_GEMINI_API_KEY with your Gemini API key

In all Qdrant nodes (Check Job Exists, Save Job, Find Similar Jobs):

  • Replace YOUR_QDRANT_CLUSTER_URL with your Qdrant cluster URL (include the port :6333)
  • Replace YOUR_QDRANT_API_KEY with your Qdrant API key in the headers

In all Google Sheets nodes:

  • Replace YOUR_GOOGLE_SHEET_ID with your Sheet ID (from the URL)
  • Connect your Google Sheets OAuth2 credential (the one you set up in Step 1)
  1. Save the workflow

Step 4: Test It

  1. Click Execute workflow. The Manual Trigger will fire and the whole pipeline should run
  2. The Apify node takes 1-3 minutes (it is scraping LinkedIn in real time)
  3. Watch the nodes light up as data flows through. If Gemini throws a 503 error, just run it again. Gemini 2.5 Flash gets overloaded sometimes. You can also switch to gemini-2.0-flash in the URL if it keeps happening
  4. Once done, check your Google Sheet. You should see new rows in both tabs

For production: Replace the Manual Trigger with a Schedule Trigger node set to run every 12 hours. Then activate the workflow (toggle in the top right). It will now run automatically.


Customizing for Your Job Search

This workflow is set up for Product Manager roles in Bangalore, but you can change it for anything:

  • Different job titles: Edit the jobTitles array in the Apify node's JSON body
  • Different location: Change locations and geoIds in the Apify node. Find your LinkedIn geoId by searching on LinkedIn and checking the URL
  • Different filters: Update the PM_KEYWORDS and DISQUALIFY_KEYWORDS arrays in the Filter: PM Relevance Check code node
  • More or fewer results: Change maxItems in the Apify node (higher = more Apify credits used)

How the Vector Search Works

This is the cool part. Every job gets two things stored in Qdrant:

  1. Payload: All 21 extracted fields as searchable metadata
  2. Vector: A 768-dimension numerical representation of the job's primary skills

When a new job is saved, the workflow runs a similarity search against all previously stored jobs. If another job has a skill vector with cosine similarity >= 0.70, it gets flagged as a "similar job" and logged to the Similar Jobs tab.

This means over time, as more jobs accumulate, the similar jobs feature gets better and better. You start seeing patterns like "hey, this Amazon PM role and this Flipkart PM role need almost the same skills."


Known Issues and Gotchas

  • Gemini 503 errors: Gemini 2.5 Flash gets overloaded during peak hours. The workflow has retry logic (3 attempts, 5s wait), but if it keeps failing, switch to gemini-2.0-flash in the URL
  • Apify rate limits: Free tier gives $5/month. Each run costs $0.10-0.20. Running every 12h with 20 maxItems will cost roughly $6-12/month. Reduce maxItems or increase the schedule interval if you want to stay on free tier
  • n8n stuck executions: If n8n crashes mid-workflow, executions get stuck as "running" forever. Fix: go to Executions tab, delete the stuck ones, restart n8n on Railway
  • Google Sheets OAuth expires: Sometimes the OAuth token expires. Just re-authenticate by clicking the credential in n8n and signing in again
  • Workflow runtime: With 20 jobs, the workflow takes 30-60 minutes because jobs are processed sequentially (each one makes ~8 API calls). This is fine for a scheduled workflow

21 Fields Extracted Per Job

# Field What It Is
1 post_id Unique LinkedIn job ID
2 role Job title (e.g., Senior Product Manager)
3 company_name Who is hiring
4 location Job location
5 primary_skills Core skills they want
6 secondary_skills Nice-to-have skills
7 must_have_skills Non-negotiable requirements
8 years_of_experience Experience range (e.g., 3-5 years)
9 looking_for_college_students Boolean flag
10 intern Boolean flag
11 salary_package Salary range if mentioned
12 email Application email if listed
13 phone Contact phone if listed
14 hiring_intent One-sentence summary of the role
15 author_name Recruiter or company name
16 author_linkedin_url Recruiter's LinkedIn profile
17 post_url Direct link to the job listing
18 date_posted When the job was posted on LinkedIn
19 date_processed When our workflow picked it up
20 keyword_matched Search keywords that matched
21 hiring_urgency_score AI-scored urgency from 0-10

Architecture

Manual/Schedule Trigger
        |
        v
Apify: Scrape LinkedIn (8 PM title variations x Bangalore)
        |
        v
Code: Flatten + filter to Bangalore/India/Remote only
        |
        v
Code: PM Relevance Filter (keywords + Gemini fallback)
        |
        v
Loop: Process each job one by one
        |
        v
Qdrant: Already processed? -- Yes --> Skip, next job
        |
        No
        v
Gemini 2.5 Flash: Extract 21 structured fields from JD
        |
        v
Gemini Embedding: Convert skills to 768-dim vector
        |
        v
Valid job? (has hiring intent) -- No --> Skip, next job
        |
        Yes
        v
Qdrant: Save job (vector + payload)
        |
        v
Qdrant: Find similar jobs (cosine similarity >= 0.7)
        |
        v
Google Sheets: Log to Similar Jobs tab
        |
        v
Google Sheets: Log to Job Listings tab
        |
        v
Loop back for next job

License

MIT


Built by Jatin during a job search. If this saves you time, star the repo.

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors