I built a zero-dependency CLI that exports remote job data to CSV / JSON / SQLite (open source)
Pulling remote-job data for analysis usually means one of two unpleasant things: scrape five job boards yourself (each with its own HTML, its own quirks, its own rate limits), or take a paid scraping service on faith.
I got tired of both, so I built a small tool that turns one free API call into a clean CSV, a JSON file, or a SQLite database-with the salary fields already parsed. It's called remote-jobs-export.
The problem
The problem I wanted to answer was a simple question: how many senior remote Python roles are actually listed right now, and what do they pay?
To answer that, I needed the data in a shape a spreadsheet or SELECT query could chew on. But the "obvious" path was a mess:
- Remotive, RemoteOK, Jobicy, We Work Remotely, Hacker News-five different boards, five different response formats.
- Salary comes back as a string like "120k-160k USD/yr" or "€50/hr" or nothing at all. Doing
int()on that is how people get sad. - Every board has its own "when was this posted" format-ISO, RFC-2822, or blank.
You could scrape each one, normalize in your head, and pray. I'd rather not.
The build
The whole thing is stdlib-only Python-no requests, no bs4, no pandas. Just urllib, csv, json, sqlite3, re.
If it's on PyPI and you can pip install it, it runs anywhere Python does, with zero transitive dependencies to audit.
It pulls from the free Remote Jobs API, which already aggregates those five boards into one schema. The export tool sits on top of that and does the last mile: fetch, filter, and write to the format you actually want.
pip install remote-jobs-export
Usage
The CLI is deliberately boring:
# Snapshot 200 jobs to a CSV you can open in Excel
remote-jobs-export --limit 200 -o jobs.csv
# Same data, but as a SQLite DB for SQL analysis
remote-jobs-export --limit 200 -o jobs.db
# JSON for a pipeline
remote-jobs-export --limit 200 -o jobs.json
# Just a one-line summary
remote-jobs-export --limit 300 --summary
And because the whole point of job data is filtering, the filters map straight to the API:
# Only Remotive
remote-jobs-export --source remotive -o remotive.csv
# Python or DevOps roles, minimum fit-score 70
remote-jobs-export --skills python,devops --min-score 70 -o top.csv
# Only jobs paying at least $90k (server-side, on the parsed top-of-range)
remote-jobs-export --min-salary 90000 -o senior.csv
That last one is the one I actually use. The parsed salary_max is what makes it work-the API decodes "120k-160k USD/yr" into salary_min=120000, salary_max=160000, salary_currency=USD, salary_period=year so the filter can compare numbers instead of regex-matching a string.
The output is real
I'm not going to paste a fabricated jq demo. This is the actual summary the tool printed against the live API a few minutes ago (300 jobs pulled):
{
"total": 300,
"by_source": [
["jobicy", 120],
["remoteok", 86],
["wwr", 78],
["remotive", 16]
],
...
}
And a real CSV row from the same run-notice the parsed salary columns, the canonical ISO-8601 published, and the structured fit_score:
"48219da41fb1c336","Coordinator, Payroll Client Services","Remote","Anywhere in the World","All Other Remote","wwr","","","","","","2026-09-29T07:30:55+00:00","https://weworkremotely.com/remote-"
When a posting has no salary, those columns are empty (honest null-ish), not a guessed number.
The published field is always canonical ISO-8601 UTC, which is the kind of thing that bites you when you're trying to sort by recency across five boards with three date formats.
Design choices
A few things I'd want you to know before you reach for it:
- Stdlib-only on purpose. The data is public and the API is free; there's no reason a snapshot tool should drag in a dependency tree. Easier to audit, easier to pin, runs in a constrained environment.
- The API is keyless for the free tier. No key juggling for a quick snapshot. (A free tier exists for higher volume, but you don't need it for
--limit 200.) - It's a companion, not a scraper. The board-scraping and schema-normalization live in the API; this tool is strictly the local export layer. If you want the data in a spreadsheet or a DB, that's the job. If you want the live stream, talk to the API directly.
Where it lives
- Source:
github.com/earnnova-dev/remote-jobs-export(MIT) - Package:
pip install remote-jobs-export(PyPI) - The upstream API:
remote-jobs-api.tten.no(keyless JSON, parsed salary,?min_salary=Nfilter)
It's small, it's free, and it does one thing-get normalized remote-job data into a local file you can actually analyze-without making you scrape five boards or trust a $20/mo scraper.
If that's your use case, try the --min-salary 90000 one-liner above and see what the live market looks like today.
Comments
No comments yet. Start the discussion.