# Remote Jobs Aggregator — 7 Boards, One Feed (`dataloft/remote-jobs-aggregator`) Actor

\[💵 $1.50 / 1K] Remote jobs API: RemoteOK, Remotive, Himalayas, We Work Remotely, Jobicy, Arbeitnow and Working Nomads via their official feeds, deduplicated into one schema with salaries and full descriptions. Monitoring mode returns only new jobs. For n8n, Make, Zapier & MCP.

- **URL**: https://apify.com/dataloft/remote-jobs-aggregator.md
- **Developed by:** [Dataloft Studio](https://apify.com/dataloft) (community)
- **Categories:** Jobs, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 jobs

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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

Remote jobs API that aggregates seven remote job boards — RemoteOK, Remotive, Himalayas, We Work Remotely, Jobicy, Arbeitnow and Working Nomads — through their own public APIs and RSS feeds, merged into one deduplicated feed with a single schema. Works with n8n, Make, Zapier, the Apify API and as an MCP tool for AI agents.

### What is the Remote Jobs Aggregator?

Remote job data is scattered across boards that each have their own format, their own idea of what "location" means, and a lot of overlap between them. This actor calls all seven, normalises everything into one shape, and drops the duplicates before anything is charged.

Every source here is an **official public feed the board publishes itself** — no scraping around a login, no browser automation, no proxies needed, no personal data.

### What data do you get from the remote job boards?

#### Output fields

| Field | Notes |
|---|---|
| `title`, `company`, `companyLogo` | |
| `location`, `locations[]`, `isRemote` | region restrictions kept where the board states them |
| `department`, `employmentType`, `seniority`, `tags[]` | when the board exposes them |
| `compensation` | `{min, max, currency, interval}` — structured where published, parsed from the salary text otherwise |
| `descriptionHtml`, `descriptionText` | full posting, both formats |
| `applyUrl`, `jobUrl` | |
| `postedAt`, `scrapedAt` | ISO 8601 |
| `source`, `sourceBoard`, `sourceHomepage` | which board the row came from |

#### Example output JSON

```json
{
  "title": "Senior Backend Engineer",
  "company": "Orca Bio",
  "location": "Worldwide",
  "isRemote": true,
  "employmentType": "full_time",
  "tags": ["python", "django", "aws"],
  "compensation": { "min": 120000, "max": 160000, "currency": "USD", "interval": "year" },
  "descriptionText": "We are looking for…",
  "applyUrl": "https://remoteok.com/remote-jobs/…",
  "postedAt": "2026-08-24T09:12:00.000Z",
  "sourceBoard": "RemoteOK"
}
```

### Which remote job boards are included?

| Board | Feed | Typical volume |
|---|---|---|
| RemoteOK | official JSON API | ~100 latest |
| Remotive | official API | current live set |
| Himalayas | official API, paginated | 100,000+ available, you choose the depth |
| We Work Remotely | official RSS | ~90 latest |
| Jobicy | official API v2 | ~100 latest |
| Arbeitnow | official job-board API, paginated | 1,000+ |
| Working Nomads | official feed | ~40 latest |

Himalayas caps every page at 20 rows no matter what you ask for, so the actor pages through it in 20s up to the depth you set — that one board alone can supply far more than the other six combined.

### How to build a remote jobs feed

1. Leave **Sources** empty to use all seven, or pick a subset.
2. Add filters — keyword, title, location, department, posted-after, minimum salary.
3. Set **Max jobs in total** on a first run to see the output shape cheaply.
4. Export as JSON, CSV or Excel, or read it from the API.

#### Example input JSON

```json
{
  "sources": ["remoteok", "himalayas", "weworkremotely", "jobicy"],
  "keywords": ["python", "backend"],
  "locationIncludes": ["europe", "worldwide"],
  "minSalary": 80000,
  "postedAfter": "2026-08-01",
  "maxJobsPerSource": 200,
  "maxTotalJobs": 1000
}
```

### Deduplication across boards

The same role frequently appears on several boards. Rows are keyed on the normalised apply URL, falling back to company + title + location. The run summary reports how many duplicates were removed so you can see the overlap — and duplicates are never charged.

### Monitoring mode: only new remote jobs since your last run

Turn on **Only new jobs since last run** and the actor remembers every posting it has already delivered, in a named key-value store on your account, and skips it next time. A daily scheduled run then returns only the roles that appeared since yesterday, which is what a jobs-alert, a newsletter or a Slack feed actually needs. Use **Seen-jobs store name** to keep separate feeds (one per keyword set, say). The log reports `monitoring: scanned N, new M, previously seen K`. Pair it with a generous or unset **Max jobs in total** — the cap bounds the scan, so a capped run returns the next unseen postings rather than everything that is new.

### How much does it cost to aggregate remote jobs?

**$1.50 per 1,000 jobs.** You pay per unique job saved to your dataset. Filtered-out postings, cross-board duplicates and previously-seen postings in monitoring mode are free — deduplication happens before anything is charged, so paying twice for the same role listed on three boards is not possible. Empty runs cost $0.

#### Cost examples

| What you run | Rows charged | Cost |
|---|---|---|
| All seven boards, latest window, no filters | ~600 | $0.90 |
| Python + backend roles, salary shown, Europe/worldwide | ~120 | $0.18 |
| Deep Himalayas pull, 5,000 rows | 5,000 | $7.50 |
| Daily monitoring of all boards, ~80 new roles a day | ~80 per day | ~$0.12 per day, ~$3.60 per month |

### Integrations: n8n, Make, Zapier, MCP and the API

#### Use with AI agents (MCP server)

```bash
npx -y @apify/actors-mcp-server --actors dataloft/remote-jobs-aggregator
```

Or enable the actor at `https://mcp.apify.com` with your Apify token. An agent can then answer "find me remote senior Python roles posted this week that state a salary" from one tool call.

#### n8n, Make and Zapier

Use the Apify node (n8n), the Apify app (Make) or the Apify integration (Zapier): **Run Actor** → `dataloft/remote-jobs-aggregator` → paste the input JSON → read dataset items. With monitoring mode on, "new remote jobs → Slack / Sheets / newsletter draft" is a three-node workflow.

#### JavaScript, Python and REST

```js
const { ApifyClient } = require('apify-client');
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('dataloft/remote-jobs-aggregator').call({
  keywords: ['python'], minSalary: 80000, maxTotalJobs: 300,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

```python
from apify_client import ApifyClient
client = ApifyClient(token=os.environ["APIFY_TOKEN"])
run = client.actor("dataloft/remote-jobs-aggregator").call(run_input={"keywords": ["python"], "maxTotalJobs": 300})
items = client.dataset(run["defaultDatasetId"]).list_items().items
```

REST: `POST https://api.apify.com/v2/acts/dataloft~remote-jobs-aggregator/run-sync-get-dataset-items?token=…`

### What this Actor does and does not do

| Does | Does not |
|---|---|
| Merge seven official remote-job feeds into one schema | Scrape LinkedIn, Indeed or boards that forbid it |
| Deduplicate the same role across boards before charging | Log in, solve CAPTCHAs, or bypass anti-bot systems |
| Parse salary ranges into structured min/max/currency | Invent salaries that are not published |
| Return only new postings in monitoring mode | Collect any personal data |

### Limits and known caveats

- Five of the seven boards publish a "latest" window (roughly 40–100 postings); only Himalayas and Arbeitnow paginate deep. Volume beyond a few hundred rows comes from those two.
- Remotive's public API sometimes serves a small set; the per-source count in the run summary shows what the board returned.
- Location on remote boards is a region restriction, not a city; `locations[]` keeps the board's wording.
- One board being down does not fail the run — it is listed under `failures` in the run summary and the rest still return.

### Example tasks

Ready-made feeds — remote Python jobs deduped across 7 boards, remote roles posted this week with salary, new remote jobs since yesterday — are on the **Example tasks** tab: https://apify.com/dataloft/remote-jobs-aggregator/examples

### Is it legal to scrape these job boards?

Every endpoint used here is a public feed the board publishes for third-party consumption — the same feeds job aggregators and RSS readers use. Postings are company information, not personal data. You remain responsible for how you use the output, including under each board's terms if you intend to republish commercially.

### Frequently asked questions

**Why did Remotive only return a handful of jobs?** Because that is what its public API is currently serving. The actor reports the fetched count per source, so a small number there reflects the board, not a failure.

**Can I get more than a few hundred jobs?** Yes — raise **Max jobs per source**. Himalayas and Arbeitnow both paginate deep; the other five publish a "latest" window.

**Do I need a proxy?** No. These are public APIs with normal rate limits. The proxy option exists only for unusually large scheduled runs.

**Can I schedule a daily pull?** Yes. With **Only new jobs since last run** on, each run delivers only what is new; with it off, deduplication still gives you a clean current snapshot rather than a growing pile of repeats.

**Can I use it from n8n or as an MCP tool?** Yes — see Integrations above.

**One board failed — did the run fail?** No. Sources are independent.

### Other job scrapers by Dataloft Studio

- [ATS Jobs Scraper](https://apify.com/dataloft/ats-jobs-scraper) — pull roles straight from company career sites across 11 ATS platforms.
- [Workday Jobs Scraper](https://apify.com/dataloft/workday-jobs-scraper) — every open role from any Workday career site.

### Changelog

- **0.2 — 2026-09-02**: monitoring mode (only new jobs since last run); memory capped at 1 GB; dataset schema fixed to match emitted fields; changelog added.
- **0.1.2 — 2026-08-25**: output schema added; first-run prefill capped for the daily automated test.
- **0.1 — 2026-08-25**: initial release, seven boards, cross-board deduplication.

### Support and feedback

Issues and feature requests go in the Issues tab. Reports that include the input JSON get fixed fastest.

# Actor input Schema

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

Leave empty to use all seven.

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

Case-insensitive, e.g. python, react, devops.

## `titleIncludes` (type: `array`):

Case-insensitive. A role is kept if its title contains at least one of these, e.g. engineer, designer, product manager.

## `titleExcludes` (type: `array`):

Case-insensitive. Handy for dropping intern, contractor or director-level postings.

## `locationIncludes` (type: `array`):

Matches the region restriction the board publishes, e.g. europe, usa, worldwide.

## `departmentIncludes` (type: `array`):

Case-insensitive match against the department or team the platform publishes.

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

Keeps only postings with a published salary at or above this number. Postings with no salary data are dropped when this is set.

## `postedAfter` (type: `string`):

ISO date such as 2026-08-01. Postings without a date are always kept.

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

Turn off for a much smaller dataset when you only need titles, companies and links.

## `maxJobsPerSource` (type: `integer`):

Also controls how deep the paginated boards go. 0 means no limit.

## `maxTotalJobs` (type: `integer`):

0 means no limit.

## `deduplicate` (type: `boolean`):

Drops the same role when it appears more than once, keyed on the apply URL.

## `proxyConfiguration` (type: `object`):

Optional. These are public APIs, so a proxy is rarely needed — use it only for very large scheduled runs from a single IP.

## `onlyNewJobs` (type: `boolean`):

Turns this into a job monitor. Every posting delivered is remembered in a named key-value store, and later runs skip anything already on that list — skipped postings are not saved to the dataset and are not charged for. The first run builds the baseline and returns everything it finds; from then on you get only what appeared since. Note the limits above count postings scanned, not new ones found, so pair this with a generous (or unset) "Max jobs in total".

## `seenStoreName` (type: `string`):

Optional. Name of the key-value store that holds the seen-jobs list. Defaults to dataloft-seen-remote-jobs-aggregator. Give each watchlist its own name when you run several monitors that should not share history.

## Actor input object example

```json
{
  "sources": [],
  "keywords": [],
  "titleIncludes": [],
  "titleExcludes": [],
  "locationIncludes": [],
  "departmentIncludes": [],
  "minSalary": 0,
  "includeDescription": true,
  "maxJobsPerSource": 0,
  "maxTotalJobs": 200,
  "deduplicate": true,
  "proxyConfiguration": {
    "useApifyProxy": false
  },
  "onlyNewJobs": false,
  "seenStoreName": ""
}
```

# Actor output Schema

## `jobs` (type: `string`):

All aggregated remote job postings in the normalized schema (title, company, location, salary, tags, applyUrl, postedAt, source board and more), deduplicated across boards.

# 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 = {
    "maxTotalJobs": 200
};

// Run the Actor and wait for it to finish
const run = await client.actor("dataloft/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 = { "maxTotalJobs": 200 }

# Run the Actor and wait for it to finish
run = client.actor("dataloft/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 '{
  "maxTotalJobs": 200
}' |
apify call dataloft/remote-jobs-aggregator --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,dataloft/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/e7aiwgPWvcbjqgDAi/builds/cub20FfT8bTMlfcjE/openapi.json
