Remote Jobs Aggregator & Scraper: Remote Job Boards, One Feed
Pricing
from $1.60 / 1,000 job results
Remote Jobs Aggregator & Scraper: Remote Job Boards, One Feed
Remote jobs aggregator and remote jobs scraper, no API key: RemoteOK, WeWorkRemotely, Working Nomads, Remotive, Jobicy, Himalayas, The Muse, HN Who is hiring in one deduplicated feed. New remote jobs alerts on a schedule, remote job boards scraper with salaries, JSON/CSV, Slack digests. $2/1k.
Pricing
from $1.60 / 1,000 job results
Rating
0.0
(0)
Developer
Flash Scrape
Maintained by CommunityActor stats
0
Bookmarked
19
Total users
15
Monthly active users
6 hours ago
Last modified
Categories
Share
Remote Jobs Aggregator is a remote job scraper that sweeps 10 keyless remote job boards in one run — RemoteOK, We Work Remotely, Working Nomads, DevITjobs (US + UK), The Muse, Remotive, Jobicy, Himalayas and the Hacker News "Who is hiring?" thread — and returns one deduplicated feed with no API key, no proxy and no login, at $0.002 per deduplicated job ($2 per 1,000; a scheduled pricing record raises this to $0.003 per job on 2026-09-14).
Try it: Daily new remote data analyst jobs alert (only new postings)
Remote job aggregator — a remote job boards scraper that merges 10 keyless boards by default — plus one opt-in board: Arbeitnow — into remote jobs in one feed, deduplicated, with only-new-jobs monitoring.
At a glance
| Boards | 10 keyless remote boards by default — RemoteOK, We Work Remotely, Working Nomads, DevITjobs US + UK, The Muse, Remotive, Jobicy, Himalayas, Hacker News "Who is hiring?" — plus opt-in Arbeitnow. devitjobs.uk is currently down upstream; it is reported per run, never billed |
| Dedup | Merged across boards by canonical job URL and by (title, company) — a role on two boards ships and bills once |
| Price | $0.002 per deduplicated job — $2 per 1,000 on the free plan, so 500 jobs = $1.00 (+ $0.00005 run start); paid plans pay less (Pricing tab). Rising to $0.003 per job on 2026-09-14 under a scheduled pricing record |
| Alerts | onlyNewJobs on a schedule delivers and bills only unseen postings; webhookUrl posts a digest to Slack, Discord or any webhook — Turn it into a job alert |
| Integrations | Apify Console schedules, n8n / Make / Zapier, MCP for AI agents; export JSON, CSV or XLSX |

Every posting is normalized to the same schema (title, company, location, salary where the board publishes one, posting date, tags, description snippet, direct link), and the same role listed on two boards ships once: rows are merged across boards by canonical job URL and by (title, company). That cross-board deduplication is what a single-board scraper can't give you — you pay per unique job, not per feed it happened to appear in.
Three things no other multi-board remote actor offers:
onlyNewJobsmonitoring — run it on a schedule and every run delivers (and bills) only postings you haven't seen before. A run where nothing is new delivers 0 rows, says so plainly, and bills no per-row events (only the $0.00005 start). See Turn it into a job alert.- Annualized salaries — hourly/weekly/monthly figures are converted to yearly (×2080 / ×52 / ×12) so
salary_min/salary_maxare comparable across boards, with a sanity guard against absurd conversions. - Descriptions — a 500-character
description_snippeton every row that has one (88% of rows on the 2026-08-15 default run), and the full HTML-stripped text viaincludeDescription.
One opt-in board, Arbeitnow (Germany-centric, remote rows only), joins the sweep only when you add it to boards. See Boards.
How much does it cost?
$0.002 per deduplicated job — $2 per 1,000 — plus $0.00005 per run start (the actor's live pricing record, read 2026-08-29 and re-read 2026-09-05; the Pricing tab on this page is always authoritative). A scheduled pricing record raises this to $0.003 per delivered job on 2026-09-14 (notified 2026-08-30); the Pricing tab on this page is authoritative. $5 buys 2,499 jobs: 2,499 × $0.002 + one $0.00005 run start = $4.998, just inside the credit. A visitor on Apify's free plan can run it without a card — the plan's monthly usage credit ($5 at the time of writing) covers about 2,499 deduplicated jobs from this actor if spent on it alone. Duplicates merge before billing, filtered rows are never charged, and a nothing-new monitoring run or zero-match run bills only the run start.
The minimal input — copy, paste, run:
{ "searchTerms": ["python"], "maxItems": 100 }
Completely empty input {} also works: up to 100 of the current remote jobs across every board.
How it compares
Read from Apify's public Store API on 2026-09-05. Seven other remote aggregators: benthepythondev (738 users, 5.0 from 1 review, 6 boards, $15/1,000 plus $10/1,000 for salary extraction), inlifeprojects/himalayas-jobs-scraper (608, no reviews, 1 board, $1/1,000), sync-network (282, 3.0 from 1, 4 boards, $3/1,000), memo23 (87, 5.0 from 2, 6 boards, $1.99/1,000, adds employer emails), get_anything (34, 5.0 from 2, 6 boards, $2/1,000), nivlekk (21, no reviews, 3 boards named in its title and 7 selectable in its input schema, $0.50/1,000) and delightful_unicorn (17, no reviews, 3 boards, $1/1,000).
This Actor (18 users, no reviews yet) sweeps the most boards, 10 keyless plus opt-in Arbeitnow, at $2 per 1,000 deduplicated jobs — a change to $3 per 1,000 is scheduled for 2026-09-14. No LinkedIn, Indeed or Glassdoor; salary on 35% of rows on the measured default run.
Use from an AI agent
- MCP: point Claude, ChatGPT, Cursor or any MCP client at
https://mcp.apify.com?tools=flash_scraper/remote-job-aggregator; the tool is named after the Store slug and takes this actor's input unchanged. Keywords for the server'ssearch-actorstool: remote jobs, job listings, job board, remote job scraper, job postings. Tool-name spellings, payment without an Apify token and measured timings: Use it from an AI agent (MCP). - Smallest useful call (Python
apify-client; the same JSON works in the Console, the REST API and n8n/Make/Zapier):
from apify_client import ApifyClientclient = ApifyClient("<APIFY_TOKEN>")run = client.actor("flash_scraper/remote-job-aggregator").call(run_input={"searchTerms": ["python"], "maxItems": 20, "boards": ["remoteok", "jobicy", "remotive", "working_nomads"]})rows = client.dataset(run["defaultDatasetId"]).list_items().items
- Output contract: one row per remote job, the same columns on every row, salaries annualized where a board publishes one. The full field list with measured fill rates is under Output columns; every run also writes a machine-readable
RUN_SUMMARYrecord to its key-value store.
What you get
One row per remote job, the same columns on every row: title, company, location, salary where the board publishes one (annualized), posting date, tags, a 500-character description snippet and the direct link. On the 2026-08-15 default run, 100 deduplicated jobs came from 9 boards with 6 cross-board duplicates merged — 35% of rows carried a salary, 88% a description snippet and 100% a posting date.
One row per remote job:
{"title": "Director, GTM Strategy & Analytics","company": "Temporal Technologies","location": "Anywhere in the World","remote": true,"salary_min": 260000,"salary_max": 326000,"salary_currency": "USD","salary_interval": "yearly","salary_source": "description","url": "https://weworkremotely.com/remote-jobs/temporal-technologies-director-gtm-strategy-analytics","source_board": "weworkremotely","posted_at": "2026-08-15","tags": ["Product"],"description_snippet": "Headquarters: United States - Remote Opportunity About Us Temporal is an open source programming model that can simplify code…","description": null,"scraped_at": "2026-08-15T20:15:17Z"}
(A real row from a live test run on 2026-08-15. That run delivered 100 deduplicated jobs from 9 boards with 6 cross-board duplicates merged — 35% of rows carrying a salary, 88% a description snippet and 100% a posting date, all measured on that run, not estimated.)
Input
| Field | Type | Default | What it does |
|---|---|---|---|
searchTerms | array of strings | [] (everything) | Keywords, case-insensitive. In the Console form matching starts wide — title + tags + the first 500 characters of the description (see matchDescriptions); an API or scheduled input that omits matchDescriptions matches titles only. A job matches if any term matches (OR). A multi-word term needs every word — "python developer" requires both, so prefer short terms like "python" for recall. Empty returns every remote job the boards offer. Capped at 10 terms per run — extras beyond the first 10 are ignored and the run's status message notes it. |
matchDescriptions | boolean | form: true · API: false | The Console form starts in wide mode: terms match the title, the tags and the first 500 characters of the description (exactly what ships in tags and description_snippet, so you can always see why a row matched). Measured 2026-08-15 with ["python"]: 194 rows wide vs 28 title-only — ~7× the recall, at a precision cost: a marketing role whose blurb mentions "our Python stack" matches too. Untick it (or send false) for the title-only precision opt-out. The stored default is false, so an API or scheduled input that omits the field keeps title-only matching and its existing onlyNewJobs memory. |
boards | multi-select | the ten default boards | Which boards to sweep: remoteok, weworkremotely, working_nomads, devitjobs_us, devitjobs_uk, muse, remotive, jobicy, himalayas, hn_hiring (all on by default) — plus the opt-in arbeitnow, which joins only when you list it. See Boards. |
maxItems | integer (1–4000) | 100 | Maximum number of deduplicated jobs to return. Results are interleaved round-robin across boards, so a small cap still samples every board instead of just the biggest feed. |
includeDescription | boolean | false | true fills the description column with the job's full HTML-stripped text (avg ~4,300 chars measured). Off by default to keep rows light — the 500-char description_snippet is always on. |
onlyNewJobs | boolean | false | Deliver only postings this exact search has never delivered before. See Turn it into a job alert. |
postedWithinDays | integer (0–365) | 0 (off) | Drop postings whose posted_at is older than N days — before the cap and before billing. Rows with no date are kept unless strictFilters is on. |
salaryMinAnnual | integer | 0 (off) | Drop postings whose annualized salary_max (salary_min when there is no max) is below N. Currency-agnostic — the figure is compared as the board published it (salary_currency tells you which). Rows with no salary are kept unless strictFilters is on. |
countries | array of strings | [] (off) | Keep postings open to any listed country or region — ISO-2 codes or names (US, United Kingdom, EU, Canada, Germany, India, LATAM, APAC, EMEA, Americas…). Regions match their member countries both ways; empty / worldwide / anywhere / remote locations match everything; a location the actor cannot read (a bare city, as RemoteOK publishes) is kept unless strictFilters is on. |
seniority | multi-select | [] (off) | Keep postings whose derived seniority column is one of entry, junior, mid, senior, lead, staff, principal, manager, director. Unknown seniority is kept unless strictFilters is on. |
excludeKeywords | array of strings | [] (off) | Drop postings whose title names any listed term (case-insensitive; a term of up to 4 letters must stand as a whole word, so go does not exclude "Django"; a multi-word term is one phrase). With matchDescriptions on, the tags and description_snippet are searched too — the same fields searchTerms reads. Runs before the cap and the charge, so an excluded row is never billed; the status message counts what it removed. |
strictFilters | boolean | false | Also drop rows that lack the data an active filter needs (no date / no salary / unreadable location / unknown seniority). See Filters for schedules. |
webhookUrl | string | (none) | Optional alert webhook: when rows are delivered, POST a digest to this URL — Slack incoming webhook or Discord webhook gets a message, any other URL (n8n / Make / Zapier catch hook) gets JSON with counts, console links and the first 20 rows. Quiet runs send nothing. See Alerts below. |
Running with completely empty input {} works and returns up to 100 jobs from all boards.
Output columns
| Column | Notes |
|---|---|
title, company, location | location is exactly what the board provides — often a country restriction like "USA Only" or "Anywhere in the World". |
remote | Always true — non-remote rows (DevITjobs office jobs, onsite HN posts) are filtered out before delivery. |
salary_min, salary_max, salary_currency, salary_interval | Only when the board publishes a figure — 35% of rows on the 2026-08-15 default run (DevITjobs ~100%, Himalayas/Remotive/WWR/HN partial, The Muse and Working Nomads none). Hourly/weekly/monthly figures are annualized (×2080 / ×52 / ×12) so salary_interval normally reads yearly and figures are comparable across boards; a magnitude guard suppresses conversions that could only be wrong (an "hourly" $50,000 stays raw rather than shipping as $104M). salary_source says whether the figure was a structured field (direct_data) or parsed out of the posting text (description). Currency is left null when the board doesn't state it — no guessing. |
url | Direct link to the posting (for Remote OK rows this is the job's Remote OK page, as their API terms require). |
source_board | Which board this (first) copy of the job came from. |
posted_at | Posting date, YYYY-MM-DD, when the board provides one. |
tags | Skills/technologies/categories as the board tags them. |
description_snippet | Always on: the first 500 characters of the HTML-stripped description — filled on 88% of rows (measured; DevITjobs publishes no description text). |
description | Full HTML-stripped description text when includeDescription: true, else null. |
seniority | Derived: entry, junior, mid, senior, lead, staff, principal, manager, director or unknown. Taken from the board's own level field where it publishes one (Jobicy jobLevel, Himalayas seniority, The Muse levels, DevITjobs expLevel, Arbeitnow's Entry / Student job types), otherwise from title keywords (Senior, Sr., Lead, Staff, Principal, Head of, VP…). director also covers VP / Head-of / C-level titles; a bare "Product Manager" is manager, a "Senior Product Manager" is senior. |
job_type | Derived: full-time, part-time, contract, internship, freelance or unknown — from the board's employment-type field (Jobicy, Himalayas, Remotive, DevITjobs, We Work Remotely, the HN first line, Arbeitnow's job types) or from the title. |
company_logo | The employer's logo URL when the board publishes one — Remotive, Jobicy and Himalayas do on most rows, DevITjobs on about a quarter of its remote rows (16 of 63 measured 2026-08-29); Remote OK's logo field was empty on every row measured that day, and the RSS/HTML boards (We Work Remotely, Working Nomads, The Muse, Hacker News, Arbeitnow) publish none, so those rows carry null. Fill rate varies by board (14 of 40 rows on the 2026-08-29 smoke run's default python sweep). Added 2026-08-29, appended after job_type. |
job_id | 16-hex fingerprint of the job's canonical URL (tracking parameters stripped; title + company when a board publishes no URL). job_id is stable across runs and is the same identity onlyNewJobs bills on — use it as the upsert key in Google Sheets / Airtable / your CRM. Added 2026-08-29 after company_logo, so existing exports keep their column order. |
salary_text | Derived, always present: the salary as one readable string built from salary_min / salary_max / salary_currency / salary_interval — $90,000–$175,000 / year, €60,000 / year, $45 / hour; "" when the board published no figure. Nothing is guessed: a board that states no currency gives a bare 111,000–114,000 / year, an unknown currency code is shown as a prefix (CHF 120,000 / year). The numeric columns stay the source of truth. Added 2026-08-29 as the very last column. |
How to read the output
The run's Output tab opens on the Overview view; the other views are one click away in the same tab.
- Overview — the first look: logo, title, company, location, readable salary, posted date, source board, seniority, job type, link. 10 columns, in that order.
- Salary — for pay research:
salary_textnext to the annualizedsalary_min/salary_maxit was built from, the currency, the interval andsalary_source(structured field vs parsed from the posting text). - Monitoring — for
onlyNewJobsschedules and upserts:job_id, title, company,posted_at,scraped_at, source board, link. - All columns — every column of the row, in export order (the order a CSV/XLSX export uses).
Derived columns (always present, never guessed): seniority, job_type, company_logo, job_id, salary_text — see the table above. posted_at is already a YYYY-MM-DD date, so no extra date column is added.
Export just one view: select it in the Output tab, then Export; or from the API, https://api.apify.com/v2/datasets/<datasetId>/items?view=salary&format=csv (the view parameter takes any of overview, salary, monitoring, all). Views only change which columns are shown — the underlying rows and their column names are identical in every export.
Boards
Ten boards are swept by default; one more is opt-in — add its id to boards to include them (that also starts a fresh onlyNewJobs baseline for that search, since the board list is part of the search signature).
| Board id | Default | Source | Depth per run | Notes |
|---|---|---|---|---|
remoteok | yes | remoteok.com/api | ~100 newest rows | Rows link back to the job's Remote OK page, as their API terms require. |
weworkremotely | yes | All-jobs RSS + 13 category RSS feeds | ~460 distinct jobs | No feed paginates; breadth across feeds is the only lever. |
working_nomads | yes | workingnomads.com exposed_jobs API | ~50 curated rows | No salary data. |
devitjobs_us / devitjobs_uk | yes | devitjobs.us / .uk jobsLight API | whole live inventory, remote rows only (~8%) | Structured annual salaries on ~100% of rows; a logo on ~25% of them. devitjobs_uk is discontinued upstream as of 2026-08-29: devitjobs.uk (its front page and its API alike) now redirects to a third-party signup page instead of serving a feed, so the board reads discontinued upstream in the status message and contributes 0 rows until it answers again; devitjobs_us still serves (it moved to devitjobs.com, which the actor follows). |
muse | yes | themuse.com public jobs API, remote filter | up to ~1,980 rows | Fixed category taxonomy; keyword gate is client-side. |
remotive | yes | remotive.com remote-jobs API | one unparameterised GET per run = the whole active feed (~20 rows, measured 2026-08-29) | Not server-side search: your terms are matched client-side across the feed. Salary is free text, parsed conservatively. |
jobicy | yes | jobicy.com API v2 | 200 rows per call (the API's documented maximum), one call per distinct leading keyword of your terms (at most 10; python and python developer share one call); plus one ?geo= call per region (max 3) when countries is set | Structured salary + period when published. |
himalayas | yes | himalayas.app jobs API (cursor-paged) + its search API | cursor crawl of up to 750 pages × 20 rows within a crawl budget of at most 300 s (less on short runs); with searchTerms set, also page 1 of the search endpoint per term (~17-19 rows per search page, measured 2026-08-29; each still passes the same client-side keyword gate as the crawl, so matches per term vary) | Structured salary + currency + period; newest-first, so monitoring runs stop early. Only page 1 of the search is read per term — its robots.txt disallows deeper pages — so depth still comes from the crawl. |
hn_hiring | yes | Newest "Ask HN: Who is hiring?" thread | one thread | Only posts that say "remote" are kept. |
arbeitnow | opt-in | arbeitnow.com job-board API (Germany-centric) | 4 pages × 175 rows, remote rows only (a few % of rows) | No salary field. Job data from Arbeitnow. |
- Not offered: Jobspresso (HTTP 403 to Apify IPs, measured 2026-08-27) — implemented and tested, but a board that fails on every platform run must not be selectable.
Data terms per board
What each board's own API terms ask of anyone re-using its listings. This actor keeps them (every row links to the board's own job page in url, and source_board names the source), and whatever you build on top of it should too:
| Board | Credit / link-back | Re-submission | In their own words (read 2026-08-29) |
|---|---|---|---|
| Remote OK | Link back to the job's Remote OK URL (the url column) and name Remote OK as the source | — | "Please link back (with follow, and without nofollow!) to the URL on Remote OK and mention Remote OK as a source, so we get traffic back from your site." (legal notice in the API feed) |
| Remotive | Link back to the Remotive URL and mention Remotive as the source | Do not resubmit to third-party job sites | "Please link back to the URL found on Remotive AND mention Remotive as a source" · "Please do not submit Remotive jobs to third Party websites, including but not limited to: Jooble, Neuvoo, Google Jobs, LinkedIn Jobs." · "we advise max. 4 times a day" (API README) · Note (read 2026-08-29): remotive.com/robots.txt also lists Disallow: /api/* for all user agents while the same API README explicitly grants /api/remote-jobs; this build treats the API README as the governing grant and makes exactly one unparameterised request to that path per run. |
| Jobicy | Keep Jobicy as the source and preserve the canonical Jobicy job URL | Do not present listings as your own postings | "Keep Jobicy as the original source and preserve the canonical Jobicy job URL when displaying a listing." · "Do not present Jobicy listings as your own original job postings or remove source attribution." · "do not run automated polling more frequently than once per hour" (Fair Use) |
| Himalayas | Link back to the job's himalayas.app URL (the url column) and credit Himalayas | Do not resubmit to third-party aggregators | "Link back to the job's URL on himalayas.app" · "Credit Himalayas as the source of the data wherever you display it." · "Do not resubmit Himalayas listings to third-party job aggregators, including but not limited to Jooble, Neuvoo, Google Jobs, and LinkedIn Jobs." (API README) |
Run it from your code
JavaScript (apify-client):
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });const run = await client.actor('flash_scraper/remote-job-aggregator').call({searchTerms: ['python', 'data engineer'],maxItems: 200,});const { items } = await client.dataset(run.defaultDatasetId).listItems();console.log(`${items.length} remote jobs`, items[0]);
Python (apify-client):
from apify_client import ApifyClientclient = ApifyClient("YOUR_APIFY_TOKEN")run = client.actor("flash_scraper/remote-job-aggregator").call(run_input={"searchTerms": ["python", "data engineer"],"maxItems": 200,})items = client.dataset(run["defaultDatasetId"]).list_items().itemsprint(len(items), "remote jobs")
curl (run synchronously and get the dataset back):
curl -sX POST \"https://api.apify.com/v2/acts/flash_scraper~remote-job-aggregator/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \-H "Content-Type: application/json" \-d '{"searchTerms": ["python"], "maxItems": 100}'
Use it from an AI agent (MCP)
The Apify MCP server at https://mcp.apify.com exposes Store actors to any MCP client as tools; an actor's tool is named after its slug with the / spelled -slash- (the docs' own example: apify/rag-web-browser becomes apify-slash-rag-web-browser), so this one is flash_scraper-slash-remote-job-aggregator. Load just this actor with https://mcp.apify.com?tools=flash_scraper/remote-job-aggregator, or let the agent find it through the server's search-actors and call-actor tools; full results come back through get-actor-output when the preview is cut.
A minimal input an agent can send — the four single-request boards only, so the sweep is seconds, not a crawl (20 rows in 2 s on a local run 2026-08-29; the same terms across all ten boards took 84 s that day, because Himalayas and The Muse are paginated feeds) — and at most $0.04 (20 rows × $0.002 + the $0.00005 run start; $0.06 after the scheduled 2026-09-14 change to $0.003 per job):
{ "searchTerms": ["python"], "maxItems": 20, "boards": ["remoteok", "jobicy", "remotive", "working_nomads"] }
Per-result price, read from the actor's live pricing record on 2026-08-29 and re-read 2026-09-05: $0.002 per delivered job on Apify's free plan (Store discounts on paid plans: $0.0018 Bronze down to $0.0014 Diamond), plus $0.00005 per run start, rising to $0.003 on the free plan from 2026-09-14 under a scheduled pricing record; the Pricing tab is always authoritative. An agent without an Apify account can still pay per call: Apify's experimental x402 agentic payments buy a prepaid API token with USDC on Base — "no Apify account needed", in the docs' words — and that token runs this actor through the same REST endpoints shown above.
Turn it into a job alert (onlyNewJobs)
Set onlyNewJobs: true and the actor becomes a monitor: every run delivers — and bills — only postings this exact search (same searchTerms + boards + filters) has never delivered before.
How it behaves, run by run:
- First run = baseline. Delivers everything currently matching (up to
maxItems; anything beyond it stays eligible for the next runs, see the notes below) and the status message says the baseline was saved. - Every later run delivers only new postings. If nothing is new, the run finishes SUCCEEDED with
Nothing new since your last run (<time> UTC): … You were not charged.— zero rows, zero cost. That message is the monitor working, not an error. - Memory lives in a named key-value store in your own Apify account (
remote-job-monitor), one record per search signature; entries are pruned after 90 days, so a job reposted after a long silence counts as new again.
Set up the alert in 2 minutes (Apify Console):
- Open this actor → Input: set your
searchTerms, tick Only new jobs, and setmaxItemsgenerously (e.g. 500–1000) — withonlyNewJobson, the sweep reads each board to full feed depth so nothing hides behind the cap, and you still only pay for what's delivered. - Run it once manually — that's your baseline.
- Schedules → Create new → pick this actor → cron like
0 8 * * *(daily 08:00). The schedule reuses your saved input. - Add a notification: Integrations on the actor → webhook on
Run succeededto Slack/email/n8n — or read the dataset from the run; on a nothing-new day the dataset is simply empty and you paid nothing.
Two honest notes: if a run hits maxItems, the matching postings it could not deliver stay eligible — the memory records their identities and the date of the oldest one, and the next runs read the boards back to that day (at most 30 days back), delivering those postings alongside new ones, up to maxItems per run and never the same row twice within the 90-day memory. Only the postings that run matched and left behind are owed this way: a posting that only becomes reachable later with a date before your last run (a board that failed that run, a feed that is not newest-first) is held back as back catalogue, not billed as new. While an overflow is owed, the newest-first boards (Himalayas, Hacker News) no longer stop paginating at your last run and are read back to that date, so those runs are slower — same price. If a board fails or is cut short on a later run, the owed postings it could not show that run stay owed (each until 30 days after its posting date) instead of dropping out of the record; when every board answers and a board no longer lists an owed posting, it is gone and the record shrinks to what is still listed. The status message says how many are still waiting (N matching posting(s) beyond maxItems stay eligible for your next run), so a generous maxItems on the first run (e.g. 500–1000) clears the backlog in one go; later runs deliver only what is new. And changing searchTerms, boards or any filter below starts a fresh signature with its own baseline.
Filters for schedules
Five optional filters run after the boards answer and after onlyNewJobs, and before the cap and the charge — a row a filter removes is never billed. All are off by default, so an existing scheduled input behaves exactly as before.
excludeKeywords: ["senior", "lead"]— drop postings whose title names any listed term; withmatchDescriptionson, tags and the description snippet count too. An excluded row is never memorised as delivered, so dropping the exclusion later (a new signature) makes it eligible again.postedWithinDays: 7— drop postings older than a week (byposted_at).salaryMinAnnual: 80000— drop postings whose annualizedsalary_max(orsalary_min) is below the floor; currency-agnostic, the number is compared as published.countries: ["US", "Europe"]— keep postings open to any listed country or region (ISO-2 codes or names;Worldwide/Anywhere/Remotelocations match everything).seniority: ["senior", "lead"]— keep postings whose derivedsenioritycolumn is in the list.strictFilters: true— also drop rows that lack the data a filter needs (no date, no salary, an unreadable location, unknown seniority); off, such rows are kept and the status message counts them.excludeKeywordsnever lacks data, so strict mode adds nothing to it.
{ "searchTerms": ["python"], "onlyNewJobs": true, "maxItems": 500, "postedWithinDays": 7, "salaryMinAnnual": 80000, "countries": ["US", "Canada"], "seniority": ["senior", "lead"] }
The status message says what each filter removed (Filters removed 12 row(s) before billing (…)) and how many rows were kept for lack of data. Filters are part of the onlyNewJobs signature, so set them before a schedule's first (baseline) run. Filters apply after the boards answer, so with any filter set the sweep reads each board to full feed depth (as onlyNewJobs does) rather than stopping at maxItems per board — a tight salary floor or country list does not under-fill your order while qualifying rows sit deeper in the feeds; you still pay only for what is delivered.
Schedule it (n8n, Make, Zapier)
A remote job aggregator earns its keep on a schedule — a daily sweep feeding a Slack channel, an Airtable base, or a job-alert newsletter:
- n8n: use the Apify node (or an HTTP Request node hitting the
run-sync-get-dataset-itemsendpoint above) on a Cron trigger, then map the JSON rows to Slack/Notion/Google Sheets nodes. - Make (Integromat): the Apify app has a "Run an Actor" module — pair it with a scheduled scenario and an Iterator over the dataset items.
- Zapier: the Apify integration offers "Actor Run Finished" as a trigger — schedule runs natively in the Apify Console (Schedules → new schedule → pick this Actor) and let Zapier fan the fresh dataset out.
- Apify Console alone also works: Schedules can run this Actor daily and email you or push to a webhook when a run finishes.
Tip for monitoring pipelines: turn on onlyNewJobs (section above) so the scheduled runs deliver only the delta — no downstream dedup needed and quiet days cost nothing.
Fair use the boards ask for — schedule accordingly. Jobicy asks for at most one automated poll an hour; Remotive advises at most 4 requests a day and blocks more than 2 a minute; Himalayas refreshes its data every 24 hours and answers 429 beyond an unpublished rate limit. Each run makes one Remotive request, one Jobicy request per distinct leading keyword of your terms (at most 10; one with no terms; plus at most 3 region calls when countries is set) and one Himalayas search page per term before its crawl — so run this actor at most hourly; daily is what the boards' own refresh cycles justify, and two overlapping schedules against the same boards count double.
Alerts: get a Slack / Discord / webhook message when new rows land
Set webhookUrl and every run that delivers at least one row POSTs a digest there — no n8n/Make scenario needed just to see the new jobs. Quiet runs send nothing, so pair it with onlyNewJobs on a schedule and the actor is a job alert on its own.
- Slack incoming webhook (
hooks.slack.com/…) or Discord webhook (discord.com/api/webhooks/…) → a message:Remote Jobs: N new row(s), one line per job (Title — Company (board) url, first 20) and the dataset link. - Anything else (n8n / Make / Zapier catch hook, your own endpoint) → JSON
{actor, delivered, run_url, dataset_url, rows[:20], text}. - Best-effort: a webhook that fails or answers non-2xx is named in the run's status message and never fails the run — you still get your rows and the dataset.
{ "searchTerms": ["python", "backend"], "onlyNewJobs": true, "maxItems": 500, "webhookUrl": "https://hooks.slack.com/services/T000/B000/XXXX" }
Every run comes with a report
Every run that delivers at least one row also writes a one-page HTML report to its key-value store under the key REPORT: the headline count, the search that produced it, stat tiles (rows delivered, boards contributing, cross-board duplicates merged, share with a salary), bar charts (jobs per board, top companies, seniority split), the first 100 rows and the same honest notes the status message carries.
Find it in the run's Output tab under REPORT, or follow the Report: … link near the end of the run's status message (just before the review ask). Its table shows the same columns as the Overview view (minus the logo), under the same headers.
It is a single self-contained HTML file (no scripts, no external assets) — safe to forward or screenshot as-is.
The dataset stays the source of truth: the report shows at most 100 rows and links back to the full dataset and the run.
Every run — including a zero-row one — also writes a machine-readable receipt under the key RUN_SUMMARY (JSON), for API and scheduled callers who never read a status line: status_message, search_terms, boards, max_items, only_new, offered (candidates after the cross-board merge), delivered and charged (always the same number), filters and filter_removed (rows your filters dropped before the cap and the charge, split per filter in filter_removed_by: exclude, posted, salary, country, seniority), filter_kept_missing_data, the onlyNewJobs counters (already_delivered_left_out, held_back_older_than_last_run, held_back_undated, owed_to_next_run), board_errors, report_url and webhook_note. Read it with GET https://api.apify.com/v2/key-value-stores/{defaultKeyValueStoreId}/records/RUN_SUMMARY.
Honest limits
- Feed-limited depth. These are public feeds, not archives: RemoteOK serves its ~100 newest rows, WeWorkRemotely ~100 per feed, Working Nomads ~50 curated rows, Jobicy 200 per call (plus a region call per
countriesentry, max 3). The Muse and Himalayas DO paginate deeply - measured 2026-08-19 at ~1,980 and ~2,000 rows respectively - so a large order draws mostly from those two. A run samples the current face of each board; it cannot paginate years back. - Keyword matching is client-side, on data you can see. The Console form starts wide (
matchDescriptions: true): a term needs to appear in the title, the tags or the first 500 chars of the description — ~7× the recall of title-only (measured), and every hit is explainable from the deliveredtags/description_snippet. Title-only is the precision opt-out (matchDescriptions: false, and what an API or scheduled input gets when it omits the field): a Python job titled "Backend Engineer" then won't match"python". Either way you are never charged for a fuzzy server-side match that isn't really your keyword. - Salary coverage varies by board — 35% of rows overall on the measured default run. DevITjobs publishes structured annual salaries on ~100% of its rows; Himalayas, Remotive, WWR and HN partially; RemoteOK ~3%; The Muse and Working Nomads none. Figures parsed out of free text (
salary_source: "description") are conservative best-effort, and annualization only multiplies figures whose interval the board or text actually states. - DevITjobs contributes remote rows only (~8% of its inventory, measured) — it's a city-based board, and shipping its office jobs with
remote: truewould be a lie. Its.ukfeed is currently gone: since at least 2026-08-29 devitjobs.uk redirects every request (front page and/api/jobsLight) to a third-party signup page, sodevitjobs_ukreadsdiscontinued upstreamon every run and delivers nothing — the other nine boards are unaffected, and you are never charged for a board that delivers nothing. Failures — including a board answering with an error object or a redirect instead of its feed — are reported per board, never silently. - HN "Who is hiring?" rows are parsed from community-formatted posts (
Company | Role | Location | Salary). Parsing degrades gracefully, but expect the occasional imperfect company/title split (a post with no role segment ships its whole first line as the title). Only posts that explicitly say "remote" are included, and candidates' "want to be hired" templates (Location: … Remote: … Willing to relocate: …) posted into the thread are skipped — they are people, not jobs. - Boards change. Undocumented internal APIs (DevITjobs in particular) can change shape without notice. A board that fails is named in the run's status message and log; its failure never poisons the other nine.
- The opt-in board is opt-in for a reason. Arbeitnow is a German city board: only the rows it flags remote are delivered (34 remote among its first 450 rows, measured 2026-08-25), so it adds a handful of mostly Germany / EU rows per run, and the actor reads at most 4 pages of its API per run, per its "please do not abuse" terms. It publishes no salary field, so its rows ship no salary.
- Attribution: job rows sourced from Remote OK link back to the job's Remote OK page in the
urlcolumn, as their API terms require. Rows from the opt-in Arbeitnow board come from the Arbeitnow job board API, whose terms ask for a link back. - The status message names every board, delivered/offered. It reads like
remoteok 12/100 · working_nomads 0/50 · devitjobs_uk discontinued upstream · muse failed: rows delivered from that board over the remote rows it offered before your keyword gate,skippedfor a board cut off by the run's time budget,discontinued upstreamfor a board whose site is gone (devitjobs.uk today),failedfor one that answered with an error (details in the log).0/50means the board answered and nothing matched — not that it was skipped. The same line is in the run report's notes. - Zero matches cost nothing. A run that delivers no rows tells you why (boards empty vs. your filters) and does not charge you.
FAQ
Is this a RemoteOK / Jobicy / Himalayas jobs API? Effectively, yes — it calls those boards' own public, keyless endpoints (plus seven more default sources and one opt-in) and normalizes everything into one schema, so instead of integrating a dozen feeds with a dozen shapes you integrate one. Remote OK rows keep their remoteok.com link in url, as its API terms require.
Can I use it instead of a remote-jobs RSS feed? That is exactly the shape it replaces. We Work Remotely arrives via its all-jobs and category RSS feeds; the other boards via their keyless JSON endpoints — all merged, deduplicated and salary-annualized into one JSON/CSV feed you can poll on a schedule.
How do I get alerts for new remote jobs only? Set onlyNewJobs: true and schedule the run — every run then delivers (and bills) only postings this exact search has never delivered before; a nothing-new day bills only the $0.00005 run start. Full walkthrough in Turn it into a job alert.
What does it cost per 1,000 jobs? $2 — the rate is $0.002 per deduplicated row plus $0.00005 per run start, rising to $0.003 per row on 2026-09-14 under a scheduled pricing record, and the live price is always on this page's Pricing tab. The free Apify plan's monthly credit is enough to try a real sweep.
How do I get only jobs with a published salary? Set salaryMinAnnual: 1 and strictFilters: true. The floor is then active, and strict mode also drops the rows that carry no usable annual figure — so only rows with an annualized salary_min / salary_max survive, and they are the only rows billed. (A raw hourly/monthly figure the sanity guard refused to annualize counts as no usable salary.)
Exclude senior roles? Set excludeKeywords: ["senior", "lead", "staff", "principal"] — any posting naming one of those in its title is dropped before the cap and before billing, and the status message says how many (1 naming excludeKeywords [senior, …]). Turn matchDescriptions on to exclude on tags and the description snippet as well. The opposite (keep only senior rows) is the seniority filter.
Do I need an API key, proxy or login? No. All eleven sources (ten by default + one opt-in) are public, keyless endpoints, and runs go direct with no proxy cost.
Does it cover LinkedIn, Indeed or Glassdoor? No — those aren't remote-only boards, and covering them well takes different machinery. That's the sibling actor below.
Is there a free way to scrape remote job boards?
Yes. Apify's free plan needs no card, and its monthly usage credit ($5 at the time of writing) covers about 2,499 deduplicated jobs from this actor. All ten default boards are public, keyless endpoints, so there is no extra API or proxy cost.
At the $0.002 rate read from the live pricing record on 2026-08-29 and re-read 2026-09-05, 2,499 jobs × $0.002 plus one $0.00005 run start comes to $4.998 — just inside the free plan's $5 monthly credit (apify.com/pricing, read 2026-09-05) — and the completely empty input returns up to 100 of the current remote jobs across every board. A change to $0.003 per result is scheduled for 2026-09-14 (notified 2026-08-30); the Pricing tab is authoritative.
How do I get remote job listings as a CSV or Excel file?
Run the actor, then click Export in the run's Output tab — CSV, XLSX and JSON are available. From the API, add format=csv to the dataset items endpoint; the view parameter (overview, salary, monitoring, all) selects which columns ship.
Column order is stable for exports: job_id, company_logo and salary_text were appended on 2026-08-29 after the existing columns so a CSV import keeps its positions, and job_id — a 16-hex fingerprint of the canonical URL — is the same identity onlyNewJobs bills on, so use it as the upsert key in Google Sheets, Airtable or your CRM.
How many remote jobs can one run return?
Up to 4,000 deduplicated jobs per run (maxItems, default 100). Depth is feed-limited: The Muse and Himalayas paginate to roughly 2,000 rows each, while the smaller feeds serve their newest ~20–460 rows.
Those depths were measured 2026-08-19 (The Muse about 1,980 rows, Himalayas about 2,000); RemoteOK serves its ~100 newest rows, We Work Remotely ~460 distinct jobs across its 14 RSS feeds, Working Nomads ~50 and Jobicy 200 per call, so a large order draws mostly from The Muse and Himalayas.
Does it include salary data?
Yes, where a board publishes one — 35% of rows on the measured default run, and ~100% of DevITjobs rows. Hourly, weekly and monthly figures are annualized (×2080 / ×52 / ×12) so salary_min/salary_max are comparable across boards.
That 35% is the 2026-08-15 default run of 100 deduplicated jobs; by board, DevITjobs publishes structured annual salaries on about 100% of rows, RemoteOK about 3%, The Muse and Working Nomads none, and salary_source says whether a figure was a structured field (direct_data) or parsed from the posting text (description).
How often should I schedule it?
At most hourly — daily is what the boards' own refresh cycles justify. Jobicy asks for at most one automated poll per hour and Remotive advises at most 4 requests a day; the actor keeps each board's fair-use terms within a run.
Those limits are the boards' own words, read 2026-08-29: Jobicy's fair-use terms say do not run automated polling more frequently than once per hour, Remotive advises at most 4 times a day, and Himalayas refreshes its data every 24 hours — so a daily cron such as 0 8 * * * is the cadence the sources justify.
Can an AI agent use it?
Yes. The Apify MCP server at https://mcp.apify.com exposes it as the tool flash_scraper-slash-remote-job-aggregator, and Apify's experimental x402 payments let an agent without an Apify account pay per call with USDC.
The agent-sized call — searchTerms python, maxItems 20 and the four single-request boards remoteok, jobicy, remotive and working_nomads — returned 20 rows in 2 seconds on a local run on 2026-08-29 (84 seconds across all ten boards that day, because Himalayas and The Muse are paginated feeds) for at most $0.04 ($0.06 from 2026-09-14, when a scheduled record takes the rate to $0.003 per job).
Is there a remote jobs API that does not need an API key?
Yes. Remote Jobs Aggregator calls the public, keyless endpoints of 10 remote boards — RemoteOK, We Work Remotely, Working Nomads, DevITjobs US and UK, The Muse, Remotive, Jobicy, Himalayas and Hacker News — and returns one normalized, deduplicated JSON, CSV or XLSX feed through the run-sync-get-dataset-items endpoint, with no proxy and no login. The 2026-08-15 default run delivered 100 deduplicated jobs from 9 boards; the free-plan price read on 2026-08-29 and re-read 2026-09-05 is $0.002 per job, rising to $0.003 per job on 2026-09-14 under a scheduled pricing record — the Pricing tab is authoritative.
Does it cover LinkedIn, Indeed or Glassdoor remote jobs?
No. Those are not remote-only boards and covering them well takes different machinery; this Actor sweeps the ten keyless remote boards at $0.002 per deduplicated job (live pricing read 2026-08-29 and re-read 2026-09-05; $0.003 from 2026-09-14). The sibling Multi Job Board Scraper adds LinkedIn, Indeed and Glassdoor, merged and deduplicated across 12 selectable working boards — 11 of them delivering rows today — with the same only-new-jobs monitoring.
How do I get an alert for new remote jobs only?
Set onlyNewJobs true, tick a generous maxItems (500–1,000) for the baseline run, then schedule it daily and add a Slack, Discord or webhook URL in webhookUrl. Every later run delivers and bills only postings this exact search has never delivered before; a nothing-new day delivers 0 rows and bills only the $0.00005 run start. Memory lives in your own account for 90 days, keyed on job_id — the same identity added to every row on 2026-08-29.
Which remote job boards can be scraped without an API key today?
Ten by default — RemoteOK (~100 newest rows), We Work Remotely (~460 distinct jobs across 14 RSS feeds), Working Nomads (~50), DevITjobs US, The Muse (up to ~1,980 rows), Remotive (~20 per feed read on 2026-08-29), Jobicy (200 per call), Himalayas (~2,000) and Hacker News — plus opt-in Arbeitnow. devitjobs.uk has been discontinued upstream since 2026-08-29, and Jobspresso is not offered because it answered HTTP 403 to Apify IPs on 2026-08-27.
Can I republish the remote jobs on my own job board?
Not from every board: the boards' own API terms, read 2026-08-29, pass through to you. Remotive says do not submit its jobs to third-party sites such as Jooble, Google Jobs or LinkedIn Jobs; Himalayas says the same for aggregators; Jobicy asks you to keep it as the source and preserve the canonical job URL; Remote OK asks for a dofollow link back naming Remote OK. Every row already links to the board's own posting in url.
Related actors
- LinkedIn Jobs Scraper — keyword + location search over LinkedIn's public job listings.
Need LinkedIn, Indeed and Glassdoor too - merged and deduplicated across 12 boards with only-new-jobs monitoring? -> Multi Job Board Scraper
Related Flash Scrape actors
- Multi Job Board Scraper — LinkedIn, Indeed, Glassdoor + 9 more boards, deduplicated, with only-new job alerts
- Local Business Leads Scraper — any category, any city, verified emails, lead scores
- Creator Leads Scraper — TikTok, Instagram and YouTube creator emails in one run
- LinkedIn Jobs Scraper — listings by title and location, no login, CSV/JSON
- All Flash Scrape actors — same house rules everywhere: pay per delivered row, honest status messages, only-new monitoring, webhook alerts, and a run report on every run.
If this run saved you time, a Store review helps others find it.