# Remote Jobs Scraper – 7 Boards: RemoteOK, WWR, Himalayas (`scrapeworkshq/remote-jobs-scraper`) Actor

Aggregate remote jobs from RemoteOK, We Work Remotely, Himalayas, Jobicy, Remotive, Working Nomads and Arbeitnow into one deduplicated, normalized dataset: salary, location eligibility, timezones, tags, apply URL. New-job alerts. No login, no proxy.

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

## Pricing

Pay per event

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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 – RemoteOK, We Work Remotely, Himalayas & 4 More Boards

Aggregate **remote jobs from 7 job boards in one run** – **RemoteOK, We Work Remotely, Himalayas, Jobicy, Remotive, Working Nomads and Arbeitnow** – into one clean, **normalized and deduplicated** dataset: title, company, salary range (min/max, currency, period), candidate location eligibility, worldwide flag, timezones, seniority, employment type, tags, posted & expiry dates, apply URL and full description. Built-in **new-job alerts** (monitor mode). Uses the boards' official public feeds – no login, no proxy, no browser.

### 🚀 Quick start (60 seconds)

1. Leave **Job boards** on all 7 (or pick some).
2. Optional: add **Keywords** (`python`, `product designer`), **Candidate locations** (`Europe`, `USA`), **Min salary**, **Posted within days**.
3. Click **Start** and export to JSON, CSV, Excel or Google Sheets.

```json
{
  "keywords": ["python", "data engineer"],
  "locations": ["Europe"],
  "postedWithinDays": 7,
  "monitorMode": "onlyNew"
}
```

#### Sample output

```json
{
  "jobId": "himalayas:momsmeals/jobs/senior-data-analyst-finance",
  "source": "himalayas",
  "sourceJobId": "momsmeals/jobs/senior-data-analyst-finance",
  "title": "Senior Data Analyst - Finance",
  "company": "Mom's Meals",
  "companyLogo": null,
  "companyUrl": "https://himalayas.app/companies/momsmeals",
  "companyHeadquarters": null,
  "url": "https://himalayas.app/companies/momsmeals/jobs/senior-data-analyst-finance",
  "applyUrl": "https://himalayas.app/companies/momsmeals/jobs/senior-data-analyst-finance?utm_source=api",
  "postedAt": "2026-10-21T21:21:21.000Z",
  "expiresAt": "2026-12-20T21:21:19.000Z",
  "employmentType": "full-time",
  "seniority": "Senior",
  "category": "Data Science, Finance",
  "tags": [
    "data analyst",
    "financial data analyst",
    "business intelligence analyst",
    "analytics engineer"
  ],
  "isRemote": true,
  "locationText": "United States",
  "locationRestrictions": [
    "United States"
  ],
  "isWorldwide": false,
  "timezoneRestrictions": [
    -10,
    -9,
    -8,
    -7,
    -6,
    -5,
    14
  ],
  "salaryMin": 101700,
  "salaryMax": 113000,
  "salaryCurrency": "USD",
  "salaryPeriod": "annual",
  "salaryText": null,
  "descriptionText": "The Senior Data Analyst turns complex data into clear, actionable insights that drive financial and operational outcomes. In this role, you will develop scalabl…",
  "scrapedAt": "2026-10-21T23:02:58.409Z"
}
```

### Why this remote jobs scraper?

- **7 boards, one schema** – no more stitching together single-board scrapers. Every job has the same fields whatever the source.
- **Cross-board dedupe** – the same job posted on several boards is returned once (and not charged twice).
- **Location eligibility done right** – `locationRestrictions`, `isWorldwide` and `timezoneRestrictions` so you can filter "open to Europe" or "anywhere".
- **Salary normalized** – `salaryMin`, `salaryMax`, `salaryCurrency`, `salaryPeriod` (parsed from text when a board only gives a string) + `minSalaryUsd` filter.
- **New-job alerts** – Monitor mode returns only jobs you have not seen before; skipped jobs are free. Pair with the summary webhook for Slack/email alerts.
- **Cheap** – $1.49 per 1,000 jobs, 25% below the leading multi-board aggregator.
- **No personal data** – recruiter names, emails and phone numbers are never collected.

### Use cases

- Remote job boards, newsletters and Telegram/Slack/Discord job bots
- Job-seeker alerts ("new remote Python jobs open to Europe, $100k+")
- Hiring-signal & sales intelligence: which companies are hiring remotely, for which roles
- Salary benchmarking for remote roles
- Training and grounding data for AI career agents

### Input

| Field | Description |
|---|---|
| `sources` | Boards to aggregate (default all 7). |
| `keywords` / `excludeKeywords` | Include/exclude by words in title, company, tags, category. Keywords also drive search on Himalayas and Jobicy for deeper results. |
| `companies` | Only these companies. |
| `locations`, `worldwideOnly` | Candidate-location eligibility (worldwide jobs are kept unless `worldwideOnly`). |
| `employmentTypes` | full-time, part-time, contract, internship… |
| `onlyWithSalary`, `minSalaryUsd` | Salary filters (min salary is annualized, USD jobs only). |
| `postedWithinDays` | Only recent jobs. |
| `dedupeAcrossBoards` | Remove the same company + title across boards (default on). |
| `includeDescription` | Description text + HTML (default on). |
| `maxJobsPerBoard`, `maxResultsTotal` | Limits (0 = all available). |
| `monitorMode`, `monitorKey`, `outputFormat`, `summaryWebhookUrl` | Monitoring & output options (see below). |

### Output

One row per job: `jobId` (board:id), `source`, `title`, `company`, `companyLogo`, `companyUrl`, `companyHeadquarters`, `url`, `applyUrl`, `postedAt`, `expiresAt`, `employmentType`, `seniority`, `category`, `tags`, `isRemote`, `locationText`, `locationRestrictions`, `isWorldwide`, `timezoneRestrictions`, `salaryMin`, `salaryMax`, `salaryCurrency`, `salaryPeriod`, `salaryText`, `descriptionText`, `descriptionHtml`, `scrapedAt`. Not every board publishes every field (e.g. Working Nomads has no salary) – missing values are `null`. A run summary (per-board counts) is saved as `SUMMARY` in the key-value store.

### ⭐ Unique: Monitor mode, CSV-ready output & webhook summary

- **Monitor mode** remembers jobs between runs (per `monitorKey`): `all` adds `changeType` and a field diff (e.g. salary edited); `changes` outputs only new or changed jobs; `onlyNew` outputs only brand-new jobs. Skipped jobs are **not charged**.
- **Flat output** turns lists into `a | b | c` columns – perfect for Google Sheets.
- **Summary webhook** POSTs counts and new-job highlights to Zapier / Make / n8n / Slack at the end of every run.

Tip: schedule hourly with `monitorMode: "onlyNew"` to run your own remote-jobs feed for a few cents a day.

### 💰 Pricing (pay per result)

| Event | Price |
|---|---|
| Run start | $0.00005 |
| Job | **$1.49 / 1,000** |

Filtered-out, duplicate and monitor-skipped jobs are free. A full run of all 7 boards (≈2,800 unique jobs) costs about $4.20; with Monitor mode `onlyNew`, repeat runs cost only for new jobs.

### Notes & limitations

- Each board's public feed has its own depth: RemoteOK ≈100 newest, We Work Remotely ≈500 (all category feeds), Remotive ≈10 (their public API is delayed and limited), Working Nomads ≈40, Arbeitnow remote-only subset, Jobicy up to thousands, Himalayas 100k+ (newest ~1,000 per run or keyword search).
- The boards ask for attribution: when you republish jobs, link back to `url` and keep the `applyUrl`. Remotive does not allow republishing its jobs on Google Jobs, LinkedIn, Jooble etc.

### Legal & privacy

Only public job postings from the boards' official public APIs/feeds are collected. No recruiter or candidate personal data is collected. Respect each board's API terms (attribution, no excessive polling).

### 🔌 Integrations & API

Apify API, JavaScript/Python clients, Zapier, Make, n8n, Google Sheets, Slack, webhooks, and as an MCP tool for AI agents.

### ❓ More FAQ

**Do I need API keys?** No.
**Can I get only new jobs?** Yes – Monitor mode `onlyNew` (old jobs are free).
**Can I add Greenhouse / Lever / Ashby company boards?** Use our ATS Jobs Scraper for company career pages.

### Changelog

- 1.0 – Initial release: 7 boards, normalized salary/location, cross-board dedupe, monitor mode, flat output, webhook summary.

# Actor input Schema

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

Boards to aggregate. All 7 by default.

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

Keep jobs whose title, company, tags or category contain any of these (case-insensitive). Also used as search terms on Himalayas and Jobicy for deeper results. Empty = all jobs.

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

Drop jobs whose title or company contains any of these, e.g. "senior", "crypto".

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

Keep only these companies (substring match).

## `locations` (type: `array`):

Keep jobs open to candidates in any of these places, e.g. "USA", "Europe", "Germany", "LATAM". Worldwide jobs are kept too unless 'Worldwide only' is on.

## `worldwideOnly` (type: `boolean`):

Keep only jobs open to candidates anywhere in the world.

## `employmentTypes` (type: `array`):

e.g. full-time, part-time, contract, internship. Empty = all.

## `onlyWithSalary` (type: `boolean`):

Keep only jobs that publish a salary or salary range.

## `minSalaryUsd` (type: `integer`):

Keep USD jobs whose (max) salary, annualized, is at least this. 0 = off.

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

Only jobs posted in the last N days. 0 = any.

## `dedupeAcrossBoards` (type: `boolean`):

The same job (same company + title) posted on several boards is output once (duplicates are free).

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

Adds descriptionText and descriptionHtml. Turn off for smaller output.

## `maxJobsPerBoard` (type: `integer`):

0 = all available (Himalayas' 100k+ feed is capped at the newest ~1,000 per run; use keywords to go deeper).

## `maxResultsTotal` (type: `integer`):

0 = no overall limit.

## `monitorMode` (type: `string`):

Remembers items from previous runs (per Monitor key) and adds changeType = new/changed/unchanged plus a field-level diff. In the two 'only' modes, unchanged/old items are not output and not charged.

## `monitorKey` (type: `string`):

Name of the memory used by Monitor mode. Use a different key per schedule/task (e.g. 'competitor-a-daily'), so separate watchlists don't mix.

## `outputFormat` (type: `string`):

Flat makes CSV/Excel/Google Sheets exports one clean column per field.

## `summaryWebhookUrl` (type: `string`):

At the end of the run, POST a compact JSON summary (counts, new/changed, price drops, dataset ID) to this URL – e.g. Zapier/Make/n8n catch hook or Slack workflow. Free.

## Actor input object example

```json
{
  "sources": [
    "remoteok",
    "weworkremotely",
    "himalayas",
    "jobicy",
    "remotive",
    "workingnomads",
    "arbeitnow"
  ],
  "keywords": [
    "python",
    "data engineer"
  ],
  "worldwideOnly": false,
  "onlyWithSalary": false,
  "minSalaryUsd": 0,
  "postedWithinDays": 0,
  "dedupeAcrossBoards": true,
  "includeDescription": true,
  "maxJobsPerBoard": 200,
  "maxResultsTotal": 0,
  "monitorMode": "off",
  "monitorKey": "default",
  "outputFormat": "full"
}
```

# Actor output Schema

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

All scraped items in the default dataset

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

No description

## `summary` (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 = {
    "sources": [
        "remoteok",
        "weworkremotely",
        "himalayas",
        "jobicy",
        "remotive",
        "workingnomads",
        "arbeitnow"
    ],
    "keywords": [
        "python",
        "data engineer"
    ],
    "maxJobsPerBoard": 200
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapeworkshq/remote-jobs-scraper").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 = {
    "sources": [
        "remoteok",
        "weworkremotely",
        "himalayas",
        "jobicy",
        "remotive",
        "workingnomads",
        "arbeitnow",
    ],
    "keywords": [
        "python",
        "data engineer",
    ],
    "maxJobsPerBoard": 200,
}

# Run the Actor and wait for it to finish
run = client.actor("scrapeworkshq/remote-jobs-scraper").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 '{
  "sources": [
    "remoteok",
    "weworkremotely",
    "himalayas",
    "jobicy",
    "remotive",
    "workingnomads",
    "arbeitnow"
  ],
  "keywords": [
    "python",
    "data engineer"
  ],
  "maxJobsPerBoard": 200
}' |
apify call scrapeworkshq/remote-jobs-scraper --silent --output-dataset

```

## MCP server setup

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

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/Ks4hX2vdL0F6utsYw/builds/4IDp2PpgiyAwOJmBT/openapi.json
