Job searching is tab hell. You open six boards, re-read the same listings, and pay $30/month for tools that are just feed aggregators wearing an "AI-powered" sticker.
So I built JobRadar — a CLI tool that searches 8 job sources concurrently and scores every listing against your profile using a local LLM that runs on your own CPU. No API keys, no subscriptions, no resume leaving your machine.
The launch post covered the what. This one covers the how.
The whole thing is one linear pipeline:
query + profile.yaml
│
▼
8 source adapters (concurrent) ──► normalized Job objects
│
▼
local LLM scores each job 0-100 (skills, experience, salary, remote fit)
│
▼
seen-jobs cache (SQLite, 7-day) ──► ranked table + results.csv
In code, the search phase looks like this:
from concurrent.futures import ThreadPoolExecutor, as_completed
def search_all(sources, query, limit, max_pages):
jobs = []
with ThreadPoolExecutor(max_workers=len(sources)) as pool:
futures = {pool.submit(s.search, query, limit, max_pages): s
for s in sources}
for future in as_completed(futures):
jobs.extend(future.result())
return jobs
Eight adapters, one interface, all running in parallel. A query that used to take me 20 minutes of tab-hopping finishes in under a minute.
Five of the sources are classic job board APIs:
The interesting two are Greenhouse and Ashby. These are applicant tracking systems that a huge chunk of tech companies use — and they have public career-page APIs. No auth, no scraping. You just hit their job board endpoint with a company slug:
class GreenhouseSearch(Source):
def search(self, query, limit, max_pages):
jobs = []
for company in self.companies: # e.g. gitlab, figma, stripe
url = f"https://boards-api.greenhouse.io/v1/boards/{company}/jobs"
resp = requests.get(url, timeout=10)
jobs.extend(normalize_greenhouse(resp.json()))
return jobs
That means JobRadar pulls roles straight from company career pages — GitLab, Figma, Stripe, OpenAI, Anthropic, Linear — without an account or an API key on any of them. The company list is a YAML file you can edit:
greenhouse:
- gitlab
- figma
- discord
- shopify
- stripe
LinkedIn scraping exists but is off by default — it depends on undocumented HTML that breaks constantly, and it might violate their ToS. I'd rather ship without it and be honest about the tradeoff than bundle a ToS risk into the default path.
This is the part people ask about the most. Every other tool I found uses cloud APIs (Claude, OpenAI) for scoring — your resume and search history go to someone else's server, and you pay per token. JobRadar runs qwen3-1.7b (a 1.1 GB GGUF) on your machine via Ollama or llama.cpp.
The rater auto-detects whatever local LLM server is running by scanning the standard ports:
_DEFAULT_PORTS = [
("http://localhost:11434", "Ollama"), # Ollama default
("http://localhost:8080", "llama.cpp"), # llama.cpp default
("http://localhost:1234", "LM Studio"), # LM Studio default
]
If you have a bigger model installed, it picks that up too — you can override with --llm-model qwen3:8b
and scoring gets smarter at the cost of speed.
Each job gets scored 0-100 across four dimensions — skills match, experience fit, salary fit, remote fit — with the model's reasoning captured so you can see why a job scored the way it did. Rating calls run concurrently (3 by default, --max-concurrency
to tune) with retry logic for the occasional malformed JSON response.
Honest take: a 1.7B model is a good filter, not an oracle. It catches "this says Python but is actually a sales role" reliably. It does not have your gut feel about company culture, and it shouldn't — that's the point. The score is a starting point, not a verdict.
Job boards republish the same listings constantly. JobRadar keeps a SQLite database of seen jobs (~/.jobradar/seen_jobs.db
) with a 7-day window, so every run only surfaces what's new. --cache-days 30
to stretch it, --no-cache
to see everything again.
The CLI is the core, but there's a companion web dashboard — a FastAPI backend, SQLite storage, and a dark-mode SPA with a Kanban pipeline. Jobs flow from Discovered to Reviewing to Applied to Interviewing, cards are color-coded by match score, and there's a live terminal-style activity log showing the LLM scoring progress as it happens. It reads like a terminal because the whole product is terminal-first.
Job
dataclass at the boundary and every downstream step gets simpler.
curl -fsSL https://raw.githubusercontent.com/ANIRudH-lab-life/job-radar/main/setup.sh | bash
Or browse the source: https://github.com/ANIRudH-lab-life/job-radar
Landing page: https://anirudh-lab-life.github.io/job-radar/
MIT licensed. Built with Python, Rich, FastAPI, SQLite, and a whole lot of tabs closed.
Also published: the launch post