Track LinkedIn outreach. Generate AI messages. Ship faster.
A Chrome extension + Node.js backend that turns LinkedIn into a structured job-hunting pipeline with AI-powered outreach.
- Auto-scrape LinkedIn profiles when you visit any
/in/page β name, title, company, email, profile URL - Smart company detection β auto-classifies as startup vs MNC using a curated list
- Handles LinkedIn's SPA navigation with a
MutationObserver - Data is cached in the service worker so the popup opens instantly
- Add contacts pre-filled from the LinkedIn scrape β one click to save
- Filter & search by status, priority, company type, or free-text search
- 9 outreach statuses:
discovered β connection_sent β connected β messaged β email_sent β replied β in_process β rejected β on_hold - Priority levels (high / mid / low) and referral tracking
- Auto-timestamping β changing status auto-stamps
connectedAt,messagedAt,repliedAt, etc. - Duplicate guard β prevents saving the same LinkedIn URL twice
Generate context-aware outreach using your profile + the contact's info + an optional job description. The backend prioritizes the Google Gemini API (gemini-2.5-flash) and automatically falls back to the Cohere API (command-r) if there are errors or missing configurations.
| Type | What it generates | Constraints |
|---|---|---|
linkedin |
Connection request note | β€ 200 chars (LinkedIn hard limit) |
message |
LinkedIn DM (post-connect) | β€ 250 words, conversational |
email |
Cold outreach email | Subject + body, β€ 120 word body |
- Prompts are crafted to sound human β no "I hope this finds you well"
- Generated content is saved on the contact for later reference
A comprehensive, widescreen desktop React application synced with your database:
- Interactive Analytics: View visual stage splits (Discovered, Messaged, Replied), total outreach counts, response rates, and top target company allocations.
- Leads Table (CRUD): Advanced grid listing all contacts, search-as-you-type filter bars, multi-category pipelines, and editing modals to quickly modify lead details.
- Outreach Composer Console: Choose target contacts, compose formatted templates, copy text directly to clipboard, and check remaining generation credits.
- Excel Import / Export: Download Excel templates, populate contacts offline, drag and drop sheets to upload in bulk, or backup your collection in one click.
- Self-Hosted Key Bypass: Set up your own Gemini and Cohere keys in the browser Settings to unlock unlimited generation actions.
- Store your bio, resume content, skills, and projects β used automatically in every AI context prompt.
- Manage self-hosted LLM keys securely inside the profile panel.
jobhunt/
βββ backend/ β Node.js + Express + MongoDB
β βββ src/
β βββ app.js β Entry point (Express server)
β βββ config/ β DB connections & client SDK configs
β βββ models/ β Mongoose schemas (Contact, User)
β βββ controllers/ β API logic (Auth, Contacts, Import/Export, AI)
β βββ routes/ β Express routes mapping
β βββ services/ β Pure prompt building logic
β βββ utils/ β Middleware helpers (Auth, Async)
β
βββ extension/ β Chrome Extension (React + Vite + Monospace CSS)
β βββ public/ β Icons & MV3 manifest
β βββ src/
β β βββ background/ β Service worker tab handlers
β β βββ content/ β DOM scrapers injected into LinkedIn
β β βββ popup/ β Pop-up user views (Auth, Contact List, AI Compose)
β βββ vite.config.js
β
βββ frontend/ β Desktop Web Application (React + Vite)
βββ src/
β βββ components/ β UI layouts & Navigation Sidebars
β βββ pages/ β Workspace Views (Landing, Dashboard, Leads, AI, Settings)
β βββ utils/ β API client & design systems styling
βββ vite.config.js
| Layer | Technology |
|---|---|
| Extension | React 18, Vite 6, Chrome Manifest V3, Monospace CSS |
| Web App | React 18, Vite 6, Tailwind CSS v4, Modern Flexbox |
| Backend | Node.js, Express 4, MongoDB (Mongoose 8) |
| AI | Gemini (gemini-2.5-flash) with Cohere (command-r) fallback |
| Export | ExcelJS, Multer (multipart Excel file parsing) |
| Dev Tools | Nodemon, ESLint, vite-plugin-static-copy |
- Node.js v18+
- MongoDB β local instance or MongoDB Atlas cluster
- Gemini API Key β get one at aistudio.google.com
- Cohere API Key (Fallback) β get one at cohere.com
- Google Chrome (or any Chromium-based browser)
git clone https://github.com/<your-username>/jobhunt-extension.git
cd jobhunt-extensioncd backend
npm installCopy the example env and fill in your values:
cp .env.example .envEdit .env with your credentials:
PORT=8000
NODE_ENV=development
MONGODB_URI=mongodb+srv://<user>:<password>@<cluster>.mongodb.net/<dbname>
JWT_SECRET=change_me_to_a_random_secret_string
GEMINI_API_KEY=your_gemini_api_key_here
COHERE_API_KEY=your_cohere_api_key_hereStart the server:
npm run dev # nodemon (auto-restart)
# or
npm start # plain nodeThe API will be available at http://localhost:8000. Verify with:
curl http://localhost:8000/healthcd frontend
npm install
npm run devOpen http://localhost:5173 to view the beautiful Landing Page and Auth Dashboard portal.
cd extension
npm install
npm run build- Open
chrome://extensionsin your browser. - Enable Developer mode (top-right toggle switcher).
- Click Load unpacked.
- Select the build output
extension/distfolder.
- Navigate to any LinkedIn profile page (
https://www.linkedin.com/in/...). - Open the JobHunt Chrome Extension popup from the extensions tray.
- Profile fields are auto-populated in the panel. Save to pipeline database.
- Open your dashboard (
http://localhost:5173) to view graphs, import/export sheets, track pipeline progress, or generate outreach copies using customized context.
All endpoints are prefixed with /api. The server runs on port 8000 by default.
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/contacts |
List all contacts (supports ?status, ?priority, ?companyType, ?search, ?sort) |
GET |
/api/contacts/:id |
Get a single contact |
POST |
/api/contacts |
Create a contact (duplicate LinkedIn URL check) |
PUT |
/api/contacts/:id |
Update any field |
DELETE |
/api/contacts/:id |
Delete a contact |
GET |
/api/contacts/export |
Download all contacts as .xlsx |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/profile |
Get your profile |
PUT |
/api/profile |
Create or update your profile (upsert) |
| Method | Endpoint | Description |
|---|---|---|
POST |
/api/generate |
Generate a message. Body: { contactId, type, jobDescription? } |
type must be one of: linkedin | message | email
| Method | Endpoint | Description |
|---|---|---|
GET |
/health |
Returns { status: "ok", timestamp: "..." } |
| Field | Type | Notes |
|---|---|---|
name |
String | Required |
position |
String | Scraped from LinkedIn |
company |
String | Scraped from LinkedIn |
companyType |
Enum | startup / mnc / unknown |
linkedinUrl |
String | Required, unique |
email |
String | Passive scrape only |
status |
Enum | 9 values (see Features) |
priority |
Enum | high / mid / low |
referralStatus |
Enum | not_asked / asked / confirmed / not_available |
generatedMessage |
String | AI-generated LinkedIn note/DM |
generatedEmail |
String | AI-generated cold email body |
generatedEmailSubject |
String | AI-generated email subject |
notes |
String | Free-form notes |
source |
Enum | wellfound / ycombinator / linkedin / other |
For development, use npm run dev in the extension/ directory to get Vite's dev server. However, for testing as a Chrome extension, you must:
cd extension
npm run build # produces dist/Then reload the extension in chrome://extensions.
cd backend
npm run dev # nodemon watches for changesContributions are welcome! See CONTRIBUTING.md for setup instructions and guidelines.
Built for hustlers who track every connection, not just spray and pray. π