remote-jobs-digest
Stop refreshing ten job boards. Get one daily digest of the remote roles you can actually get.
"Remote" on a job board often means remote, if you live in California. rjs reads the whole posting and keeps the ones that fit where you live, the years you have, your stack and your salary floor. It pulls from public company career pages (Greenhouse, Ashby, Lever…) and remote job APIs, drops 99 % of the noise with plain rules at zero cost, and optionally asks an LLM about the few dozen survivors.
Quickstart
pipx install remote-jobs-digest # or: uv tool install remote-jobs-digest
rjs init # 13 questions, or: rjs init --from backend-ai-eu
rjs run --no-ai # prints today's digest, no API keys needed
A first run takes under a minute. It prints a summary and the digest, and writes a CSV, a JSON and
digest_latest.md to the data dir (rjs paths shows where).
💼 Remote jobs digest — 9 shortlisted, 🆕 9 new (out of 4853 collected)
🚫 173 management/staff+ · 🏢 4 consultancy · ⚠️ 44 to review
1. 🆕 Frontend Web Application Developer — KoboToolbox
[stack 3/10] · 💰 90,000–105,000 USD
https://remotive.com/remote-jobs/... (via Remotive)
To cover more companies, build the list of company ATS boards once (about 10 minutes, all public APIs):
rjs boards build --fetch # ~1,100 remote-friendly companies from public GitHub lists
rjs boards discover # finds their Greenhouse/Ashby/Lever/... boards (~500)
Why this and not…
| Instead of… | Use that when | Use rjs when |
|---|---|---|
| career-ops | You want the whole funnel inside an AI coding CLI: evaluation reports, tailored CVs, tracking, negotiation. It is far more complete. | You only want to find the jobs, every morning from cron, without spending tokens on each posting. rjs spends none unless you enable the LLM, and only on survivors. |
| JobSpy | You want raw rows from LinkedIn/Indeed/Glassdoor in a DataFrame. | You want them filtered: rjs adds location eligibility, years, stack, salary and consultancy rules on top. It can use JobSpy as an optional source. |
| LinkedIn / Remotive / Himalayas alerts | Keyword alerts are good enough. | You keep getting "Remote (US only)", "Senior Staff, 10+ years" or agency reposts. |
How it works
flowchart LR
A[ATS boards<br/>Greenhouse · Ashby · Lever · …] --> C[collect + dedup]
B[Remote OK · Remotive · Himalayas<br/>Working Nomads · NoDesk · HN] --> C
C --> D[rules: location eligibility · level/years<br/>stack score · salary floor · consultancy]
D -->|~1% survive| E{LLM pass?<br/>optional}
E --> F[rank]
F --> G[digest: stdout · Markdown · Telegram]
F --> H[CSV + JSON]
- Location eligibility uses roles, not a country list: home (where you live: onsite, hybrid and remote all fine), region (e.g. EMEA, LATAM), away (other countries in your region: remote only), blocked. "Remote" with no scope, or "Remote — LATAM" for a Europe-based user, goes to REVIEW, never straight to MATCH.
- Level comes from what the posting asks, not the title: "Senior Engineer, 3+ years" is mid; "Engineer, 8+ years" is not.
- Stack is a weighted table: strong words count anywhere, weak words ("python", "cloud") only in the title or tags, so a job does not score 10/10 because the company blog mentions AWS.
- Salary floors per currency. Unknown currencies are flagged, not assumed to be USD.
- The optional LLM reads the full description of the survivors and returns real level, fit 0–10, workload and a one-line reason. Its instructions are generated from your profile.
Configuration
rjs init writes ~/.config/rjs/config.yaml. Everything the filter uses is in that file;
rjs config show prints the effective values and rjs config check validates edits.
Bundled examples (rjs init --list-examples): backend-ai-eu, frontend-latam.
| Key | What it does |
|---|---|
geo.home_hints / region_hints / away_hints / blocked_hints |
Location roles described above |
geo.accept_modes |
remote, hybrid, onsite |
experience.ideal_min / ideal_max / stretch / max_required |
Years band: ideal → MATCH, stretch → REVIEW, above max → rejected |
stack.weighted |
[{weight, label, strong: [...], weak: [...]}] |
stack.required |
Hard AND: words that must appear |
salary.floor |
{EUR: 40000, USD: 60000} |
company.kind |
product, any or consultancy |
company.consulting_companies |
Consultancies to drop. The package ships none: your call |
company.skip_platforms |
Categories of the bundled platform list to drop: talent_marketplace, freelance_marketplace, ai_data_work, reposting_intermediary (default: all). rjs platforms shows every name and its source |
company.allow_platforms |
Bundled names to keep anyway (e.g. [Toptal] if you want Toptal gigs) |
company.staffing_platforms |
Your own extra platform names to drop |
signals.negative |
Phrases that reject a job ("must reside in the US", "security clearance"…) |
search.active_sources |
Which sources run |
narrative or ~/.config/rjs/profile.md |
Free text about you, read by the LLM |
Keys you leave out take built-in defaults (the backend-ai-eu example), so check
rjs config show after editing by hand.
Environment variables (~/.config/rjs/.env works too): see .env.example.
The LLM pass takes any OpenAI-compatible endpoint: RJS_LLM_BASE_URL, RJS_LLM_API_KEY,
RJS_LLM_MODEL (Groq by default; OpenAI, OpenRouter, Gemini, Ollama and LM Studio work).
Telegram: TELEGRAM_BOT_TOKEN + TELEGRAM_CHAT_ID.
Running it daily
rjs run is idempotent and remembers what it already showed you (🆕 marks new jobs).
Pick one: cron, a systemd user timer, or a
GitHub Actions schedule in a private repo (no server).
Sources, ethics and terms of service
| Source | Access | Default |
|---|---|---|
| Company ATS boards (Greenhouse, Ashby, Lever, SmartRecruiters, Recruitee, Breezy, Workable, Personio) | Public job-board APIs meant for embedding | on |
| Remote OK, Remotive, Himalayas | Public APIs; attribution required: every digest line says "via …" and links to the original | on |
| Working Nomads, NoDesk, HN "Who is hiring" | Public API / RSS / Algolia API | on |
| We Work Remotely | RSS; applying needs a paid account | off |
| LinkedIn (guest endpoint), Indeed / Glassdoor / Google (via JobSpy), Built In | Scraping; their terms forbid automated access | off: opt in via search.active_sources, your IP and your call |
Please keep the defaults polite: Remotive asks for at most ~4 requests a day and Himalayas caches for 24 h, so one run a day is plenty. The digest is for you; do not republish it as a job board.
The apply extra (supervised pilot)
pip install 'remote-jobs-digest[apply]' adds tools the author uses after the digest: dossiers
with a draft cover letter (rjs apply), LLM re-verification of full postings (rjs verify),
screening-question drafts (rjs answer), ATS form pre-fill in a real browser that stops before
submit (rjs fill), cold-email drafts (rjs outreach) and inbox triage (rjs gmail).
Personal data lives in ~/.config/rjs/identity.yaml. Nothing is sent or submitted for you. These
tools are less polished than the core, their prompts and messages are still partly in Spanish, and
automating applications can break job sites' terms. Use them as drafts.
Limits and non-goals
- Not an auto-apply bot, and it will not become one.
- Without the LLM pass, stack scoring is keyword-based and coarse: a Python job that mentions
Kubernetes can reach a Go profile. The LLM pass, or a finer
stack.weightedtable, fixes most of it. - The rules are tuned for software roles; other fields need your own stack table and signals.
- Location matching is text matching. Unusual phrasings end up in REVIEW, which is the point.
- Some code comments are in Spanish; the CLI, logs, output and docs of the core are English. The
CSV
verdictcolumn keeps its original values (APTA= match,REVISAR= review,DESCARTADA= rejected). PRs welcome. - Linux and macOS. Windows is untested.
Roadmap
--export career-opsto feed its pipeline.- Labelled golden sets per example profile, with precision/recall in CI.
- More notification targets (Slack, email) via apprise.
License
MIT. Job data belongs to the sources and employers; respect their terms.
Release files for remote-jobs-digest 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| remote_jobs_digest-0.1.1.tar.gz | 156.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| remote_jobs_digest-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 319.9 kB
Release files / remote_jobs_digest-0.1.1.tar.gz
| Download URL | remote_jobs_digest-0.1.1.tar.gz |
|---|---|
| Size | 156.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c444c7844c02ca4886ceb0338e0591dac9a7e8c4c0617658edcf2cfe4c8b0d81
|
|
BLAKE2b-256 checksum How to use checksums |
87db1d23eba0685b58af7fe8fcb8b422ed922e18bafd85b83518f6202be69a22
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.
Transparency logRelease files / remote_jobs_digest-0.1.1-py3-none-any.whl
| Download URL | remote_jobs_digest-0.1.1-py3-none-any.whl |
|---|---|
| Size | 163.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
738db5b5712fe79d8f825a9554a01db1b7734513ecde0f83e93f2b2280f65cac
|
|
BLAKE2b-256 checksum How to use checksums |
c777b0ce5430bb5d5bfbc8db1a24a6a7d5e853891ad38ebc961c178427f3d57e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.
Transparency log