Case study · Job search automation
napotnica: a tool that reads student job ads for me
I built napotnica because looking for student work in Slovenia was the same loop every time. I'd open studentski-servis.com, scroll through a few hundred ads, skip the warehouse night shifts, and rewrite my CV for every job I liked. Now a script collects the ads, an LLM ranks them against my profile, and I get a CV and cover note for the ones worth applying to.

The problem
The site lists hundreds of jobs, and most have nothing to do with what I study. Picking out the handful that fit meant reading all of them, and every application after that needed its own version of my CV.
Pay made comparing harder. Some ads quote the hourly rate net and some gross, so two ads with the same number can pay different amounts.
What I built
napotnica runs on my own computer and keeps everything in one SQLite file. It has four parts.
The scraper uses Playwright to open the public listing pages, and a small parser pulls out the title, company, location, pay and description. It doesn't log in, and it waits 1.5 to 3 seconds between pages.
Scoring takes two LLM calls per ad. The first pulls out the facts: the category, the skills it asks for, the language, and whether the work is remote. The second compares those facts with my profile and returns a score from 0 to 100, plus a list of what fits and a list of what's missing. I made the model explain itself because a bare number gave me nothing to act on.
The CV generator takes a job I like, picks the relevant parts of my profile, rewords them for that ad, and prints one A4 page to PDF with a cover note. If the page overflows, it drops the least relevant project and measures again. It never shrinks the text.
A Flask app puts it all on one page: the list sorted by score, with filters for location, category, minimum score and minimum pay. Net and gross pay sit next to each other, and a job I've applied to drops out of the list.
A napotnica is the paper referral slip for student work, so I styled the app to look like a printed document.

The CV can't make things up
This is the part I care about most. An LLM asked to write a CV will add a company you never worked at, and you might not notice until an interviewer asks about it.
So the model gets three jobs. It chooses which entries from my profile matter for this ad, puts them in order, and rewords the bullet points. It can't add a company, project, school or skill that isn't in my profile file, and the code checks every reply against that file. If the model invents something, it gets one more try with the invented item named. If it does it again, napotnica stops and writes no document.
Job titles, employer names, years, schools and contact details never go through the model. They're copied straight from the profile, so they can't change by accident.
What went wrong
Scoring shipped with a bug. I had required every score to come with at least one thing that fits. For a job where nothing fit, the model sent back an empty list, my check rejected the reply, and the job was left without a score. Nothing was logged, so a failed job gave no reason. The fix was one line, plus a warning in the log for every job that fails.
The scores are still too generous. Most land high and close together, and one ad in my screenshots scores 92 while its own gaps list says the candidate is missing a key requirement. I use the score to sort the list and make the decision myself.
What I have now
A scrape of ten pages brings in 500 listings. I read a short list sorted by fit and skip the rest, and a CV with a cover note for one job is a button press.
Building it took about ten days, from the first commit on 12 July 2026 to the last feature on 22 July. It has 66 tests. All but one run offline against saved copies of the site's pages, so the suite finishes in a few seconds on every push.
What it doesn't do
- It reads one site. The parser depends on how studentski-servis.com is built today, so a redesign there breaks it until I update the parser.
- The score is one model's opinion. I haven't measured it against the jobs I'd pick by hand.
- It's built for one person on one computer, so there's no login.
- CVs come out in Slovenian or English.
- Scoring costs API calls, so each run stops at 40 jobs.
I build the same kind of tool for businesses: a scraper or script that does the weekly chore and puts the result where you already look. If you have one of those, write to me at luka@puhmeister.dev.