jobhunter
v0.5.0
Published
AI job hunter for your terminal: scores remote jobs against your CV and tracks them in a spreadsheet (a local .xlsx file or Google Sheets)
Maintainers
Readme
jobhunter
A command-line job hunter. It reads your CV, checks remote job boards and the careers pages of companies hiring for your roles, lets an AI score every new job against your profile, and keeps everything in a tracking spreadsheet, so you don't see the same job again on the next run. The spreadsheet is a file on your computer, or a Google Sheet in your Drive if you prefer.
$ jobhunter hunt
✔ Remotive: 18 jobs
✔ Remote OK: 99 jobs
✔ We Work Remotely: 89 jobs
✔ Company job boards: 41 jobs for your roles from 12 boards
✔ Checked your sheet
✔ Scored 35 jobs
Best matches:
92 Senior Full-Stack Engineer · Acme (Remotive)
Strong match: 6 years of React and Node.js, fully remote, salary above your minimum.
88 Frontend Engineer · Initech (Greenhouse)
Your React and design-system work fits; remote within Europe, matching your timezone.
85 Product Engineer · Globex (We Work Remotely)
Good fit for your TypeScript and Rails experience; EU timezones only, which suits you.
…
✔ 247 jobs · 0 already in your sheet · 247 new → 212 ignored by your filters · 12 To apply · 23 Skipped- Your CV is the source of truth. Jobhunter works out your roles, seniority and stack from it and turns them into a short candidate profile the AI uses when scoring jobs.
- Jobs straight from the companies. Many companies post jobs only on their own careers page, hosted by Greenhouse, Lever, Ashby or Workable.
jobhunter companies findsearches the web for companies hiring for your roles there, and everyhuntreads their boards too. - Cheap filters first, the AI second. Code drops jobs that clearly don't fit (wrong role, too old, wrong employment type or work mode, salary below your minimum, companies you avoid). Only the rest goes to the AI, which gives each a score from 0 to 100 and a short note.
- Everything lands in a spreadsheet with status tabs, colours and dropdowns, ready for you to track your applications. By default it's an
.xlsxfile on your computer that you open in LibreOffice or Excel. No Google account needed. - Your data stays yours. Your keys, profile and sheet live on your machine. If you choose a Google Sheet instead, Jobhunter can only see the files it created in your Drive.
🚧 Early version (0.5).
init,huntandcompanies findwork and are ready for daily use, but Jobhunter is still being actively developed: more commands are on the way, and details like the sheet layout may change between versions. Bug reports and ideas are welcome.
Jobhunter works with Claude (Anthropic), Gemini (Google), and any OpenAI-compatible API (OpenAI, OpenRouter, Groq, a local Ollama or LM Studio, …).
Contents
- Requirements
- Install
- Quick start
- Setting up Google access
- Commands
- Your tracking sheet
- Common tasks
- Where your data lives
- Privacy
- Troubleshooting
- Development
- License
Requirements
Node.js 22.12 or newer. Check with
node --version. Get it from nodejs.org.An API key for an AI provider, one of:
- Anthropic (Claude), from the Anthropic Console.
- Google (Gemini), from Google AI Studio.
- Any OpenAI-compatible API, such as OpenAI, OpenRouter or a local Ollama. You enter its base URL (for example
https://openrouter.ai/api/v1orhttp://localhost:11434/v1) and its API key; for a local server that doesn't check keys, type anything.
💡 Try Jobhunter for free with Gemini. As of October 2026, the Gemini API has a free tier for some of its models, with rate limits. Google may change this, so check the pricing page. On the free tier, Google may use what you send (including your CV) to improve its products. The paid tier doesn't.
Paid usage is billed by the provider to your account. The cost depends on the model you pick and how many jobs are scored; each run prints the AI tokens it used.
A spreadsheet app to open your tracking sheet. We recommend LibreOffice (free and open source). Excel works too. See Using a local file.
Optional: a Google account, only if you'd rather keep the sheet in Google Sheets. That also needs a free Google Cloud project to sign in with (step-by-step guide below).
Optional: a web search API key, to find companies' own job boards quickly with
jobhunter companies find. Either works within its free tier:- SerpApi (250 free searches a month), or
- Brave Search API ($5 of free credit a month; asks for a credit card).
Without one, Jobhunter can use your AI's own web search (Claude, or Gemini with billing turned on) or DuckDuckGo, which is free but slow. See Choosing a search.
Your CV as PDF, DOCX, Markdown or plain text.
Install
npm install -g jobhunterThen run jobhunter --help to check it's installed.
Quick start
- Run the setup:
It walks you through the AI key, your CV, your preferences, creating your tracking sheet and choosing a web search. For the sheet, press Enter to keep it injobhunter init~/Documents/jobhunter.xlsx, or type another file or folder. When it says "You're all set. 🎉 Happy hunting! 🏹", you're done. - Go hunting:
Close the sheet first if you have it open. New jobs are added to it, and the run ends with where it is. Open it and go to the To apply tab to see the best matches.jobhunter huntjobhunter sheet --openopens it any time. - Run
jobhunter huntagain whenever you like, for example once a day. Jobs already in your sheet are skipped, so each run only adds new ones. - Find companies hiring for your roles with
jobhunter companies find, about once a week. It looks for companies' own job boards, and everyhuntafter it reads them too.
Prefer Google Sheets? First create your Google OAuth client by following Setting up Google access (about 10 minutes, once), then choose In a Google Sheet when init asks where to track jobs.
Setting up Google access
Only needed if you want your tracking sheet in Google Sheets. The default local file needs none of this.
With a Google Sheet, Jobhunter writes to a spreadsheet in your Drive. To do that, Google needs to know which app is asking. For now, every user creates their own small, free OAuth client in Google Cloud and gives the downloaded file to Jobhunter. Nothing gets deployed, and there's no billing.
Coming in a future version: Jobhunter will ship with its own Google sign-in, so you won't need to create a Google Cloud project at all. Using your own OAuth client will stay available as an option for anyone who prefers it.
1. Create a Google Cloud project
- Open the Google Cloud Console and sign in with the Google account that should own the tracking sheet.
- Click the project picker at the top → New project. Name it anything, for example
jobhunter, then Create. - Make sure the new project is selected in the project picker.
Google's guide: Create a Google Cloud project.
2. Enable the Sheets and Drive APIs
With your project selected, open each link and click Enable:
Google's guide: Enable Google Workspace APIs.
3. Configure the consent screen
- Open Google Auth Platform (under APIs & Services → OAuth consent screen) and click Get started.
- App information: any app name (for example
jobhunter), and your email as the support email. - Audience: choose External.
- Contact information: your email. Accept the policy and click Create.
- Go to Audience → Test users → Add users and add your own Google address. While the app is in "Testing" mode, only test users can sign in.
Google's guide: Configure the OAuth consent screen.
Sign-ins expire after 7 days in "Testing" mode. Google does this for every app in Testing, so after a week
huntwill ask you to sign in again. To avoid it, open Audience and click Publish app. Jobhunter only asks for access to the files it creates itself (the non-sensitivedrive.filescope), so publishing doesn't need a Google review for your own use.
4. Create the OAuth client and download it
- Go to Clients → Create client.
- Application type: Desktop app. Name it anything, then click Create.
- Click Download JSON and save the file (named like
client_secret_….json) somewhere you can find it.
Google's guide: Create OAuth client ID credentials for a desktop app.
5. Give the file to Jobhunter
Run jobhunter init and choose In a Google Sheet when it asks where to track jobs. Then paste the path of the downloaded file. Jobhunter copies it into its own config folder, so you can delete the download afterwards. Your browser then opens Google's sign-in page. Choose your account and allow access.
You may see "Google hasn't verified this app". That's expected: it's your own app. Click Continue.
What Jobhunter can access
Jobhunter asks for three permissions:
openidandemail: shows which account you're connected as.drive.file: lets it see and edit only the files Jobhunter created, meaning theJobhunterfolder and the tracking sheet inside it. It can't see anything else in your Drive.
You can remove access at any time in your Google Account permissions.
Commands
jobhunter init
Sets everything up. Run it once to start. Run it again any time to change one part of your setup.
On first run it goes through every step:
AI provider, key and model. Choose Anthropic (Claude), Google (Gemini), or an OpenAI-compatible API (then also enter its base URL; for a server on your own machine or network, such as Ollama, you also choose whether the model may think before answering, which is off by default because it's often many times slower). Paste your API key and pick a model from the list your key can use; for an OpenAI-compatible API you can also type a model ID it doesn't list. Jobhunter checks that the key and model actually work before going on.
CV. Give the path to your CV (PDF, DOCX, MD or TXT). PDF and Word files are converted to Markdown by the AI. You see a short summary (name, roles) and can accept it, edit it in your editor, or try again.
Preferences. A few questions the CV can't answer:
- work mode (remote, hybrid, onsite)
- countries you can legally work from, and your timezone
- cities and relocation, for hybrid or onsite work
- whether you need visa sponsorship
- employment type
- minimum salary (optional)
- industries and companies to avoid (optional)
Candidate profile. The AI writes a short profile of you from your CV and preferences. This is what it reads when scoring jobs. You can accept it, edit it in your editor, or have it written again.
Tracking sheet. Choose where Jobhunter tracks jobs:
- In a spreadsheet file on this computer (the default). Give a file or a folder, or press Enter for
~/Documents/jobhunter.xlsx. Missing folders are created after you confirm. If the file is already a Jobhunter sheet, it's reused with all its jobs. Any other existing file is never overwritten. - In a Google Sheet. Give the path to your OAuth client file (how to get one) and sign in with Google in your browser. Jobhunter then creates the sheet in a
Jobhunterfolder in your Drive and prints its link.
- In a spreadsheet file on this computer (the default). Give a file or a folder, or press Enter for
Web search. Choose which web search Jobhunter uses. Today that's how
jobhunter companies findfinds companies' own job boards:- Your AI's own web search (Claude's web search, or Gemini with Google Search), offered first when your AI provider has one. No extra key, but the slowest and most expensive per search, billed to your AI account. OpenAI-compatible APIs can't search, so this isn't offered for them. On Gemini's free tier, Gemini 3 models can't use Google Search either; turn on billing (5,000 free searches a month) or pick another search.
- SerpApi (Google results, 250 free searches a month) or the Brave Search API ($5 of free credit a month). Fast and the best results; paste the service's API key.
- DuckDuckGo. Free and needs no key, but slow: DuckDuckGo has no API, so Jobhunter reads its result pages like a browser and waits 45 seconds between searches. Searching everything takes about 10 minutes, and DuckDuckGo still blocks searches now and then.
See Choosing a search to compare them. Except for DuckDuckGo, Jobhunter checks your choice with one real search for your first role, and keeps the job boards it finds.
When you're already set up, init asks what you'd like to change:
| Choice | Use it when you want to… | | ------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | | Everything | start over from step 1 | | AI provider, key or model | switch to another model, or replace your API key (press Enter to keep the saved one) | | CV | import an updated CV; your preferences and candidate profile are updated after it | | Preferences and candidate profile | change what you're looking for, or rewrite the profile the AI scores you by | | Tracking sheet (local file or Google Sheet) | use another file, start a fresh sheet, switch between a local file and Google Sheets, or sign in to Google again | | Web search | change which web search is used, or replace its API key | | Nothing, exit | leave everything as it is |
Any step can be skipped and finished later by running jobhunter init again. Pressing Ctrl+C cancels without saving the step you're in.
jobhunter hunt
Finds new jobs and adds them to your sheet. Each run goes like this:
Fetch the job boards: Remotive, Remote OK and We Work Remotely. A board checked in the last 6 hours is skipped, to go easy on it. If one board is down, the others still run.
Company job boards. It also reads the companies' own job boards that
jobhunter companies findfound, through each board's public API (every 6 hours at most). A board lists every job the company has, so the title check is stricter here, and jobs that fail it aren't added to Ignored. A title is kept when it matches your roles or main technologies, or is a general software engineer or developer title ("Senior Software Engineer, Payments", "Product Engineer"). A title naming another speciality is dropped ("Android Developer", "Data Engineer", "DevOps Engineer", "QA Engineer", "Java Developer"), unless your CV names it. So is one two or more levels away from yours: for a senior CV, "Intern" and "Junior" titles. Lead, staff and principal titles count as one level, since their meaning differs between companies.huntitself never searches the web: when no boards are known yet (so company boards are skipped), or the weekly searches are due, it says so at the start of the run and suggestsjobhunter companies find. With"queries": [](see Common tasks) it says nothing.More boards are coming in future versions. Also on the way:
jobhunter discoverwill let the AI search the web for job boards and feeds that suit your profile. You pick the ones you want and Jobhunter learns to read them, so the nexthuntincludes them. Found a feed yourself?jobhunter map <url>will add that one.Skip jobs already in your sheet, in any tab, including jobs you added by hand with a link. A job is recognised by its board and ID, or by its link. The same job posted on two boards has a different link on each, so it can show up twice. A posting a board lists more than once, with the same ID and link (Workable lists it once per location), is counted once; the summary shows the extra listings as "duplicates".
Filter in code. A job is ignored if:
- its title doesn't match your roles or main technologies (any software engineer or developer title still goes through, so the AI can judge it; company job boards are stricter, see above)
- it was posted more than 30 days ago
- its employment type or work mode isn't one you chose
- its salary is below your minimum (only compared when the job lists it in the same currency as your minimum; nothing is converted)
- its company is on your avoid list, or one of your industries to avoid appears in its title, company name or tags (Jobhunter has no industry data, so it matches words)
Ignored jobs go to the Ignored tab with the reason. When a job doesn't say something (no salary, say), it isn't ignored for that.
The same job in several locations. Some companies post a job once per country ("Lead Engineer - Vietnam", "Lead Engineer - Germany"). Jobs from the same source with the same company and title, once place names and words like "Remote" are left out, count as one job. The AI sees it once, with every location it's posted in, judges it against the one that suits you best and names that location in the note. One copy goes to your sheet with that score, a remote one if there is one. The other copies are separate postings, each with its own link, so they aren't duplicates: they go to Ignored, so they aren't scored again, and the summary shows them as "copies for other locations".
Score with the AI. The remaining jobs, newest first, are sent to the AI together with your candidate profile, CV and preferences. Each gets a score from 0 to 100 and a short note: why it fits, the main gaps, and anything to watch for, like timezone or location limits. Location is judged here rather than in code, because "EU only" or "EMEA" needs judgement. Jobs are sent in batches of 5, with up to four batches at a time. A run scores at most 100 jobs, counting the same job in several locations once. When more pass your filters and you're running
huntin a terminal, it asks whether to score them all (with the number of AI requests that takes), only the newest 100, or another number you type in; from a script or cron it keeps the limit. The jobs over the limit aren't added to your sheet, and a later run picks them up while they're still listed. To score them right away, runjobhunter hunt --force --max-ai-jobs <n>(--force, because every source was just checked).Write to your sheet. Jobs scoring at or above your cut-off (50 by default) go to To apply; the rest go to Skipped, with the note explaining why.
Print a summary: how many jobs were found, known, ignored and scored, your 10 best matches with their notes, and how many AI tokens the run used. The last line says where your sheet is: its file path, or its link.
If your sheet file is open. A spreadsheet app with the file open would overwrite the new rows the next time you save (and on Windows it blocks the write), so hunt checks first. Before fetching anything, it asks you to close the file, then carries on. It checks again just before writing, because scoring takes a while. If you'd rather not close it then, the jobs are kept and added on your next hunt, so AI results you've paid for are never lost. Run from a script or cron, hunt doesn't wait: it stops before fetching if the file is open at the start, and keeps the jobs for later if it's opened during the run.
Options
| Option | What it does |
| --------------------- | --------------------------------------------------------------------------------------------------- |
| -f, --force | Check every board, even ones checked in the last 6 hours. |
| --dry-run | Show the results without writing anything to your sheet. Doesn't need Google to be connected. |
| --no-ai | Skip the AI: every job that passes the filters goes to To apply, without a score or note. Free. |
| --max-ai-jobs <n> | Score at most this many jobs per run (default 100), without asking. The rest wait for the next run. |
| --since <days> | Ignore jobs posted more than this many days ago (default 30). |
| --min-score <score> | The score needed for To apply, for this run only (0–100). |
| --parallel <n> | AI calls made at the same time (1–16). Default 4, or 1 for a model on your own machine or network. |
Examples:
jobhunter hunt # the usual
jobhunter hunt --dry-run # look before writing anything
jobhunter hunt --since 7 # only jobs from the last week
jobhunter hunt --force # check the boards again right now
jobhunter hunt --min-score 70 # stricter: only strong matches go to To applyjobhunter companies find
Many companies list jobs only on their own careers page, hosted by Greenhouse, Lever, Ashby or Workable. This command finds those boards by searching the web for your roles on each of these sites (for example site:lever.co "Senior Frontend Engineer"), and remembers them so every hunt reads them.
- It searches for your first 3 roles, plus "remote" if remote is your only work mode. With 4 sites that's 12 searches. To search for something else, see Common tasks.
- It searches with what you chose in
jobhunter init→ Web search: your AI's web search, SerpApi, Brave Search or DuckDuckGo. Until you choose, it uses DuckDuckGo, which takes about 10 minutes for 12 searches. - Each search is repeated after a week, to find companies that started hiring since. Run it again before then and there's nothing to do; add
-for--forceto search again anyway. - Progress is saved after every search. Press Ctrl+C any time, or let a rate limit stop it, and the next run picks up where it stopped. After 2 failed searches in a row (no connection, say) it stops too, and leaves the rest for next time.
jobhunter companies find # the usual, about once a week
jobhunter companies find --force # search again nowChoosing a search
A full run is 12 searches a week (3 roles × 4 sites), about 50 a month, so every option below fits its free tier.
| Search | Key | Free | Speed | Good to know | | ---------------------------------------------------------------------------- | -------------------- | ------------------------------------------------------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | SerpApi (Google) | yes | 250 searches a month, 50 an hour | fast | Google's own results, 10 per search. Paid plans start at $25 a month for 1,000. | | Brave Search API | yes | $5 of credit every month, about 1,000 searches ($5 per 1,000) | fast | Up to 20 results per search. Brave asks for a credit card when you sign up, to prevent abuse. | | Your AI's web search (Claude's web search, or Gemini with Google Search) | no, uses your AI key | none: billed to your AI account | slow | The easiest to set up, but costs the most per search: the search itself, the tokens to read its results, and a second request to list the pages. The model writes the actual searches, so results are fewer and less predictable. An organisation admin can turn Claude's web search off. Gemini's free tier has no Google Search for Gemini 3 models: it needs billing turned on, which includes 5,000 free searches a month. | | DuckDuckGo | no | free | very slow | DuckDuckGo has no API, so Jobhunter reads its result pages like a browser and waits 45 seconds between searches: about 10 minutes for 12 searches. It still blocks searches now and then. |
AI search is only offered when your AI provider has one. OpenAI-compatible APIs (OpenAI, OpenRouter, Ollama…) can't search: Chat Completions has no standard web search tool. With one of those, choose SerpApi, Brave or DuckDuckGo.
Prices and free tiers are the services' own and can change; check the linked pages.
jobhunter sheet
Prints where your tracking sheet is: the path of your file, or the link to your Google Sheet. Add -o or --open to open it in your spreadsheet app (or, for a Google Sheet, your browser).
jobhunter sheet # print the path or link
jobhunter sheet --open # open itComing soon
These commands are listed in jobhunter --help but aren't ready yet:
| Command | What it will do |
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| jobhunter discover | Let the AI search the web for job boards and feeds that match your profile; pick the ones you want and Jobhunter learns to read them for hunt. |
| jobhunter sync | Apply the "Change status" picks from the status tabs to the Applications tab (Google Sheets only; the local file's status tabs have no picks). |
| jobhunter map <source> | Add a job feed you found yourself (JSON or XML): the AI works out how to read it, and it joins your boards. |
| jobhunter convert <source> | Convert a job feed to Jobhunter's format with its saved mapping. |
Also planned: using an existing spreadsheet as your tracking sheet, and Jobhunter's own Google sign-in.
Your tracking sheet
init creates it as an .xlsx file where you chose, or as a spreadsheet called Jobhunter in a Jobhunter folder in your Drive. Both have the same tabs:
| Tab | What's in it | | ---------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Applications | Every job you're tracking, one per row: company, role, applied date, where it was found, status, last update, link, the AI's note, two columns for your own notes, and the match score. Rows are coloured by status. | | To apply, Applied, Interview & Offer, Declined, Ghosted, Skipped | Read-only views of Applications filtered by status and sorted by company. They update by themselves as you change statuses. | | Ignored | Jobs the filters dropped, with the reason. They stay here so they aren't checked again. |
Where it was found (the Channel column) names the job board, such as Remotive. For a job from a company's own careers page, it names the system hosting it (Greenhouse, Lever, Ashby or Workable), and Company says whose page it is.
To track an application, change its Status in the Applications tab with the dropdown: To apply → Applied → Interview → Offer, or Declined, Ghosted, Withdrawn or Skipped. Fill in Applied and Last update as you go. The row colour and the view tabs follow the status.
A few hidden columns (Key, Source, Found) and a hidden _meta tab are how Jobhunter recognises jobs it has already seen. Please don't rename or delete them. You can add your own columns after the last one.
Using a local file
- Close it before running
hunt. Jobhunter checks and asks you to (seejobhunter hunt). - Which apps:
- LibreOffice (recommended, free) and Excel (Microsoft 365) are what we test with, including view tabs that update as you change statuses.
- The view tabs use FILTER, SORT and SEQUENCE, which arrived in LibreOffice 24.8 and Excel 2021. Older versions haven't been tested, so use a current one.
- What
huntkeeps and what it rebuilds. It keeps everything you type into Applications and Ignored: statuses, dates, notes, your own columns, rows you add. A formula you type is kept as its result. It rebuilds the formatting, dropdowns and view tab formulas each time. Formatting you change by hand and extra tabs you add aren't kept. - Filtering. Applications and Ignored have filter buttons in the header, for any filter the view tabs don't cover.
- On more than one computer:
- Put the file in a Dropbox, iCloud Drive, OneDrive, Nextcloud or Google Drive folder (choose that path in
init). You get a backup and your sheet on every device, without setting up Google access. - Edit it on one device at a time. Jobhunter can only tell the file is open on the computer it runs on.
- Put the file in a Dropbox, iCloud Drive, OneDrive, Nextcloud or Google Drive folder (choose that path in
- Backup. Before each write, the previous version is copied to
~/.cache/jobhunter/backups/.
Common tasks
Change the AI model or API key. Run jobhunter init → AI provider, key or model. Press Enter at the key prompt to keep your saved key, then pick a model. The new model is used from the next hunt on.
Update your CV. Run jobhunter init → CV. Your preferences and candidate profile are updated after it.
Change what you're looking for. Run jobhunter init → Preferences and candidate profile.
Fine-tune how jobs are scored. Your candidate profile is a plain Markdown file (see Where your data lives). Edit it in any editor, for example to add "no agencies" or "I prefer product companies". The next hunt uses it.
Change the To apply cut-off for good. Use --min-score for a single run. To change the default of 50, add "minScore": 70 (any number from 0 to 100) inside "preferences" in your profile.json.
Choose what's searched for company job boards. By default jobhunter companies find searches for your first 3 roles, plus "remote" if remote is your only work mode. To search for something else, add your own queries to your profile.json. Each one is searched on every site:
"boardSearch": { "queries": ["\"react developer\" remote", "typescript \"product engineer\""] }Use "queries": [] to stop searching. Boards found before are still read; delete ~/.cache/jobhunter/boards.json to forget them.
Change the web search. Run jobhunter init → Web search. Press Enter at the key prompt to keep your saved key.
Run it on a schedule. For example hunt every morning and companies find every Monday, with cron (crontab -e on macOS and Linux):
0 8 * * * /usr/local/bin/jobhunter hunt >> ~/jobhunter.log 2>&1
0 7 * * 1 /usr/local/bin/jobhunter companies find >> ~/jobhunter.log 2>&1- Use the full path that
which jobhunterprints: cron doesn't load your shell'sPATH, so a Node.js installed with nvm or Homebrew isn't found otherwise. - Without a terminal, nothing waits for an answer.
huntscores at most 100 jobs (add--max-ai-jobs <n>to change that), and stops before fetching if your sheet file is open. The output goes to~/jobhunter.log. - With a Google Sheet in "Testing" mode, the sign-in expires after 7 days and scheduled runs fail until you sign in again; publish the app to avoid that.
Sign in again, or switch Google accounts. Run jobhunter init → Tracking sheet → In a Google Sheet → Sign in again.
Use a different OAuth client file. Same menu → Use a different credentials file.
Find your sheet. Run jobhunter sheet --open.
Move or rename your sheet file. Move it, then run jobhunter init → Tracking sheet → In a spreadsheet file on this computer and give the new path. Jobhunter recognises it and keeps its jobs.
Switch between a local file and Google Sheets. Run jobhunter init → Tracking sheet and choose the other one. Jobs aren't copied between them yet.
Start with a fresh sheet.
- Local file: run
jobhunter init→ Tracking sheet → In a spreadsheet file on this computer and give a new file name. - Google Sheet: choose In a Google Sheet → Keep it for the connection → Create a new one for the sheet.
The old sheet stays where it is until you delete it. Because the new sheet is empty, the next hunt treats every listed job as new and scores it again. Add --force if the boards were checked in the last 6 hours.
Have a job checked again. Delete its row from Applications or Ignored. The next hunt (with --force, if the boards were checked recently) will treat it as new.
Disconnect Google. Remove Jobhunter's access in your Google Account permissions, and delete google-token.json (and google-credentials.json if you like) from the config folder.
Uninstall.
npm uninstall -g jobhunter
rm -rf ~/.config/jobhunter ~/.cache/jobhunterIf you used Google, remove its access as above. Your tracking sheet stays where it is: on your computer or in your Drive.
Where your data lives
Everything is stored on your machine, in ~/.config/jobhunter (or $XDG_CONFIG_HOME/jobhunter):
| File | What it holds |
| ---------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| config.json | AI provider, model, API key (and base URL for an OpenAI-compatible API), and the web search (with its key) |
| google-credentials.json | your Google OAuth client (a copy of the file you downloaded) |
| google-token.json | your Google sign-in |
| profiles/default/profile.json | your preferences, facts from your CV, and where your tracking sheet is |
| profiles/default/pending.json | jobs found while your sheet file was open, until the next hunt adds them |
| profiles/default/cv.md | your CV as Markdown |
| profiles/default/instructions.md | your candidate profile, the text the AI scores jobs by |
Files holding keys or tokens are readable only by you (mode 600), and Jobhunter warns you if their permissions get looser. When each board was last checked is kept in ~/.cache/jobhunter/sources.json. Deleting it just makes the next hunt check every board. The company job boards found by search, and when each search ran, are in ~/.cache/jobhunter/boards.json. Deleting it means running jobhunter companies find again from scratch. The previous version of a local sheet is kept in ~/.cache/jobhunter/backups/.
Set JOBHUNTER_HOME to keep everything in another folder instead (useful for testing).
Privacy
- The AI provider you chose (Anthropic, Google, or the OpenAI-compatible API at your base URL) receives your CV when it's converted (PDF and DOCX only) and when your profile is written. On each
huntit also receives your candidate profile, CV and preferences along with the jobs being scored. See your provider's privacy policy, for example Anthropic's or Google's Gemini API terms. On Gemini's free tier, Google may use this data to improve its products. - Your tracking sheet stays on your computer when it's a local file. With a Google Sheet, Google stores it in your Drive, and Jobhunter can only reach the files it created.
- Job boards are fetched anonymously. Nothing about you is sent to them.
- The web search you chose (your AI provider, SerpApi, Brave or DuckDuckGo) receives the searches for company job boards: your role titles (or your own queries) and the site names. Nothing else from your CV or profile is sent. DuckDuckGo searches are sent with the headers Chrome uses, because DuckDuckGo blocks nearly every search from a non-browser User-Agent; everything else Jobhunter fetches says it's Jobhunter.
- Jobhunter itself has no server and collects nothing.
Troubleshooting
"Your Google sign-in has expired or was revoked." Your app is probably still in "Testing" mode, where sign-ins last 7 days. Run jobhunter init → Tracking sheet → In a Google Sheet → Sign in again, and publish the app so it doesn't happen again.
"Access blocked" or "has not completed the Google verification process" when signing in. Add your Google address under Audience → Test users in Google Auth Platform, or publish the app.
"Google Sheets API has not been used in project … or it is disabled." Enable both Sheets and Drive for the project your OAuth client belongs to. Wait a minute, then try again.
"DuckDuckGo asked to slow down." DuckDuckGo's bot check answered instead of results. Nothing is lost: the searches that ran are kept, the rest run the next time you run jobhunter companies find, and hunt still reads the boards already found. If it happens every time, wait an hour or two, since repeated tries keep the block going, or choose a faster search in jobhunter init → Web search.
"… didn't accept the API key" or "… run out of searches" from companies find. Check the key or plan with the search service, then run jobhunter init → Web search to paste a new key or choose another search.
"Gemini answered "You exceeded your current quota…" to a Google Search request." On Gemini's free tier, Gemini 3 models can't use Google Search at all, so its quota is zero and waiting doesn't help. Your key still works for scoring. Either turn on billing for your project in Google AI Studio (that includes 5,000 free searches a month; see Gemini API pricing), or run jobhunter init → Web search and choose SerpApi, Brave or DuckDuckGo.
"Stopped after 2 failed searches in a row" from companies find. The search service couldn't be reached twice running, usually because there's no internet connection (or a VPN or firewall is in the way). The searches that went through are kept; check your connection and run jobhunter companies find again for the rest.
"Company job boards are searched for by your roles, which come from your CV." companies find needs your CV first. Run jobhunter init → CV, or set your own queries in profile.json (see Common tasks).
"Couldn't read N company job boards; the next hunt tries again." Those companies' boards didn't answer this time (a timeout, or the service was down). Nothing to do: they're read again on the next hunt. A board that no longer exists is forgotten by itself.
"jobhunter.xlsx is open in LibreOffice…" Close the file and choose It's closed now. If nothing has it open (the app crashed, say), choose Continue anyway: a lock file was left next to your sheet (.~lock.jobhunter.xlsx# for LibreOffice, ~$… for Excel), and you can delete it.
"Can't find your tracking sheet at …" The file was moved, renamed or deleted. Run jobhunter init → Tracking sheet and give its new path. If it was deleted, restore it from ~/.cache/jobhunter/backups/ (that copy is from before the last write, so it's missing the jobs that write added), or give a new name to start a fresh one.
"Every source was checked recently." Each board is checked at most once every 6 hours. Use jobhunter hunt --force to check now.
"Before hunting, set up …" Part of the setup is missing. Run jobhunter init and finish the step it names.
The API key is rejected, or there's no credit. Check your key and billing with your provider (the Anthropic Console, Google AI Studio, or your OpenAI-compatible service), then run jobhunter init → AI provider, key or model.
Too many good jobs in Skipped, or too many poor ones in To apply. Lower or raise the cut-off with --min-score, or edit your candidate profile (instructions.md) to say more clearly what you want and what you don't.
Development
git clone https://github.com/nkavt/jobhunter.git
cd jobhunter
npm install
npm run build # bundle to dist/ with tsup
npm link # make `jobhunter` available globally from this checkout
npm test # vitest
npm run lint # eslint
npm run typecheck # tsc --noEmit
npm run format # prettiernpm run dev rebuilds on every change. Tests never touch your real config: they use a temporary JOBHUNTER_HOME and mock every network call.
License
MIT © 2026 Nikoloz Kavtaradze
