# Remote Jobs Scraper & Aggregator 🌍 (7 job boards, one feed) (`tagadanar/remote-jobs-aggregator`) Actor

Remote jobs from Remote OK, We Work Remotely, Jobicy, Working Nomads, Arbeitnow and more in one clean feed: normalised location, salary, category, cross-board duplicates removed. Filter by keyword, region, salary, date. Monitor mode returns only new jobs. No API key.

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

## Pricing

from $1.40 / 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?

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 Scraper & Aggregator: 7 remote job boards in one clean feed

Pull current remote job openings from **Remote OK, We Work Remotely, Jobicy, Working Nomads, Arbeitnow (remote roles), NoDesk and Real Work From Anywhere** with one run and get one flat schema: normalised location eligibility, salary, category, job type, posted date, apply link and the full description. The same job posted on several boards is delivered once.

Filter by keyword, category, where you can work from, minimum salary, job type and posting date. Schedule it with **Monitor mode** and you get a new-remote-jobs feed that never repeats itself.

$2.00 per 1,000 jobs, platform usage included. No API key, no login.

### What you get

One row per job:

| Field | Description |
| --- | --- |
| `jobId`, `source`, `sourceName` | `remoteok:1137528`, `remoteok`, `Remote OK` |
| `url`, `applyUrl` | The job's page on the board it came from (always the original) and where to apply |
| `title`, `company`, `companyUrl`, `companyLogo` | Company website and logo when the board gives them |
| `locationRaw` | The location text exactly as the board states it |
| `eligibility` | `worldwide`, `regional` or `unknown` |
| `regions`, `countries`, `timezone` | Who can apply, normalised: `worldwide`, `usa`, `canada`, `uk`, `europe`, `latam`, `apac`, `africa`, `middle-east`; countries named; the time-zone phrase if any ("CET +/- 3 hours") |
| `salaryMin`, `salaryMax`, `salaryCurrency`, `salaryPeriod`, `salaryRaw`, `salarySource` | Parsed pay (`year`, `month`, `week`, `day`, `hour`). `salarySource` is `board` when the board gives structured pay and `parsed-from-text` when we read a clear range like "$120k - $150k" from the posting |
| `tags` | Skills / tags |
| `jobType`, `jobTypeRaw` | `full-time`, `part-time`, `contract`, `freelance`, `internship` (null when the board publishes none) |
| `category`, `categoryRaw` | One shared category list (engineering, devops, data-ai, design, product, marketing, sales, customer-support, finance, hr, writing, education, operations, other) and the board's own label |
| `seniority` | Where the board gives it (Jobicy) |
| `postedAt`, `expiresAt` | ISO dates |
| `description`, `descriptionHtml` | Plain-text description; cleaned HTML with the option on |
| `alsoListedOn`, `duplicateOf` | Cross-board duplicates (see below) |
| `scrapedAt` | When the row was collected |

#### Example row (trimmed, from a real run on 2026-10-16)

```json
{
  "jobId": "weworkremotely:sanctuary-computer-head-of-technology",
  "source": "weworkremotely",
  "sourceName": "We Work Remotely",
  "url": "https://weworkremotely.com/remote-jobs/sanctuary-computer-head-of-technology",
  "title": "Head of Technology",
  "company": "Sanctuary Computer",
  "companyUrl": "https://sanctuary.computer",
  "locationRaw": "Anywhere in the World",
  "eligibility": "worldwide",
  "regions": ["worldwide"],
  "jobType": "full-time",
  "category": "engineering",
  "categoryRaw": "Full-Stack Programming",
  "postedAt": "2026-10-16T14:21:11.000Z",
  "expiresAt": "2026-11-15T14:21:11.000Z",
  "alsoListedOn": [],
  "duplicateOf": null,
  "scrapedAt": "2026-10-16T16:11:34.079Z"
}
```

### Who uses this

- **Job seekers and career coaches** who want every remote opening for a skill in one list, filtered to where they can legally work.
- **Recruiters and sourcers** watching which companies hire remotely and for what.
- **Job boards, newsletters and communities** curating remote roles. Each row links to the original posting; see Attribution below.
- **Market researchers and analysts** tracking remote-hiring volume, pay ranges and skills by category.

### Input

| Field | What it does |
| --- | --- |
| Keywords | Keep jobs with ANY of these words in title, tags, company or description (whole words) |
| Exclude keywords | Drop jobs with any of these words in title or tags |
| Job boards | Pick boards; empty = all seven |
| Categories | Keep these categories |
| Where can you work from? | Keep jobs open to a candidate in ANY of the chosen regions. Worldwide jobs always match |
| Minimum salary (USD per year) | Keep jobs whose posted range reaches this yearly amount (other periods and common currencies converted at fixed approximate rates). Jobs with no readable salary are dropped when set |
| Posted within (days) | Newest jobs only |
| Job type | Full-time, part-time, contract, freelance, internship |
| Max jobs | Newest first, default 100, up to 5,000 |
| Drop cross-board duplicates | On by default |
| Include description HTML | Adds `descriptionHtml` |
| Monitor mode / Monitor name | Return only jobs not delivered by an earlier run with the same name |

Example:

```json
{
  "keywords": ["python", "django"],
  "candidateRegions": ["europe", "usa"],
  "salaryMin": 80000,
  "postedWithinDays": 14,
  "maxJobs": 200
}
```

#### Duplicates

Companies post the same role on several boards. Rows are matched on company + title. By default each job is delivered once, from the board with the richest data, and `alsoListedOn` names the other boards. Turn **Drop cross-board duplicates** off to get every copy; the extra copies carry `duplicateOf` (the `jobId` of the copy to keep). You pay for every row delivered.

#### Monitor mode

Run it on a schedule with Monitor mode on: each run returns only jobs not delivered before under that Monitor name, and a job that later shows up on a different board is not reported again.

### Pricing

Pay per job delivered, platform usage included, plus a flat start fee per run.

| Plan | Per 1,000 jobs | Run start |
| --- | --- | --- |
| Free | $2.00 | $0.001 |
| Bronze | $1.80 | $0.001 |
| Silver | $1.60 | $0.001 |
| Gold | $1.40 | $0.001 |

Boards that return nothing for your filters cost nothing beyond the start fee. Duplicates dropped, jobs filtered out and monitor-mode repeats are not charged. Set a maximum charge per run and the actor stops when it is reached.

### Attribution and terms

Every job links back to its original posting on the board it was listed on (`url`), and `sourceName` names the board. If you republish these jobs, link to the original posting and mention the source board by name: Remote OK, Jobicy and Arbeitnow ask for it, and it is good practice for the other boards too. Do not use the Remote OK logo without its permission.

Two well-known boards are deliberately **not** included: Himalayas and Remotive forbid redistributing their jobs to other job boards and aggregators.

### Limits and honest caveats

- **Boards list current openings only.** Jobicy keeps a 7-day window, Remote OK about 100 jobs, We Work Remotely roughly 10 per category in its all-jobs feed (choosing categories reads the deeper category feeds), Working Nomads about 50, NoDesk about 10. This is a "what is open now" feed, not an archive; schedule runs to build history.
- **Arbeitnow is a German board.** Only roles it flags as remote are read, and they are treated as open to Europe (`regions: ["europe"]`).
- **Location is only as good as the board's text.** Remote OK's location field is often the company's home city, and NoDesk or Real Work From Anywhere give little; those rows come out `unknown` and do not match a region filter. We never guess a region from a city.
- **Salary:** Jobicy and Remote OK give structured pay. For the other boards a salary appears only when the posting states a clear range with a currency; most remote jobs state none. When you set a minimum salary, jobs without a readable salary are left out.
- **Job type:** Remote OK, Working Nomads and Real Work From Anywhere publish none, so they never match a job-type filter.
- **Categories** are inferred from each board's labels and the job title; a few roles land in `other`.
- **Time zones** are kept as text ("CET +/- 3 hours") and only imply a region when nothing else is named.
- The run status names every board that answered empty, was refused, or was not selected, and counts what your filters removed.

### FAQ

**Do I need an API key or account?** No.

**Why did a board return nothing?** The run status says whether it answered with an empty list, refused the request, or was not selected.

**Can I get older jobs?** No, only what the boards list now.

### Related actors

- [LinkedIn Remote Jobs Scraper](https://apify.com/tagadanar/linkedin-remote-jobs): remote jobs from LinkedIn only.
- [ATS Jobs Scraper](https://apify.com/tagadanar/ats-jobs-aggregator): every open role at the companies you name, from their own Greenhouse, Lever, Ashby, Workable and SmartRecruiters boards.
- [GCC Jobs Scraper](https://apify.com/tagadanar/gcc-jobs-scraper): jobs in the Gulf states from Bayt, GulfTalent and NaukriGulf.

# Changelog

This Actor's version history is a separate document: https://apify.com/tagadanar/remote-jobs-aggregator/changelog.md

# Actor input Schema

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

Keep jobs whose title, tags, company or description contains ANY of these words, e.g. python, react, product manager. Whole words, case-insensitive. Leave empty for every job.

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

Drop jobs whose title or tags contain any of these words, e.g. intern, junior.

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

Which boards to read. Leave empty for all seven. Boards you do not select are named as not queried in the run status.

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

Keep only these job categories. Every board's own categories are mapped to one list; the board's original label stays in categoryRaw.

## `candidateRegions` (type: `array`):

Keep jobs open to someone living in ANY of these regions. Worldwide jobs always match. A job whose location says nothing we can read (a bare city, or Remote OK's company-HQ field) does not match when this is set.

## `salaryMin` (type: `integer`):

Keep jobs whose posted pay range reaches at least this yearly amount. Hourly, daily and monthly pay is converted to a year; EUR, GBP and a few other currencies at fixed approximate rates. Jobs with no readable salary are dropped when this is set. 0 = no filter.

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

Keep jobs posted in the last N days. 0 = no limit (boards only list current openings anyway).

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

Keep only these job types. Remote OK, Working Nomads and Real Work From Anywhere publish no job type, so their jobs only match when this is empty.

## `maxJobs` (type: `integer`):

Newest jobs first. You pay only for jobs delivered.

## `dropDuplicates` (type: `boolean`):

The same job (same company and title) is often on several boards. On: deliver it once, from the board with the richest data, and list the other boards in alsoListedOn. Off: deliver every copy and mark the extra ones with duplicateOf (you pay for each).

## `includeDescriptionHtml` (type: `boolean`):

Adds descriptionHtml (cleaned HTML) next to the plain-text description.

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

Remember delivered jobs between runs and return only new ones. Combine with a schedule for a new-jobs feed. The same job appearing on another board later is not reported again.

## `monitorName` (type: `string`):

Label for the remembered set. Use different names to run independent watches (for example one per keyword list).

## Actor input object example

```json
{
  "keywords": [
    "python"
  ],
  "salaryMin": 0,
  "postedWithinDays": 14,
  "maxJobs": 50,
  "dropDuplicates": true,
  "includeDescriptionHtml": false,
  "monitorMode": false,
  "monitorName": "default"
}
```

# Actor output Schema

## `results` (type: `string`):

One item per job in the default dataset.

# 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"
    ],
    "postedWithinDays": 14,
    "maxJobs": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("tagadanar/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"],
    "postedWithinDays": 14,
    "maxJobs": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("tagadanar/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"
  ],
  "postedWithinDays": 14,
  "maxJobs": 50
}' |
apify call tagadanar/remote-jobs-aggregator --silent --output-dataset

```

## MCP server setup

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