AI-powered job portal that fetches, matches, and applies to jobs — all on autopilot.
Features · Tech Stack · Quick Start · Full Setup · AI Setup · Usage · API · Contributing · Contributors · Disclaimer · License
JobbLoot is an automated job portal that scrapes Python developer jobs from RSS feeds, Technopark, and Cutshort, matches them against your profile using a weighted scoring engine, generates AI-powered cover letters, and sends applications via Gmail — all displayed on a modern React dashboard.
- Multi-source job fetching — RSS (3000+ jobs), Technopark (130+ jobs), Cutshort (300+ jobs) with parallel fetching (10/6 workers)
- Smart matching engine — weighted scoring: skills (60%), project relevance (20%), experience (15%), title (5%)
- Template cover letters — market-validated Problem-Solution format with 6 company-type templates (startup, enterprise, tech, fintech, AI, general)
- AI cover letters — provider-agnostic LLM integration (OpenAI, Groq, DeepSeek, Gemini, OpenRouter) with hallucination-proof validation
- Auto-apply — one-click apply from Job Detail page with generated cover letter + uploaded resume
- Batch apply — send applications to multiple jobs with one click via Gmail SMTP
- Parallel enrichment — email/salary enrichment runs with 8 workers for faster processing
- Real-time progress — fetcher progress bar with resume polling on mount
- Profile management — editable from the dashboard, takes effect on next fetch cycle
- Skill gap analysis — filters out jobs requiring >40% unknown skills
- Experience + salary + location filtering — respects your preferences
- Coverage warnings — shows which job requirements your cover letter misses
- Modern SPA — React 19 + TypeScript 6 + Vite 8 dashboard with Flat Design 2.0
| Layer | Technology |
|---|---|
| Backend | Django 5, Django REST Framework 3.17, Django Channels 4.3 |
| Frontend | React 19, TypeScript 6, Vite 8, React Router 7, Axios |
| Charts | Chart.js 4 |
| AI / LLM | OpenAI-compatible API (OpenAI, Groq, DeepSeek, Gemini, OpenRouter) |
| HTTP Client | httpx (HTTP/2) for scraping, Axios for frontend |
| HTML Parsing | BeautifulSoup 4, lxml |
| Database | SQLite3 (dev), PostgreSQL (prod) |
| WebSocket | Django Channels + Daphne (ASGI) |
| Encryption | Fernet (cryptography) for credential storage |
| Package Manager | uv (Python), npm (frontend) |
| Linter | Ruff (Python), Oxlint (TypeScript) |
| Testing | Pytest + pytest-django |
Requirements: Python 3.10+, Node.js 18+ (uv auto-installed if missing)
# 1. Clone
git clone https://github.com/dennisjoseph2025/JobbLoot.git
cd JobbLoot
# 2. One-command setup (installs everything)
python setup.py
# 3. Edit your config files (required before first run)
# .env — set EMAIL_USER, EMAIL_PASS, DJANGO_SECRET_KEY
# config/profile.py — fill in your real profile data
# 4. Run (development)
python manage.py runserver # Terminal 1 — Django on :8000
cd frontend && npm run dev # Terminal 2 — Vite on :5173Open http://localhost:5173 — the Vite dev server proxies API calls to Django automatically.
| Step | Action |
|---|---|
| 1 | Checks Python >= 3.10 and Node.js >= 18 |
| 2 | Installs uv if missing |
| 3 | Installs Python dependencies (uv sync) |
| 4 | Copies .env.example → .env (skips if exists) |
| 5 | Copies config/profile.example.py → config/profile.py (skips if exists) |
| 6 | Runs database migrations |
| 7 | Installs frontend dependencies (npm install) |
git clone https://github.com/dennisjoseph2025/JobbLoot.git
cd JobbLootUsing uv (recommended):
uv syncOr with pip:
python -m venv .venv
.venv\Scripts\activate # Windows
# source .venv/bin/activate # macOS/Linux
pip install -e ".[dev]"cd frontend
npm install
cd ..copy .env.example .env # Windows
# cp .env.example .env # macOS/LinuxEdit .env with your values:
# Required
DJANGO_SECRET_KEY=your-random-secret-key
EMAIL_USER=your-email@gmail.com
EMAIL_PASS=your-gmail-app-password
# Optional — resume (legacy, upload via dashboard instead)
# RESUME_PATH=resume/Your_Resume.pdf
# Optional — AI cover letter generation (configure via dashboard > Profile > AI)
# AI_PROVIDER=openai
# AI_API_BASE_URL=https://api.openai.com/v1
# AI_API_KEY=sk-your-api-key
# AI_MODEL=gpt-4o-mini
# Optional — Production database
# DB_ENGINE=django.db.backends.postgresql
# DB_NAME=jobbloot
# DB_USER=postgres
# DB_PASSWORD=your-db-password
# DB_HOST=localhost
# DB_PORT=5432copy config\profile.example.py config\profile.py # Windows
# cp config/profile.example.py config/profile.py # macOS/LinuxEdit config/profile.py with your real info:
CANDIDATE_PROFILE = {
"name": "John Doe",
"email": "john@example.com",
"phone": "+91-9876543210",
"experience_min": 2,
"experience_max": 5,
"skills": {
"backend": ["python", "django", "fastapi", "postgresql"],
"frontend": ["react", "javascript", "typescript"],
"ai_llm": ["langchain", "openai api"],
"cloud": ["aws", "docker"],
"devops": ["github actions", "nginx"],
"tools": ["git", "linux", "redis"],
},
"projects": [
{
"name": "ProjectX",
"description": "Real-time analytics dashboard",
"tech": ["django", "channels", "react", "postgresql"],
}
],
"looking_for": ["python developer", "django developer", "full stack developer"],
}You can also edit your profile from the dashboard at http://localhost:8000/profile/ — changes take effect on the next fetch cycle without restarting.
python manage.py migratepython manage.py runserver # Terminal 1 — Django on :8000
cd frontend && npm run dev # Terminal 2 — Vite on :5173Then open http://localhost:5173.
JobbLoot generates AI-powered cover letters using any OpenAI-compatible LLM provider. Configure it from the dashboard — no env vars needed.
- Open http://localhost:8000/profile/
- Switch to the AI tab
- Select your provider from the dropdown (presets auto-fill the base URL and model)
- Enter your API key — encrypted with Fernet before saving to the database
- Click Save
That's it. Open any job in the Apply Queue or Jobs page and click Generate Cover Letter.
| Provider | Base URL | Default Model | Notes |
|---|---|---|---|
| OpenAI | api.openai.com/v1 |
gpt-4o-mini |
Best balance of quality and cost |
| Groq | api.groq.com/openai/v1 |
llama-3.3-70b-versatile |
Fastest inference, free tier available |
| DeepSeek | api.deepseek.com/v1 |
deepseek-chat |
Cheapest, great for batch jobs |
| Gemini | generativelanguage.googleapis.com/v1beta/openai/ |
gemini-2.5-flash |
Google's free tier is generous |
| OpenRouter | openrouter.ai/api/v1 |
auto |
Access to 100+ models via one key |
User clicks "Generate" on Job Detail page
↓
Backend sends system + user prompts to configured LLM
↓
Response parsed — think tags stripped (DeepSeek R1, QwQ, o1, o3, o4-mini)
↓
Deterministic validation layer:
├── Checks salutation ("Dear Hiring Manager" / "Dear [Company]")
├── Checks signature (candidate name, phone, email)
├── Blocks forbidden skills (skills NOT in your profile)
├── Blocks ungrounded claims (vague testing/monitoring/security claims)
├── Blocks project misattribution (projects only mentioned in YOUR profile)
├── Blocks acronym expansion ("REST" → "Representational State Transfer")
└── Coverage warning if >30% of job requirements are unaddressed
↓
Letter saved to Application.cover_letter_text
↓
User reviews, edits if needed, then clicks "Apply" to send via Gmail
If you prefer env vars over the dashboard, add these to .env:
AI_PROVIDER=openai
AI_API_BASE_URL=https://api.openai.com/v1
AI_API_KEY=sk-your-api-key
AI_MODEL=gpt-4o-miniNote: Dashboard settings take priority over env vars. If you've saved a config in the dashboard, the env vars are ignored.
| Use case | Recommended model | Why |
|---|---|---|
| Daily batch (many jobs) | deepseek-chat |
~$0.14/M tokens — cheapest option |
| Quality over cost | gpt-4o-mini |
Best instruction following |
| Free tier | gemini-2.5-flash |
15 RPM free, good quality |
| Fast iteration | llama-3.3-70b-versatile (Groq) |
Sub-second inference |
JobbLoot automatically detects reasoning models (DeepSeek R1, QwQ, o1, o3, o4-mini) and:
- Disables extended reasoning in the API payload
- Strips `` tags from the response
- Falls back to a stricter retry prompt if output is malformed
python manage.py run_allStarts the Django server on http://localhost:8000 and runs the fetch-match cycle every 60 minutes.
# Fetch jobs once (no server)
python manage.py run_fetcher
# Run scheduler only (fetches every N minutes, no dashboard)
python manage.py run_scheduler
# Run dashboard only (no auto-fetching)
python manage.py runservercd frontend
npm run dev # Start Vite dev server (hot reload)
npm run build # Build for production
npm run lint # Run Oxlint
npm run preview # Preview production buildFetch (RSS / Technopark / Cutshort — parallel fetching)
↓
RawJob (Data Lake — deduplicated by source + uid)
↓
Matcher (weighted scoring: skills 60%, projects 20%, experience 15%, title 5%)
↓
Job (Data Warehouse — matched jobs with scores)
↓
JobEvent (CDC — lifecycle events for every state change)
↓
DailyStats (Data Mart — aggregated daily metrics)
Auto-Apply → Template Cover Letter (Problem-Solution format) + Static Resume → Gmail SMTP → Application
Batch Apply → User selects jobs → Cover Letter (AI or template) → Gmail SMTP → Applications
All endpoints are under /api/v1/:
| Endpoint | Method | Description |
|---|---|---|
/api/v1/jobs/ |
GET | List jobs (paginated, filterable by status/location/salary/search) |
/api/v1/jobs/<id>/ |
GET | Job detail with match breakdown, skill gaps, cover letter |
/api/v1/jobs/<id>/apply/ |
POST | Apply to a single job |
/api/v1/jobs/<id>/generate-cover-letter/ |
POST | Generate AI cover letter |
/api/v1/jobs/<id>/generate-template-cover-letter/ |
POST | Generate template cover letter (Problem-Solution format) |
/api/v1/applications/ |
GET | List all applications |
/api/v1/apply-queue/ |
GET | Jobs ready to apply (have email, not yet applied) |
/api/v1/apply-queue/batch/ |
POST | Batch apply to selected jobs |
/api/v1/apply-queue/progress/ |
GET | Batch apply progress |
/api/v1/stats/overview/ |
GET | Dashboard overview stats |
/api/v1/stats/skills/ |
GET | Skill frequency across jobs |
/api/v1/stats/companies/ |
GET | Company job counts |
/api/v1/stats/locations/ |
GET | Location distribution |
/api/v1/profile/ |
GET/PUT | User profile |
/api/v1/profile/resume/ |
GET/PUT | Resume upload |
/api/v1/profile/security/ |
GET/PUT | Email/password settings |
/api/v1/profile/ai/ |
GET/PUT | AI/LLM configuration |
/api/v1/web-apply/ |
GET | Jobs with apply links (no email found) |
/api/v1/missing-emails/ |
GET | Jobs missing company emails |
/api/v1/fetcher/run/ |
POST | Trigger a fetch cycle |
/api/v1/fetcher/status/ |
GET | Current fetcher status |
WebSocket:
| Endpoint | Description |
|---|---|
ws/fetcher/progress/ |
Real-time fetcher progress updates |
JobbLoot/
├── apps/ # Django applications
│ ├── core/ # Shared infrastructure (pagination)
│ ├── dashboard/ # Legacy template views (still functional)
│ └── jobs/ # Main app — models, API, business logic
│ ├── models/ # Job, Application, SkillLog, DailyStats, RawJob, JobEvent, CredStore, AIConfig
│ ├── views/ # 15 view modules (DRF API views)
│ ├── urls/ # API URL patterns
│ ├── serializers/ # DRF serializers
│ ├── fetchers/ # Technopark, Cutshort scrapers (parallel fetching)
│ ├── cv_engine/ # Cover letter template engine
│ │ └── cover_templates.py # 6 market-validated cover letter templates
│ ├── management/commands/ # run_all, run_fetcher, run_scheduler
│ ├── matcher.py # Weighted scoring engine
│ ├── applicant.py # Cover letter gen + Gmail SMTP + auto-apply
│ ├── llm_client.py # OpenAI-compatible LLM client
│ ├── services.py # CRUD, parallel enrichment, salary extraction
│ └── consumers.py # WebSocket consumer
├── config/ # Django project settings
│ ├── settings/ # base.py, dev.py, prod.py, test.py
│ ├── queries.py # Search queries per source
│ ├── constants.py # Role rejection keywords, filters
│ ├── profile.example.py # Template profile
│ ├── urls.py # Root URL configuration
│ ├── asgi.py # ASGI application
│ └── wsgi.py # WSGI application
├── common/ # Shared utilities
│ └── utils.py # Email detection, UID generation, HTML cleaning
├── frontend/ # React SPA
│ ├── src/
│ │ ├── pages/ # 11 page components (Overview, Jobs, JobDetail, etc.)
│ │ ├── components/ # Reusable UI components
│ │ ├── lib/ # API client, utilities
│ │ ├── types/ # TypeScript type definitions
│ │ ├── App.tsx # Router + layout
│ │ └── style.css # Flat Design 2.0 theme
│ ├── public/ # Static assets (favicon, icons)
│ ├── package.json # Frontend dependencies
│ ├── vite.config.ts # Vite config (proxy to Django)
│ └── tsconfig.json # TypeScript config
├── tests/ # Test suite
├── static/ # Built frontend output (from Vite)
├── media/ # User uploads (resumes, etc.)
│ └── resumes/ # Uploaded resume PDFs (auto-created)
├── .env.example # Environment template
├── pyproject.toml # Python project config
├── manage.py # Django management
├── CONTRIBUTING.md # Contribution guidelines
├── LICENSE # MIT License
└── README.md # This file
| Page | Route | Description |
|---|---|---|
| Overview | / |
Stats cards, jobs-over-time chart, top skills chart |
| Jobs | /jobs |
All matched jobs with search, filters, pagination |
| Job Detail | /jobs/:id |
Full breakdown: match score, skill gaps, auto-apply, generate CV, generate template CL |
| Applications | /applications |
Sent applications with status tracking |
| Apply Queue | /apply-queue |
Jobs ready to apply (with email), batch apply |
| Web Apply | /web-apply |
Jobs with apply links (no email found) |
| Missing Emails | /missing-emails |
Jobs needing manual application |
| Skill Stats | /stats/skills |
Skill frequency across all jobs |
| Company Stats | /stats/companies |
Company job counts |
| Location Stats | /stats/locations |
Job distribution by location |
| Profile | /profile |
Edit profile, resume, security, AI settings |
Edit config/settings/base.py:
MATCH_THRESHOLD_TRACK = 50 # Minimum % to track a job
MATCH_THRESHOLD_APPLY = 65 # Minimum % to include in apply queue
MIN_SALARY = 18000 # Minimum salary filter
MAX_SKILL_GAP_PCT = 40 # Skip jobs needing >40% unknown skills
MAX_SALARY_GAP_PCT = 50 # Skip if salary gap exceeds 50%Edit config/queries.py:
SEARCH_QUERIES = [
"python developer",
"django developer",
"python full stack developer",
# add more...
]Edit config/constants.py:
REJECT_ROLE_KEYWORDS = [
"data engineer", "devops", "java", ".net",
# add more...
].env,config/profile.py,profile.json,media/,db.sqlite3— all gitignored- Credentials stored with Fernet encryption (cryptography library)
- Django CSRF, X-Frame-Options, Content-Type nosniff, HttpOnly cookies enabled
- No raw SQL, no
eval/exec— Django ORM throughout - Production settings: HSTS, SSL redirect, secure cookies, proxy headers
Contributions are welcome! Please see CONTRIBUTING.md for guidelines.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Thanks to everyone who has contributed to JobbLoot!
Dennis Joseph Creator & Author |
Mohammed Swalih N K Contributor |
This project is provided as-is for educational and personal use purposes.
- No warranty. This software is provided without warranty of any kind, express or implied. The authors and contributors are not responsible for any damages, data loss, or legal consequences arising from the use of this software.
- User responsibility. You are solely responsible for how you use this tool. By using JobbLoot, you acknowledge that:
- Automated job applications may violate the Terms of Service of certain job platforms. Use at your own risk and always respect platform-specific rules.
- Sending automated emails via Gmail is subject to Google's automation policies. Excessive sending may result in account suspension.
- You must comply with all applicable laws and regulations, including data protection laws (GDPR, CCPA, etc.) when handling personal or third-party data.
- AI-generated content. Cover letters generated by LLMs may contain inaccuracies. Always review and edit before sending. The hallucination validation layer reduces but does not eliminate this risk.
- No affiliation. This project is not affiliated with, endorsed by, or connected to any job platform (Technopark, Cutshort, etc.), email provider (Google/Gmail), or AI service (OpenAI, Groq, etc.) referenced in this documentation.
- Educational purpose. This project demonstrates a full-stack architecture combining Django REST Framework, React, WebSockets, and LLM integration. It is intended as a learning resource and personal productivity tool, not a commercial service.
MIT License — Copyright (c) 2026 Dennis Joseph, Mohammed Swalih N K
See LICENSE for full terms. If you use or distribute this software, you must mention the original author.
Built with Django + React + AI
Star this repo if you find it useful!