Case study · 2026
Job Search Panel AI-assisted job hunting on hh.ru
₽0
spent on the AI model’s API: Claude Code scores the vacancies and writes the letters on the user’s subscription
5
sources in one panel: hh.ru, Telegram channels, company sites, tenders and studios
- Problem
- Job hunting on hh.ru, Russia’s largest job board, takes hours: a few searches return hundreds of vacancies, every letter is written from scratch, and applications and employers’ replies have to be tracked by hand.
- Solution
- Built a panel: it finds vacancies through the official hh.ru API and filters them by rules, while Claude Code scores each one and writes a letter from the facts in the CV.
- Result
- Vacancies arrive with a score and a letter, every application is on one board, and invitations and rejections come to Telegram. A letter that lacks facts won’t go out until they are filled in.
Every vacancy and application is on one board
The Funnel board shows where each vacancy stands: new, awaiting confirmation, sent, viewed, answered. Any column opens into a working list with the score and the letter, and the list can be worked through from the keyboard: open the application, skip, put off. Tenders and studios have boards of their own, so it is always clear where each application is and what to do with it next.
How it was donecollapse
Saved hh.ru searches are checked on a schedule. New vacancies go through the hard rules of the questionnaire — stop words, salary, region, work format — and get a preliminary score without any AI, from the weights of the skills.
Above the board is a 30-day funnel: how many vacancies were found, scored, sent and viewed, and how many invitations came. The working list moves with J/K, Enter opens the application on hh.ru, S skips a vacancy and D puts it off.
— open full size
Claude scores each vacancy and writes the letter from the facts in the CV
Every suitable vacancy gets a score from 0 to 100 with an explanation: what matches the CV and what is missing. Claude writes the letter from the facts of the profile, and where there are no facts it leaves a “clarify” mark — such a letter won’t go out until it is finished. The same way Claude drafts replies to employers, interview prep sheets, answers to application form questions and edits to the CV. All of it runs in Claude Code on a subscription, with no API bill.
How it was donecollapse
The panel packs suitable vacancies into batches of 12 in an exchange folder. Claude works through them in a Claude Code session with the /hh-batch command: it reads the profile and the rules, writes the result against a JSON schema, and the panel imports it by itself. The product makes no AI API calls and holds no API keys.
The review can also run without a person: the panel starts claude -p itself when batches appear and reports to Telegram how many were reviewed and how many fit. The background run has narrow rights: it writes only to the result folders, the only command it may run is the style checker, and it can’t read .env, the database or the backups.
To Claude the vacancy texts are data, not instructions: letters with fragments that look like secrets are rejected on import. Claude writes Russian letters by the humanizer-ru skill (MIT, by Ilya Utov), and the panel checks imported and edited letters with the same scanner.
— open full size
A person sends the application, and employers’ replies don’t get lost
In Assistant mode the panel opens the application page on hh.ru with the letter already in the clipboard, and a person confirms the send. Where hh.ru allows it, the application goes through the API, with a queue, pauses and limits. Employers’ replies come from hh.ru, from email or from a manual log, and invitations, rejections and new vacancies with action buttons arrive in Telegram.
How it was donecollapse
On 15 December 2025 hh.ru restricted its API methods for job seekers, and what a given app may use is not known in advance. So the panel runs a probe and picks a mode from its results: if applying through the API is closed, Assistant mode takes over; if the list of applications is closed, a manual log and hh.ru emails read over IMAP.
Every request to hh.ru goes through one client: OAuth with PKCE, request rate, retries, errors, token refresh and captcha. Methods missing from the public specification are called only after the probe confirms them. The panel keeps to hh.ru’s limits and its own stricter ones — per day, per hour and a sending window — doesn’t emulate a browser or get around captcha, and asks for consent before the first application: applications go out in the user’s name.
In Telegram, events of one kind within five minutes are merged into one message. A vacancy with a letter comes with the buttons “Yes, sent”, “Skip” and “Put off”, and the bot takes commands only from the owner’s chat.
— open full size
Analytics shows where the odds are better
Analytics splits the market into segments by search, job title and skill: salaries, competition, trends and who answers applications. It suggests which searches to add, which words to put on the stop list and which skills to bring forward in the CV, and Claude drafts the CV edits for a segment.
How it was donecollapse
A segment is any slice of the base: a saved search, a criteria set, a target title, a work format, experience or a tag. Tags — industry, tasks and tools — are set by the panel from the title, skills and description of a vacancy, Claude adds its own when scoring, and labels that mean the same thing count as one.
“Hot” vacancies are fresh, well scored and have few applicants, so they are worth opening first. “Borderline” ones fell a few points short of a threshold and are worth a look by eye.
The summary counts conversions from a found vacancy to an invitation, the median employer response time and the daily trend, and any data exports to CSV. The reports are computed in a separate thread, so the rest of the panel doesn’t freeze meanwhile.
— open full size
Beyond hh.ru: channels, company sites, tenders and studios
The panel reads public Telegram job channels, company career sites and workspace.ru tenders for digital services, and finds development and design studios that could use the experience. Claude writes the tender responses and the letters to studios, in a formal or a plain style, and a person sends them.
How it was donecollapse
Channels are read through the public web version at t.me/s, without signing in to Telegram. The panel reads company and studio sites without a browser, respects robots.txt and doesn’t get around bot protection.
Tenders are checked once an hour and compared with the questionnaire. Studios found through hh.ru are offered only an unsolicited application: the hh.ru agreement allows its data to be used for job hunting only.
— open full size
— open full size
Runs on its own server and opens on a phone
The panel runs on a computer or permanently on its own Linux server. Updating and rolling back take one command, and if the panel doesn’t answer within four minutes after an update, the previous version comes back. The database is copied every night, and a fresh copy can be pulled off the server every day. The panel opens in any browser through a one-time link from the bot, and the interface works on a phone.
How it was donecollapse
On the server the panel is a sandboxed systemd service with memory and CPU limits. A health check every two minutes restarts the service if it fails to answer three times in a row and reports it to the bot. Installation can be left to Claude Code: the /setup command installs the dependencies, builds the interface, starts the panel and walks the user through the steps only they can do — hh.ru keys, sign-in, CV, bot.
An update waits for the panel’s jobs to finish, copies the database, switches the code and checks /api/health. Only a commit merged into master with green CI on Windows and Ubuntu goes out: 744 automated tests (625 for the backend, with mocks of hh.ru and Telegram, and 119 for the interface), ruff and strict mypy. Restoring from a backup is tested: a script brings the copy up as a second panel without hh.ru sign-in or the bot and compares the data.
The browser sign-in link lasts five minutes, and the bot reports the time, browser and IP of every sign-in; without one, the public address serves only the sign-in page. Only Caddy faces the internet, and it is locked down: binding the port is the only privilege it keeps, the file system is read-only, and system calls are filtered.
The interface is built on its own design system: OKLCH colours, six text sizes, every component in every state on the /ui page. The page never scrolls sideways, tables scroll inside their own block, and every page is checked at 1280–1536 px and on a phone.
— open full size
How it was built
I worked with Claude Code: I kept the architecture and sign-off, the agent wrote the code to a written brief, and every stage closed with tests, linters and a check of the pages in a browser. That is how one person built the whole product — from the skeleton and the hh.ru API work to the analytics, the server and the new interface. The code is open under the MIT licence: github.com/
Got a similar problem? Describe it and I’ll suggest where to start.
Message me on Telegram