# Remote Jobs with Yearly USD Salary: 6 Boards Deduped (`transparent_meteorite/remote-jobs-aggregator`) Actor

Remote jobs from 6 official job-board APIs (Remotive, Remote OK, We Work Remotely, Himalayas, Jobicy, Arbeitnow), deduped, with listed salaries converted to yearly USD so they compare. Category, seniority, region and timezone filters; monitor mode charges only new jobs. No login.

- **URL**: https://apify.com/transparent_meteorite/remote-jobs-aggregator.md
- **Developed by:** [Detroit Web Agent - Open Data](https://apify.com/transparent_meteorite) (community)
- **Categories:** Jobs, Lead generation, Automation
- **Features:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 90.5% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.90 / 1,000 job listings

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Remote Jobs Aggregator: 6 Remote Job Boards in One Clean, Deduped Dataset

**Remote jobs** from Remotive, Remote OK, We Work Remotely, Himalayas, Jobicy and Arbeitnow in one deduped dataset: the same job posted on several boards becomes one row, salaries are parsed into numbers, and every board shares the same categories, job types, seniority levels and regions.

**This Actor vs memo23/remote-jobs-aggregator:** that one reads 7 boards, including Relomote and a full-depth Remote OK crawl, and adds opt-in employer emails. Choose this one for a minimum-salary filter in 7 currencies, region and UTC timezone filters, and a monitor mode that returns, and charges for, only jobs you have not seen before.

![Remote Jobs Aggregator: features, price and a real output row](https://api.apify.com/v2/key-value-stores/9BAn0msnxToXlD9TO/records/sf-remote-jobs-aggregator-banner-10140227.png)

[![Run it free on Apify](https://api.apify.com/v2/key-value-stores/9BAn0msnxToXlD9TO/records/sf-cta.png)](https://console.apify.com/actors/IBxCdrQKiNtBPYA19/input)

| Price per 1,000 jobs | Free plan | Starter | Scale | Business |
|---|---|---|---|---|
| **This Actor** | **$1.50** | **$1.20** | **$1.00** | **$0.90** |

<sub>Pay per event, no monthly rental. Prices read from each Actor's public Apify pricing on 2026-10-13; check the Pricing tab for the current figure.</sub>

![Remote Jobs Aggregator sample output table from a real run](https://api.apify.com/v2/key-value-stores/9BAn0msnxToXlD9TO/records/sf-remote-jobs-aggregator-output-10140227.png)

Get remote jobs from Remotive, Remote OK, We Work Remotely, Himalayas, Jobicy and Arbeitnow in one run. The same job posted on several boards becomes one row. Salaries are parsed into numbers, and every board uses the same categories, job types, seniority levels and regions. Monitor mode only returns, and only charges for, jobs you have not seen before.

- **In short:** Remote Jobs Aggregator (Apify actor `transparent_meteorite/remote-jobs-aggregator`) reads the official public APIs and feeds of 6 remote job boards. It returns one flat, typed row per unique remote job and keeps a link back to the original listing on every row.
- **Who it is for:**
  - remote job seekers and job alert newsletters
  - remote job boards and aggregators that want a clean feed
  - recruiters and sourcing teams
  - salary and labour market researchers
  - sales teams looking for companies that are hiring
  - AI agents that need fresh remote jobs as a tool
- **Input:**
  - keywords (any or all), exclude keywords, title-only matching
  - which boards to read
  - categories, job types and experience levels
  - minimum yearly salary in 7 currencies, "only jobs with a salary"
  - candidate regions, UTC timezones, posted within N days
  - company include and exclude lists
  - sort order, max jobs, description format (text, HTML, Markdown)
  - monitor mode for new jobs only
- **Output:** 37 fields per job, including:
  - title, company, logo and company URL
  - board, listing URL and apply URL
  - posted date, normalised category, tags, job type, experience level
  - parsed salary (min, max, currency, period), salary converted to yearly and to yearly USD, the salary text as posted
  - location restriction, regions, timezones
  - description as text, HTML or Markdown
  - dedupKey, alsoListedOn (the same job on other boards), attribution line
- **Pricing:** pay per event. $1.50 per 1,000 jobs on the Free plan, and less on paid Apify plans (down to $0.90 per 1,000 on Gold, Platinum and Diamond). There is also a start event of $0.00005. Duplicates and already-seen jobs are never charged.
- **Limits:** you get only what the boards publish in their public APIs and feeds. Each board shows its newest jobs, not its whole archive. The actor never calls a board more often than that board allows. Results are cached per Apify account, so two runs a few minutes apart return the same jobs.

**Key facts**

- Actor name: Remote Jobs Aggregator
- Data sources: the official public APIs/feeds of Remotive, Remote OK, We Work Remotely, Himalayas, Jobicy and Arbeitnow
- Output formats: JSON, CSV, Excel, XML, HTML table, RSS (Apify dataset)
- Access: Apify Console, REST API, JavaScript/Python clients, schedules, webhooks, Apify MCP server
- Login, proxy or third-party API key needed: no, only an Apify account
- Also known as: remote job scraper, remote jobs API, remote job board aggregator, Remotive API alternative, Remote OK scraper, We Work Remotely scraper, Himalayas jobs API

### Run in 30 seconds

1. Open [Remote Jobs Aggregator](https://apify.com/transparent_meteorite/remote-jobs-aggregator) and press **Try for free** (you need a free Apify account).
2. Keep the prefilled input, or clear it to get the newest remote jobs from all 6 boards, then press **Start**. A run usually takes under a minute.
3. Open the **Output** tab. Switch to the Table view, or download the results as CSV, Excel or JSON.

Prefilled input:

```json
{
  "keywords": ["python"],
  "maxItems": 50
}
```

To get the newest 200 remote jobs from every board, use an empty input: `{}`.

### Why choose this over alternatives

- **Six boards and one schema.** Every board names its fields differently. Here every row has the same fields, and those fields use the same fixed lists:
  - 17 categories
  - 6 job types
  - 6 experience levels
  - 11 regions
- **Real dedupe across boards.** Many companies post the same job on Remote OK, We Work Remotely and Jobicy. Each such job becomes one row, and the other listings go to `alsoListedOn`. You pay for the job once.
- **Salary as numbers you can filter.** The actor parses salaries from three places, in this order:

  - the board's structured salary fields
  - the board's salary text (for example "$35,3k - $52k" or "$50/hour")
  - the description itself, as a last resort ("base salary of $140,000 - $180,000 USD per year")

  Every salary is turned into a yearly amount and a yearly amount in USD. `minSalary` compares like with like, so a $60/hour contract and a $120k salary sit on the same scale.
- **More filters than other remote-job actors:**
  - exclude keywords and title-only matching
  - normalised categories and experience levels
  - minimum salary in 7 currencies, and "only jobs with a salary"
  - candidate regions, with or without worldwide jobs
  - UTC timezones
  - company allow and block lists
  - a choice of three sort orders
- **Monitor mode that saves money.** Schedule the actor every few hours. You get, and pay for, only jobs you have not received before.
- **Polite and durable.** The actor uses only the official public APIs and RSS feeds, with no scraping of HTML pages and no proxies. It respects each board's stated call limits through a cache shared by your runs. Every row keeps the attribution link that each board asks for.

### What it does and who it is for

Remote Jobs Aggregator collects remote job listings from six job boards that offer a public API or feed. It then normalises them into one table. For each board it reads the endpoint the board publishes for developers:

- the Remotive remote-jobs API
- the Remote OK JSON API
- the We Work Remotely RSS feed
- the Himalayas jobs API (plus its search endpoint for keyword runs)
- the Jobicy v2 API
- the Arbeitnow job-board API, keeping only its remote jobs

It then does six things:

1. **Cleans up each job.** It decodes HTML, repairs broken characters, and converts every date to ISO 8601 UTC.
2. **Sorts each job into fixed lists.** Each job gets a category, a job type and an experience level from fixed lists. Where the board gives a value, the actor uses it. Otherwise it works one out from the title.
3. **Parses the salary**, as described above.
4. **Reads where candidates can live.** It turns the board's location restriction ("USA only", "Europe", "Anywhere in the world") into region codes, and reads timezones where the board publishes them.
5. **Merges duplicates.** It treats two listings as the same job when the company name and the job title match after normalisation. The most complete listing becomes the row, and the others are listed under `alsoListedOn`.
6. **Applies your filters, sorts the jobs and stops at `maxItems`.**

Typical uses:

- **Job alert emails and Slack/Discord bots:** run on a schedule in monitor mode and post only new jobs.
- **Niche job boards** ("remote Python jobs", "remote design jobs in Europe"): keep an up-to-date, deduped feed with attribution links.
- **Salary research:** collect remote jobs that publish a salary and compare the yearly USD figures by category or seniority.
- **Recruiting and sales prospecting:** spot which companies are hiring remote engineers right now, with company links for your CRM.
- **AI agents:** give an assistant a "find remote jobs" tool through the Apify MCP server.

### Quick start

- **Everything new, all boards:** `{}` returns the newest 200 jobs. Boards are mixed in turns, so every board is represented.
- **Remote Python jobs paying $100k or more:** `{"keywords": ["python"], "minSalary": 100000, "hasSalaryOnly": true}`
- **Remote design jobs open to Europe, from the last 3 days:** `{"categories": ["design"], "regions": ["europe"], "postedWithinDays": 3}`
- **A daily feed of new senior engineering jobs:** `{"categories": ["software-development"], "experienceLevels": ["senior", "lead"], "monitorMode": true}`, scheduled daily.

### What you get: output fields

| Field | Type | What it is |
|---|---|---|
| `id` | string | Board id plus the board's own job id, for example `remoteok:1131313`. |
| `title` | string | Job title, cleaned. |
| `company` | string | Company name. |
| `companyLogo` | string or null | Logo image URL as published by the board. |
| `companyUrl` | string or null | Company website or company page on the board. |
| `source` | string | Board id: `remotive`, `remoteok`, `weworkremotely`, `himalayas`, `jobicy`, `arbeitnow`. |
| `sourceName` | string | Board name, for example "We Work Remotely". |
| `sourceUrl` | string | The listing on the board. Link back to this; each board asks for it. |
| `applyUrl` | string | Where to apply (the board's apply link or the listing). |
| `postedAt` | string | Publish time, ISO 8601 UTC. |
| `expiresAt` | string or null | Expiry time, where the board publishes it. |
| `category` | string | Normalised category: software-development, design, marketing, sales, customer-support, product, data, devops, qa, finance, hr, writing, management, legal, education, healthcare, other. |
| `sourceCategory` | string or null | The board's own category label. |
| `tags` | array | The board's tags and skills. |
| `jobType` | string or null | full-time, part-time, contract, freelance, internship, temporary. |
| `experienceLevel` | string or null | internship, entry, mid, senior, lead, executive. |
| `salaryMin`, `salaryMax` | number or null | Salary range in the posted currency and period. |
| `salaryCurrency` | string or null | ISO currency code, for example USD, EUR, GBP, CAD. |
| `salaryPeriod` | string or null | hour, day, week, month or year. |
| `salaryText` | string or null | The salary as posted. |
| `salaryYearlyMin`, `salaryYearlyMax` | number or null | The range converted to a yearly amount (hourly × 2,080, monthly × 12, and so on). |
| `salaryYearlyMaxUsd` | number or null | Yearly top of the range in USD, using fixed approximate exchange rates. Used by `minSalary` and the salary sort. |
| `salarySource` | string or null | Where the salary came from: `structured`, `text` or `description`. |
| `locationRestriction` | string or null | Where candidates may live, as posted ("USA", "Europe", "Worldwide"). |
| `regions` | array | Normalised regions: worldwide, usa, canada, north-america, latam, europe, uk, apac, oceania, africa, middle-east. |
| `timezones` | array | UTC offsets the job asks for, for example `["UTC-5", "UTC+1"]`. |
| `isRemote` | boolean | Always true. Non-remote Arbeitnow jobs are dropped. |
| `descriptionText` | string or null | Description as plain text (default format). |
| `descriptionHtml` | string or null | Description as HTML (format `html` or `all`). |
| `descriptionMarkdown` | string or null | Description as Markdown (format `markdown` or `all`). |
| `dedupKey` | string | Normalised `company|title` key. The same job on several boards shares it. |
| `alsoListedOn` | array | The same job on other boards: `{source, sourceName, sourceUrl, postedAt}`. |
| `sourcesCount` | number | How many boards list this job. |
| `attribution` | string | Ready-made credit line, for example "Job listed on Jobicy: https://jobicy.com/jobs/...". |
| `scrapedAt` | string | When this run produced the row. |

### Sample output

```json
{
  "id": "jobicy:153107",
  "title": "Senior Manager, Corporate Sales - West",
  "company": "Webflow",
  "companyUrl": "https://jobicy.com/company/webflow",
  "source": "jobicy",
  "sourceName": "Jobicy",
  "sourceUrl": "https://jobicy.com/jobs/153107-senior-manager-corporate-sales-west",
  "applyUrl": "https://jobicy.com/jobs/153107-senior-manager-corporate-sales-west",
  "postedAt": "2026-10-13T02:10:22.000Z",
  "category": "sales",
  "tags": ["Sales"],
  "jobType": "full-time",
  "experienceLevel": "executive",
  "salaryMin": 255000,
  "salaryMax": 350000,
  "salaryCurrency": "USD",
  "salaryPeriod": "year",
  "salaryYearlyMaxUsd": 350000,
  "salarySource": "structured",
  "locationRestriction": "Canada, USA",
  "regions": ["usa", "canada", "north-america"],
  "timezones": [],
  "descriptionText": "Webflow is the agentic web marketing platform for modern marketing teams...",
  "dedupKey": "webflow|senior manager corporate sales west",
  "alsoListedOn": [
    { "source": "weworkremotely", "sourceName": "We Work Remotely", "sourceUrl": "https://weworkremotely.com/remote-jobs/webflow-senior-manager-corporate-sales-west", "postedAt": "2026-10-13T07:31:00.000Z" }
  ],
  "sourcesCount": 2,
  "attribution": "Job listed on Jobicy: https://jobicy.com/jobs/153107-senior-manager-corporate-sales-west"
}
```

### Sources, terms and attribution

The actor only uses boards whose public API or feed may be read by programs without logging in, without getting past bot protection, and without a terms-of-service ban. It follows each board's attribution rule and call limit. If you republish the jobs, you must follow the same rules. Every row gives you `sourceUrl` and a ready-made `attribution` line for this.

| Board | Endpoint used | Terms | Attribution and limits the actor respects |
|---|---|---|---|
| Remotive | `remotive.com/api/remote-jobs` | [API documentation](https://remotive.com/api-documentation) | Link back to the Remotive listing and mention Remotive as the source. At most 4 calls a day (the actor calls it at most once every 6 hours per Apify account). Jobs appear with a 24-hour delay. Do not submit Remotive jobs to third-party job sites such as Jooble, Google Jobs or LinkedIn Jobs. |
| Remote OK | `remoteok.com/api` | [API notice](https://remoteok.com/api) (first element of the response) | Link back to the Remote OK listing URL and mention Remote OK as the source. Do not use the Remote OK logo. |
| We Work Remotely | `weworkremotely.com/remote-jobs.rss` | Public RSS feed published by the board for readers and aggregators | Link back to the listing. The feed's TTL is 60 minutes, and the actor does not read it more than once an hour. The board has no separate API terms page that we could load, so treat it as an RSS feed: show the title, link to the listing, and do not republish full descriptions without permission. |
| Himalayas | `himalayas.app/jobs/api` and `/jobs/api/search` | [Remote jobs API docs](https://himalayas.app/docs/remote-jobs-api) | Free and needs no key. Attribution with a link to Himalayas is required. Rate-limited, and its data is refreshed about every 24 hours, so the actor caches it for 12 hours. |
| Jobicy | `jobicy.com/api/v2/remote-jobs` | [API and feed terms](https://jobicy.com/jobs-rss-feed) | Credit Jobicy with a direct link to the listing, and send apply clicks to the original URL. Do not sync more than once an hour (the actor caches it for 60 minutes). |
| Arbeitnow | `arbeitnow.com/api/job-board-api` | [Job board API](https://www.arbeitnow.com/blog/job-board-api) | A free public API ("please do not abuse"). Linking back is appreciated. Only jobs marked remote are kept. |

Boards we checked and left out on purpose:

- **The Muse:** its API terms require app registration and forbid redistributing or reselling the data.
- **Working Nomads:** its terms forbid commercial use of the service and building a competing product.

### Input options

| Input | Type | Default | What it does |
|---|---|---|---|
| `keywords` | array | none | Words or phrases matched as whole words in title, company, tags and description. Keyword runs also query the Himalayas search endpoint, which reaches deeper than its newest-jobs feed. |
| `keywordMode` | `any` / `all` | `any` | A job needs at least one keyword (`any`) or every keyword (`all`). |
| `excludeKeywords` | array | none | Drop jobs containing any of these words. |
| `titleOnly` | boolean | false | Match keywords and exclude keywords against the title only. |
| `sources` | array | all 6 | Which boards to read: `remotive`, `remoteok`, `weworkremotely`, `himalayas`, `jobicy`, `arbeitnow`. |
| `himalayasPages` | integer | 10 | Pages of the Himalayas feed to read (20 jobs per page, 1 to 50). |
| `arbeitnowPages` | integer | 3 | Pages of the Arbeitnow feed (100 jobs per page; only remote ones are kept). |
| `categories` | array | any | Normalised categories to keep (see the output table). |
| `jobTypes` | array | any | full-time, part-time, contract, freelance, internship, temporary. |
| `experienceLevels` | array | any | internship, entry, mid, senior, lead, executive. |
| `postedWithinDays` | integer | 0 (off) | Keep jobs posted in the last N days. |
| `companies` | array | none | Keep only these companies (a name contains-match). |
| `excludeCompanies` | array | none | Drop these companies, for example staffing agencies. |
| `minSalary` | integer | 0 (off) | Drop jobs whose yearly top of range is below this. Jobs without a salary are kept unless `hasSalaryOnly` is on. |
| `salaryCurrency` | string | USD | Currency of `minSalary`: USD, EUR, GBP, CAD, AUD, CHF or INR. |
| `hasSalaryOnly` | boolean | false | Keep only jobs that publish a salary. |
| `regions` | array | any | Keep jobs open to candidates in these regions. |
| `includeWorldwide` | boolean | true | With `regions`, also keep jobs open worldwide. |
| `timezones` | array | any | UTC offsets such as `-5`, `+1` or `UTC+5:30`. Jobs that list no timezone are kept. |
| `maxItems` | integer | 200 | Stop after this many jobs. You only pay for jobs delivered. |
| `sortBy` | string | `balanced` | `balanced` puts the newest jobs first within each board and takes the boards in turns. `newest` sorts strictly by date. `salary` puts the highest yearly USD salary first. |
| `dedupe` | boolean | true | Merge the same job across boards. |
| `includeDescription` | boolean | true | Include the description. |
| `descriptionFormat` | string | `text` | `text`, `html`, `markdown` or `all`. |
| `monitorMode` | boolean | false | Return and charge only jobs not delivered by earlier monitor runs. |
| `stateStoreName` | string | `remote-jobs-aggregator-seen` | Named key-value store holding the seen jobs. Use one name per saved search. |

The actor also accepts `searchKeyword` (a single string) as an alias for `keywords`, and `locationRestrictions` as an alias for `regions`. This lets inputs written for other remote-job actors work unchanged.

#### Input examples

Remote contract work for React developers, excluding crypto, newest first:

```json
{
  "keywords": ["react"],
  "titleOnly": true,
  "jobTypes": ["contract", "freelance"],
  "excludeKeywords": ["crypto", "web3"],
  "sortBy": "newest"
}
```

The highest-paying remote data jobs open to candidates in Canada:

```json
{
  "categories": ["data"],
  "regions": ["canada", "north-america"],
  "hasSalaryOnly": true,
  "sortBy": "salary",
  "maxItems": 100
}
```

Jobs open to Europe in the UTC+1 timezone, with full HTML and Markdown descriptions:

```json
{
  "regions": ["europe"],
  "timezones": ["+1"],
  "descriptionFormat": "all"
}
```

### Use cases and n8n automation ideas

Run this actor from an n8n workflow with the Apify node or an HTTP Request node, then read the dataset items in the next step. Ideas:

- **Job board feeds.** Combine several remote job boards into one deduplicated list with parsed salary.
- **Salary research.** Filter by category or seniority and compare the parsed yearly salary ranges.
- **New-job alerts.** Use monitor mode from n8n so only jobs not seen before are returned and sent to Slack or email.

### Pricing

This actor uses pay-per-event pricing. You pay for each unique job delivered, plus a tiny start fee per run. Apify platform usage is included in these prices.

| Apify plan | Price per job | Price per 1,000 jobs |
|---|---|---|
| Free | $0.0015 | $1.50 |
| Bronze (Starter) | $0.0012 | $1.20 |
| Silver (Scale) | $0.0010 | $1.00 |
| Gold (Business) | $0.0009 | $0.90 |
| Platinum | $0.0009 | $0.90 |
| Diamond | $0.0009 | $0.90 |
| Actor start (every plan) | $0.00005 per run | |

Worked examples:

- The default run (`{}`, 200 jobs) on the Free plan costs 200 × $0.0015 + $0.00005 ≈ **$0.30**.
- A daily monitor run that finds 40 new jobs costs about **$0.06** per day on the Free plan, or **$0.04** on Silver. Jobs already sent in earlier runs cost nothing.
- 10,000 jobs a month on Gold costs **$9.00**.

You are never charged for:

- duplicates merged across boards
- jobs removed by your filters
- jobs skipped by monitor mode

Set a maximum cost per run in the run options. The actor stops cleanly when it reaches that limit.

### How to run it

#### In Apify Console

Open the actor, fill in the input form (every field has a description), and press **Start**. Results appear live in the Output tab. You can download them as JSON, CSV, Excel, XML or HTML, or open them through the API.

#### From the API or a client library

curl (synchronous; returns the jobs directly):

```bash
curl -X POST "https://api.apify.com/v2/acts/transparent_meteorite~remote-jobs-aggregator/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"keywords": ["python"], "minSalary": 100000, "maxItems": 50}'
```

Python:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("transparent_meteorite/remote-jobs-aggregator").call(run_input={
    "keywords": ["python"],
    "minSalary": 100000,
    "maxItems": 50,
})
for job in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(job["title"], job["company"], job["salaryYearlyMaxUsd"], job["sourceUrl"])
```

JavaScript:

```js
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('transparent_meteorite/remote-jobs-aggregator').call({
  categories: ['design'],
  regions: ['europe'],
  postedWithinDays: 3,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.length, items[0]);
```

#### On a schedule

In Apify Console, open **Schedules**, create a schedule (for example every 6 hours), and add this actor or a saved task with `"monitorMode": true`. Each run then returns only new jobs. Add a webhook or an integration to send them to email, Slack, Google Sheets or your own API.

#### With n8n, Make, Zapier or AI agents

- **n8n:** use the Apify node ("Run actor and get dataset").
- **Make:** use the Apify app ("Run an Actor" and then "Get Dataset Items").
- **Zapier:** use the Apify integration.

A common flow:

1. A schedule runs the actor in monitor mode.
2. The flow loops over the new jobs.
3. It posts each job to Slack or Discord, or appends it to a Google Sheet.

AI agents can call the actor through the Apify MCP server (see below).

### Watch mode (new jobs only)

Turn on `monitorMode` and keep the same `stateStoreName` between runs. The actor keeps a small map of delivered jobs, keyed by `dedupKey`, in that named key-value store.

- Delivered jobs are skipped on later runs, and skipped jobs are not charged.
- The first run delivers everything that matches your filters. Later runs deliver only new jobs.
- A job seen on one board is not sent again when it later appears on another board.
- Entries older than 120 days are forgotten automatically.
- Use a different `stateStoreName` for each saved search, so separate alerts do not hide each other's jobs.

### Run summary record

Each run writes an `OUTPUT` record to its default key-value store. It contains:

- **Per-board results:** jobs fetched, whether the cache served them, cache age, any error, and the board's terms URL.
- **Totals:** jobs fetched, unique jobs, duplicates merged, jobs matched, jobs delivered, jobs skipped as already seen.
- **Filters:** how many jobs each filter removed (for example `{"keywords": 447, "minSalary": 8}`).
- **Warnings:** anything worth checking.

Use it to see why a run returned fewer jobs than you expected.

### Limits and honest caveats

- **Newest jobs, not full archives.** Each board's public API returns its recent jobs. Remote OK returns about 100, the We Work Remotely feed about 90, Jobicy up to 200, and Remotive's free API a delayed sample. Himalayas and Arbeitnow page deeper. Expect a few hundred to about a thousand unique jobs per full run, depending on settings. Run on a schedule in monitor mode to build up a complete history.
- **Shared cache.** To respect each board's call limits, the actor stores responses in a key-value store in your account called `remote-jobs-aggregator-cache`. Each board is called again only after its minimum interval: Remotive 6 hours, Himalayas 12 hours, the others 1 hour. Runs inside that interval reuse the stored copy. If a board is down, a copy up to 48 hours old is used, and the run shows a warning.
- **Remotive jobs are delayed** by 24 hours in its free API.
- **Inferred fields are best effort.** Category, experience level and regions use the board's own values where they exist; otherwise they are inferred from the title and the location text. They are good for filtering, not for legal decisions.
- **Exchange rates are fixed and approximate.** `salaryYearlyMaxUsd` is for ranking and filtering, not for payroll.
- **Dedupe matches company plus title.** Two genuinely different openings with the same company and the same title (for example in different countries) merge into one row. The other listings stay visible in `alsoListedOn`. Set `dedupe: false` to keep every listing.

### FAQ

**Do I need API keys or accounts for these job boards?**
No. All six boards publish keyless public APIs or feeds. You only need an Apify account.

**Why did my keyword run return only a few jobs?**
Boards publish their newest jobs only, and keyword matching uses whole words. Try `keywordMode: "any"` with several synonyms, or turn off `titleOnly`. The `OUTPUT` record shows how many jobs each filter removed.

**Can I republish these jobs on my own site?**
Follow each board's attribution rule (see the sources table). Link every job to its `sourceUrl` and name the board. Do not use the Remote OK logo. Do not pass Remotive jobs on to other job sites such as Google Jobs, LinkedIn or Jooble. The `attribution` field gives you a ready-made credit line.

**How is this different from scraping each board myself?**
You get one schema for six boards, cross-board dedupe, parsed salaries and regions, monitor mode, and built-in respect for each board's call limits, without maintaining six parsers.

**Why does a second run within the hour return the same jobs?**
That is the shared cache, which respects the boards' call limits. New jobs appear after the board's refresh interval. In monitor mode, repeats are skipped and never charged.

**Does it include salaries for jobs that only mention pay in the description?**
Yes. When there is no structured salary or salary text, the actor looks for a pay range in the description. Such rows have `salarySource: "description"`.

**What does `minSalary` do with jobs that have no salary?**
It keeps them, because many good remote jobs do not publish pay. Add `hasSalaryOnly: true` to drop them.

**Can I get the description in Markdown for an LLM?**
Yes. Set `descriptionFormat` to `markdown`, or to `all` to get text, HTML and Markdown.

### Troubleshooting

| Symptom | Likely cause | Fix |
|---|---|---|
| 0 jobs delivered | Filters too strict, or monitor mode has already sent everything | Check `filteredOut` and `skippedAlreadySeen` in `OUTPUT`. Loosen the filters or wait for new jobs. |
| A board shows `error` in `OUTPUT` | The board was down or rate-limited | The run continues with the other boards. A cached copy (up to 48 hours old) is used when available. Retry later. |
| Same jobs as the last run | The shared cache is inside the board's refresh interval | Expected. Use monitor mode to get only new jobs. |
| Salary looks off | Unusual salary text | Compare with `salaryText` and `salarySource`, and open an issue with the job URL. |
| Fewer Remotive jobs than on its site | The free API returns a 24-hour-delayed sample | Expected. The other boards cover the gap. |

### Terms and responsible use

The actor reads only public, keyless APIs and feeds that the boards publish for programmatic use. It does not log in, solve captchas, rotate proxies or scrape HTML pages. It calls each board no more often than that board allows, and it keeps a link back to each original listing on every row.

You are responsible for how you use the data. Follow each board's attribution rule and restrictions, listed in the sources table. Do not resubmit Remotive jobs to third-party job boards. Do not imply that any board endorses your product. Treat company names and job descriptions as the property of their owners.

### More resources

- Guide & examples: [mamoo85.github.io/open-data-actors/remote-jobs-aggregator.html](https://mamoo85.github.io/open-data-actors/remote-jobs-aggregator.html)

### Works well with

Other actors from Detroit Web Agent - Open Data that pair with this one:

- [LinkedIn Public Jobs Scraper](https://apify.com/transparent_meteorite/linkedin-public-jobs): add on-site and hybrid roles from LinkedIn's public job search.
- [Workday Jobs Scraper](https://apify.com/transparent_meteorite/workday-jobs-scraper): pull jobs straight from company career sites that run on Workday.

### Changelog

- **0.1 (2026-10-13):** first release. Six boards (Remotive, Remote OK, We Work Remotely, Himalayas, Jobicy, Arbeitnow). Added in this release:
  - cross-board dedupe with `alsoListedOn`
  - salary parsing with yearly and yearly-USD amounts
  - normalised categories, job types, experience levels, regions and timezones
  - monitor mode
  - text, HTML and Markdown descriptions
  - balanced, newest and salary sort orders
  - a per-account cache that respects each board's call limits

### Support

Found a bug, or need a board or a field? Open an issue on this actor's Issues tab. Include the run URL and an example job URL, and we will reply there.

***

Built and maintained by **Detroit Web Agent - Open Data** (Detroit, Michigan). Questions, bugs or a field you need: open an issue on this Actor's Issues tab and we will reply there.

### Use it from AI assistants (MCP)

Remote Jobs Aggregator works as a tool for AI assistants through the official Apify MCP server. Add this server URL to any MCP client (Claude Desktop, Claude Code, Cursor, ChatGPT connectors, VS Code):

```
https://mcp.apify.com/?actors=transparent_meteorite/remote-jobs-aggregator
```

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com/?actors=transparent_meteorite/remote-jobs-aggregator",
      "headers": { "Authorization": "Bearer <YOUR_APIFY_TOKEN>" }
    }
  }
}
```

Example prompts: "Find remote Python jobs paying over $120k posted this week", "List new remote design jobs open to Europe".

**Did this actor do the job?** If it saved you time, a short honest review on the [Reviews tab](https://apify.com/transparent_meteorite/remote-jobs-aggregator/reviews) helps other people judge it. If something was off, open an Issue on this page with the input you used. I read every one and fix real bugs, usually the same day.

# Actor input Schema

## `keywords` (type: `array`):

Words or phrases to look for in the title, company, tags and description (whole-word, case-insensitive). Leave empty for every remote job. Example: \["python", "data engineer"].

## `keywordMode` (type: `string`):

any = a job matches if it contains at least one keyword. all = it must contain every keyword.

## `excludeKeywords` (type: `array`):

Drop jobs that contain any of these words. Example: \["crypto", "commission only"].

## `titleOnly` (type: `boolean`):

Match keywords and exclude keywords against the job title only (much stricter, fewer false hits).

## `sources` (type: `array`):

Which official job-board APIs/feeds to read. Default: all 6.

## `himalayasPages` (type: `integer`):

Pages of the Himalayas newest-jobs feed to read (20 jobs per page). Keyword runs also use the Himalayas search endpoint.

## `arbeitnowPages` (type: `integer`):

Pages of the Arbeitnow feed to read (100 jobs per page, only remote ones are kept).

## `categories` (type: `array`):

Keep only these normalised job categories (same list for every board).

## `jobTypes` (type: `array`):

Keep only these employment types.

## `experienceLevels` (type: `array`):

Keep only these seniority levels (from the board where given, else from the title).

## `postedWithinDays` (type: `integer`):

Keep only jobs posted in the last N days. 0 = no limit. Example: 1 for the last 24 hours, 7 for the last week.

## `companies` (type: `array`):

Keep only jobs from companies whose name contains one of these. Example: \["GitLab", "Automattic"].

## `excludeCompanies` (type: `array`):

Drop jobs from companies whose name contains one of these (recruiters, staffing agencies you do not want).

## `minSalary` (type: `integer`):

Drop jobs whose salary range tops out below this yearly amount (hourly and monthly pay is converted to yearly). Jobs without a salary are kept unless "Only jobs with a salary" is on. 0 = off.

## `salaryCurrency` (type: `string`):

Currency of the minimum salary above. Comparisons are made in USD using fixed approximate rates.

## `hasSalaryOnly` (type: `boolean`):

Keep only jobs that publish a salary (structured or in the text).

## `regions` (type: `array`):

Keep jobs open to candidates in these regions (location restriction of the remote job).

## `includeWorldwide` (type: `boolean`):

With a region filter, also keep jobs open worldwide.

## `timezones` (type: `array`):

Keep jobs that list one of these UTC offsets, plus jobs that list no timezone. Example: \["-5", "+1", "UTC+5:30"]. Himalayas publishes timezones; other boards rarely do.

## `maxItems` (type: `integer`):

Stop after this many jobs (after dedupe and filters). You only pay for jobs delivered.

## `sortBy` (type: `string`):

balanced = newest first within each board, boards taken in turn, so a run capped by Max jobs still shows every board. newest = strictly newest first overall. salary = highest yearly salary (in USD) first.

## `dedupe` (type: `boolean`):

One row per job when the same company posts the same title on several boards; the other listings go to alsoListedOn.

## `includeDescription` (type: `boolean`):

Include the job description.

## `descriptionFormat` (type: `string`):

Format of the description field(s): descriptionText, descriptionHtml, descriptionMarkdown.

## `monitorMode` (type: `boolean`):

Remember delivered jobs in a named key-value store and skip them next time. Schedule the actor daily or hourly to get (and pay for) only new jobs.

## `stateStoreName` (type: `string`):

Named key-value store that holds the seen jobs. Use a different name per saved search so they do not share memory.

## Actor input object example

```json
{
  "keywords": [
    "python"
  ],
  "keywordMode": "any",
  "titleOnly": false,
  "sources": [
    "remotive",
    "remoteok",
    "arbeitnow",
    "jobicy",
    "himalayas",
    "weworkremotely"
  ],
  "himalayasPages": 10,
  "arbeitnowPages": 3,
  "postedWithinDays": 0,
  "minSalary": 0,
  "salaryCurrency": "USD",
  "hasSalaryOnly": false,
  "includeWorldwide": true,
  "maxItems": 50,
  "sortBy": "balanced",
  "dedupe": true,
  "includeDescription": true,
  "descriptionFormat": "text",
  "monitorMode": false,
  "stateStoreName": "remote-jobs-aggregator-seen"
}
```

# Actor output Schema

## `overview` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "keywords": [
        "python"
    ],
    "sources": [
        "remotive",
        "remoteok",
        "arbeitnow",
        "jobicy",
        "himalayas",
        "weworkremotely"
    ],
    "maxItems": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("transparent_meteorite/remote-jobs-aggregator").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "keywords": ["python"],
    "sources": [
        "remotive",
        "remoteok",
        "arbeitnow",
        "jobicy",
        "himalayas",
        "weworkremotely",
    ],
    "maxItems": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("transparent_meteorite/remote-jobs-aggregator").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "keywords": [
    "python"
  ],
  "sources": [
    "remotive",
    "remoteok",
    "arbeitnow",
    "jobicy",
    "himalayas",
    "weworkremotely"
  ],
  "maxItems": 50
}' |
apify call transparent_meteorite/remote-jobs-aggregator --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,transparent_meteorite/remote-jobs-aggregator"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/IBxCdrQKiNtBPYA19/builds/avZSXpWq6oVKB8Xh1/openapi.json
