Personalised job search pipeline: scrape, rank, and rate jobs against your resume
Project description
Jobs Radar
Personalised job search pipeline: scrape → tag → embed → rank → rate.
Features
- Resume parsing — paste raw resume text, get structured output (target roles, skills, seniority, experience) via OpenAI structured outputs
- Parallel scraping — scrapes Indeed across multiple queries and locations concurrently using
ThreadPoolExecutor - Dedup + tagging — removes duplicate listings, tags each job for seniority, entry-level, remote, clearance, and experience range using regex
- Hybrid vector search — dense (BGE) + sparse (BM25) search with Reciprocal Rank Fusion via Qdrant in-memory
- LLM-as-judge rating — each result is scored 1–10 against the candidate's resume using
gpt-4o-mini - Feedback loop — rate jobs 1–5 stars; liked/disliked examples are injected into the judge prompt on future searches to personalise ratings over time
- Single-table persistence — search results and user feedback live in one SQLite table (
job_results), keyed byjob_url; re-submitting feedback updates the row, no duplicates - CSV export — every scrape is auto-saved to
~/.jobs-radar/jobs_{timestamp}.csv, downloadable from the UI - Streamlit UI — parse resume, search jobs with inline feedback, browse and rate recent results
- REST API — FastAPI backend, useful for scripting or other clients
Stack
- Python 3.12, FastAPI, Streamlit
- OpenAI API (
gpt-4o-miniby default) - Qdrant (in-memory) + fastembed
- jobspy (Indeed scraping)
- SQLite (results + feedback storage)
- uv (dependency management)
Installation
pip install jobs-radar
# or with uv
uv add jobs-radar
Or run from source:
git clone https://github.com/your-username/jobs-radar
cd jobs-radar
uv sync
Setup
cp .env.example .env # fill in OPENAI_API_KEY
Data (SQLite DB + CSVs) is stored in ~/.jobs-radar/ by default. Override with DATA_DIR in your .env.
Running
You need two terminals — one for the API server and one for the UI.
From a pip/uv install:
# terminal 1 — start the API server
jobs-radar-serve
# terminal 2 — open the UI
jobs-radar-ui
From source:
# terminal 1 — API
uvicorn jobs_radar.main:app --reload
# terminal 2 — UI
streamlit run jobs_radar/ui.py
Then open http://localhost:8501 in your browser. API docs at http://localhost:8000/docs.
Configuration
All settings are read from environment variables or a .env file:
| Variable | Default | Description |
|---|---|---|
OPENAI_API_KEY |
— | Required |
DATA_DIR |
~/.jobs-radar |
Where SQLite DB and CSVs are stored |
API_HOST |
0.0.0.0 |
FastAPI bind host |
API_PORT |
8000 |
FastAPI port |
API_BASE |
http://localhost:8000 |
URL the Streamlit UI uses to reach the API |
API Endpoints
| Method | Path | Description |
|---|---|---|
| POST | /parse |
Parse resume text → structured profile |
| POST | /search |
Scrape, embed, rank, and rate jobs |
| GET | /results/recent |
Load the most recent search results |
| POST | /feedback |
Save user rating for a job |
Project Structure
jobs_radar/
main.py # FastAPI routes
pipeline.py # scrape, dedup, tag, validate
vector_store.py # Qdrant setup, upsert, hybrid search
llm.py # resume parsing, job rating, judge prompt
feedback.py # SQLite — job_results table, save/load functions
models.py # Pydantic models
config.py # pydantic-settings — all env/config vars
cli.py # jobs-radar-serve and jobs-radar-ui entrypoints
ui.py # Streamlit UI
tests/ # pytest suite (54 tests)
Data Model
All results and feedback share one SQLite table (~/.jobs-radar/feedback.db):
| Column | Source |
|---|---|
job_url (PK) |
scraper |
title, company, location, ... |
scraper |
llm_rating, relevance_score, llm_reasoning |
system |
user_rating, notes |
user feedback |
search_id, saved_at |
system |
Re-running a search updates system fields for any URL already in the DB while preserving user_rating and notes.
Notes
- Qdrant runs in-memory — the vector index is not persisted between server restarts. The SQLite DB and CSV exports are the durable layer.
- fastembed downloads model weights on the first
upsert_jobscall (~30–60s). Subsequent calls are fast.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file jobs_radar-0.1.1.tar.gz.
File metadata
- Download URL: jobs_radar-0.1.1.tar.gz
- Upload date:
- Size: 133.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e04c657c9cf965996437dcaf7d74c278416a85f00fa6e003ca864c8712656ca2
|
|
| MD5 |
8d3b9c13396647f6cf7e7b8ac8a1c49c
|
|
| BLAKE2b-256 |
2334c5a7993ee411fcd2eef9c7256db198b4c409b1c95279de2aa78188eea850
|
File details
Details for the file jobs_radar-0.1.1-py3-none-any.whl.
File metadata
- Download URL: jobs_radar-0.1.1-py3-none-any.whl
- Upload date:
- Size: 15.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1cbe7e152815cd962eb9bbb985f61886077094555ee046ecd077aade254b4fa2
|
|
| MD5 |
87f744dc4df434525a201f836414c72a
|
|
| BLAKE2b-256 |
800782479dbcf3e6ccec0d5492accd67dc1ff230d3ff0f512a01d500008688cd
|