Say you want a short list every morning: new remote Python and data engineering jobs, posted in the last 24 hours, from the boards you trust. Maybe it's for your own job search, maybe it's for a Slack channel, a Discord community or a weekly newsletter.
The four boards in this article all publish a feed for this kind of reuse, but each one does it differently. Remote OK and Jobicy have JSON APIs, yet Remote OK calls the job title position and Jobicy calls it jobTitle. We Work Remotely has one RSS feed per category, with company and title packed into one field like Company: Job title. Working Nomads has its own JSON list. Before you can filter anything, you write four parsers, normalise dates, locations and salaries, and remove jobs posted on two boards.
This tutorial uses an Apify Actor that already does that merging, so you can go straight to the alert.
Disclosure: I built and maintain this Actor. It is a paid tool ($1.00 per 1,000 results). I've tried to describe what it can't do as plainly as what it can.
What you can do with the data
- A personal job alert. Run it once a day with your keywords and get only the jobs you haven't seen yet.
- A job newsletter or community feed. Post a daily digest of remote jobs in your niche, with the source board credited on every line.
- Salary and market research. Jobicy and Remote OK often state salary ranges, so you can compare pay by role over time.
Step 1: Set up a run in Apify Console
Open Remote Jobs Scraper and click Try for free. The input form has these fields:
| Field | What it does |
|---|---|
Keywords (keywords) | Keep jobs whose title, tags or category contain one of these, e.g. python, data engineer. Whole words only, so java does not match JavaScript. |
Exclude title keywords (excludeKeywords) | Skip jobs whose title contains one of these, e.g. senior or sales. |
Locations (locations) | Keep jobs whose location text contains one of these, e.g. Europe, USA, Worldwide. |
Worldwide jobs only (worldwideOnly) | Keep only jobs whose location says Worldwide, Anywhere or Global. |
Posted within (days) (postedWithinDays) | Keep only jobs posted in the last N days. Set it to 1 for a daily alert. |
Jobs with salary only (withSalaryOnly) | Keep only jobs that state a salary range. |
Job boards (sources) | remoteok, weworkremotely, jobicy, workingnomads. Empty means all four. |
Max results (maxItems) | Upper limit, newest first. Empty means every matching job. |
Include description (includeDescription) | On by default. Adds the description as plain text. includeDescriptionHtml adds the original HTML. |
Max description length (descriptionMaxLength) | Cuts descriptions to this many characters to keep the dataset small. |
All filters run before results are saved, so you only pay for jobs that match. No login, API key or proxy is needed.
Step 2: Run it and export
Click Start. When the run finishes, open the Output tab and export as JSON, CSV or Excel.
For this article I used this input:
{ "keywords": ["python", "data engineer"], "postedWithinDays": 7, "maxItems": 30, "descriptionMaxLength": 300 } The run took about 10 seconds and saved 19 jobs: 12 from Jobicy, 5 from We Work Remotely, 1 from Remote OK and 1 from Working Nomads. Here are two of them, with some fields removed and descriptions shortened (values unchanged):
[ { "source": "We Work Remotely", "sourceJobId": "discord-senior-data-engineer-ads", "title": "Senior Data Engineer, Ads", "company": "Discord", "category": "All Other Remote", "employmentType": "Full-Time", "location": "Anywhere in the World", "isWorldwide": true, "salaryMin": null, "salaryMax": null, "postedAt": "2026-11-01T07:30:53Z", "expiresAt": "2026-12-01T07:30:53Z", "jobUrl": "https://weworkremotely.com/remote-jobs/discord-senior-data-engineer-ads", "companyHeadquarters": "Remote (U.S.)", "scrapedAt": "2026-11-02T13:25:17Z" }, { "source": "Jobicy", "sourceJobId": "158160", "title": "Senior Data Intelligence Engineer", "company": "Deepgram", "category": "Data Science & Analytics", "employmentType": "Full-Time", "seniority": "Senior", "location": "USA", "isWorldwide": false, "salaryMin": 165000, "salaryMax": 205000, "salaryCurrency": "USD", "salaryPeriod": "year", "postedAt": "2026-10-31T09:30:24Z", "expiresAt": null, "jobUrl": "https://jobicy.com/jobs/158160-senior-data-intelligence-engineer", "scrapedAt": "2026-11-02T13:25:17Z" } ] Both records have the same columns even though they come from an RSS feed and a JSON API. Fields a board doesn't provide are null.
One thing I noticed in this sample: the keyword filter is broad on purpose. The words of a phrase must appear in the same field, but not next to each other, so "Senior Applied Data Scientist / ML Engineer" matched data engineer. Tags count too: "Automation Specialist" came back because Python is one of its tags, and the one Remote OK job carried 20 tags, from python and devops to marketing and medical. That's fine for research, but for an alert you'll want a stricter check, which is easy in pandas.
Step 3: Turn it into a daily alert with Python
Install the official client and pandas:
pip install apify-client pandas Run the Actor for the last day only:
from pathlib import Path from apify_client import ApifyClient import pandas as pd client = ApifyClient("YOUR_APIFY_TOKEN") run = client.actor("pipiagent/remote-jobs-scraper").call(run_input={ "keywords": ["python", "data engineer"], "postedWithinDays": 1, "descriptionMaxLength": 300, }) items = list(client.dataset(run.default_dataset_id).iterate_items()) if not items: raise SystemExit("No matching jobs today.") df = pd.DataFrame(items) Keep only jobs where the title itself fits, or where python is one of the tags:
title_fits = df["title"].str.contains(r"\bpython\b|\bdata engineer", case=False, regex=True) python_tag = df["tags"].apply(lambda tags: "python" in [t.lower() for t in (tags or [])]) df = df[title_fits | python_tag] print(df.groupby("source").size()) On my 19-job sample, this kept 14 jobs.
Then remember which jobs you already sent, and write the new ones as a Markdown digest. Each line names the board it came from:
seen_file = Path("seen_jobs.txt") seen = set(seen_file.read_text().split()) if seen_file.exists() else set() new = df[~df["jobUrl"].isin(seen)] seen_file.write_text("\n".join(sorted(seen | set(new["jobUrl"])))) lines = [] for _, job in new.iterrows(): pay = "" if pd.notna(job["salaryMin"]) and pd.notna(job["salaryMax"]): pay = f", {job['salaryCurrency']} {job['salaryMin']:,.0f}-{job['salaryMax']:,.0f}" where = job["location"] or "location not given" lines.append(f"- [{job['title']}]({job['jobUrl']}) at {job['company']} ({where}{pay}), via {job['source']}") Path("digest.md").write_text("\n".join(lines), encoding="utf-8") On the sample data, the digest lines look like this:
- [Senior Data Engineer, Ads](https://weworkremotely.com/remote-jobs/discord-senior-data-engineer-ads) at Discord (Anywhere in the World), via We Work Remotely - [Sr. Data Engineer](https://jobicy.com/jobs/158109-sr-data-engineer-4) at PadSplit (USA, USD 180,000-200,000), via Jobicy Run the script once a day with cron or Task Scheduler and send digest.md wherever you like. If you'd rather not run anything yourself, create a schedule in Apify with Posted within (days) set to 1 and connect an integration for Slack, email or Google Sheets.
Credit the source. The boards share these feeds on the condition that you link back to them and name them as the source when you publish their jobs. Keep source and jobUrl in anything public, as the digest above does.
What it costs
You pay per saved job: $1.00 per 1,000 jobs, with no start fee and no monthly rental. Filters run first, so jobs that don't match are free.
- My 30-job test: $0.03.
- A daily alert that saves about 20 jobs a day: $0.02 per day, about $0.60 for a 30-day month.
- Everything the four boards show at once (about 2,000 jobs, per the README): about $2.00.
You can also set a maximum charge per run in the run options, and the Actor stops when it reaches that amount.
Limitations
- The boards show recent jobs only. Remote OK shows its latest 100 jobs, We Work Remotely its latest jobs per category (about 500 in total), Jobicy the last 7 days and Working Nomads its latest few dozen. To build a history, run it daily and keep the results.
- Locations are free text. Each board writes them its own way ("USA Only", "Anywhere in the World", "EMEA, LATAM"), so put several words in Locations. Remote OK often gives no location, and those jobs are skipped by location filters.
- Salaries appear only when the employer states them, mostly on Jobicy and Remote OK. In my sample, all salaries came from Jobicy.
- Only four boards. Boards whose terms don't allow passing listings on, or that need a login, are not included.
- This is unofficial. The Actor is not made by, endorsed by or connected to any of the job boards.
Related tools
If you want jobs straight from employers rather than from job boards, Career Site Jobs Scraper reads company career sites on Greenhouse, Lever, Ashby, Workday and six more systems. To reach the hiring companies, Website Contact Scraper collects public emails, phone numbers and social links from their websites.
If you want to try the alert with your own keywords, the Remote Jobs Scraper store page has the input form and more examples. If something doesn't work the way this article says, open an issue there and I'll look into it.



