# Remote Jobs Scraper - RemoteOK, Remotive & Himalayas (`borasipahi/remote-jobs-scraper`) Actor

One deduplicated feed of remote jobs from RemoteOK, Remotive and Himalayas. USD-normalised salaries, contact details stripped, incremental mode. Pay per job row.

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

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

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

## 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

One run, three remote job boards, one clean feed. Remote Jobs Scraper pulls the public APIs of **RemoteOK**, **Remotive** and **Himalayas**, removes the same job posted on more than one board, normalises salaries to USD per year, strips recruiter e-mail addresses and phone numbers, and gives you a single JSON or CSV dataset you can filter by keyword, category, seniority, employment type, salary and location.

It is priced **per job row**, lower than any single-board scraper on the Store, and it needs no proxies, no browser and no login, so runs finish in seconds.

### What you get

Each dataset row is one job with the same field names regardless of the board it came from:

| Field | Meaning |
| --- | --- |
| `source`, `sourceUrl`, `sourceAttribution` | Which board, the listing URL, and the attribution text the board requires |
| `jobId` | Stable id such as `remoteok:1137307` |
| `title`, `companyName`, `companySlug`, `companyLogo` | Basics |
| `categories[]`, `parentCategories[]`, `tags[]` | The board's own taxonomy |
| `seniority` | `intern`, `junior`, `mid`, `senior`, `lead`, `manager`, `director`, `executive` or `null` |
| `employmentType` | `full-time`, `part-time`, `contract`, `internship` or `null` |
| `minSalary`, `maxSalary`, `currency`, `salaryPeriod` | As published |
| `salaryUsdAnnualised` | The published range converted to USD per year so you can compare boards |
| `locationRestrictions[]`, `timezoneRestrictions[]` | Where the employer will hire |
| `descriptionHtml`, `descriptionText`, `excerpt` | Full description, plain text version, short summary |
| `applicationLink`, `publishedAt`, `expiresAt` | How and when |
| `alsoOn[]` | Other boards where the same job appears (when deduplication is on) |
| `scrapedAt` | Run timestamp |

### How to use it

1. Open the Actor and press **Start**. The defaults return up to 100 jobs from the last 7 days across all three boards.
2. To narrow the feed, set **Search query** (for example `react, node`), pick a **Seniority** or **Employment type**, or set **Minimum annual salary (USD)**.
3. Download the dataset as JSON, CSV or Excel, or read it through the API with the link on the **Output** tab.
4. For a daily digest of only new jobs, turn on **Incremental mode** and add a **Schedule**. Every job already delivered is remembered in a named key-value store and skipped next time.

The Actor can also be called from the Apify API, the Apify MCP server or any AI agent that supports agentic payments.

### Pricing

You pay for one event, `job-listing`, once per job row saved to the dataset. Nothing is charged for jobs that were filtered out, deduplicated or already delivered in incremental mode. Set **Maximum jobs** to cap the cost of a run. A full default run of 100 jobs costs under ten cents; see the Pricing tab for the exact per-row price and volume tiers.

There is no separate charge for the full description and no platform usage is passed on to you.

### Input example

```json
{
    "sources": ["remoteok", "remotive", "himalayas"],
    "searchQuery": "typescript, node",
    "seniority": ["senior", "lead"],
    "employmentType": ["full-time"],
    "minSalary": 120000,
    "postedWithinDays": 14,
    "maxItems": 200,
    "deduplicate": true,
    "stripContactDetails": true,
    "incremental": false
}
```

### Output example

```json
{
    "source": "himalayas",
    "sourceUrl": "https://himalayas.app/companies/oura-health-ltd/jobs/senior-product-manager-new-experiences",
    "sourceAttribution": "Data sourced from Himalayas (https://himalayas.app)",
    "jobId": "himalayas:oura-health-ltd/jobs/senior-product-manager-new-experiences",
    "title": "Senior Product Manager, New Experiences",
    "companyName": "Oura Health Ltd",
    "seniority": "senior",
    "employmentType": "full-time",
    "minSalary": 147900,
    "maxSalary": 203000,
    "currency": "USD",
    "salaryPeriod": "annual",
    "salaryUsdAnnualised": 203000,
    "locationRestrictions": ["United States"],
    "publishedAt": "2026-09-06T04:24:11.000Z",
    "alsoOn": [],
    "scrapedAt": "2026-09-07T00:30:00.000Z"
}
```

### Where the data comes from, and the rules that come with it

- **RemoteOK** asks that you link back to the listing on remoteok.com with a normal (follow) link and name Remote OK as the source. The Remote OK logo is a trademark and must not be reused. The `sourceAttribution` field carries the wording.
- **Remotive** asks for a link back to the listing and a mention of Remotive as the source, and delays its public feed by 24 hours. Remotive also asks that its jobs are not resubmitted to other job sites or used to collect sign-ups.
- **Himalayas** asks for a visible link back to himalayas.app and a mention that the data is sourced from Himalayas. Its feed is cached daily, so running more than once a day gives you nothing new.
- **We Work Remotely** is deliberately not included. Its API terms prohibit building a job search or job advertising service on the data.

Only company job postings are collected. Contact details that recruiters paste into descriptions are removed by default. Candidate profiles and any personal data are never touched.

### Related Actors

- [Himalayas Remote Jobs](https://apify.com/himalayas/remote-jobs) is the official single-board Actor from Himalayas itself. Use it when you want Himalayas only and every field exactly as they publish it.
- Pair this Actor with an e-mail or Slack integration to get a daily digest of new matching jobs.

### FAQ

**Why do I get fewer jobs than Maximum jobs?** The boards publish a limited number of new jobs per day and the default only looks back 7 days. Raise **Posted within (days)** or clear the filters.

**Why are some salaries null?** Most postings do not publish a salary. When a board gives one, it is converted to USD per year with approximate exchange rates for comparison only.

**Is a duplicate charged twice?** No. With deduplication on, one copy is saved and charged and the other boards are listed in `alsoOn`.

**Can I run it every hour?** You can, but RemoteOK and Remotive publish a handful of jobs a day and Himalayas refreshes daily. Once or twice a day with incremental mode is the sensible setting.

**Something broke?** Open an issue on the Actor's Issues tab. The Actor is tested against all three boards several times a day and fixed when a board changes.

# Actor input Schema

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

Job boards to pull from. All three are public JSON APIs; no login is used.

## `searchQuery` (type: `string`):

Case-insensitive keywords matched against title, company, tags and description. Separate alternatives with a comma, e.g. "react, vue".

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

Keep only jobs whose category or tags contain any of these words, e.g. \["engineering", "design", "sales"].

## `seniority` (type: `array`):

Keep only jobs at these levels. Inferred from the title and from the board's own field when it has one.

## `employmentType` (type: `array`):

Keep only these employment types.

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

Keep only jobs whose USD-annualised salary is at least this. Jobs with no salary are dropped when this is set.

## `locationRestrictions` (type: `array`):

Keep only jobs open to these regions or countries, matched as substrings, e.g. \["Worldwide", "Europe", "United States"]. Jobs with no restriction are always kept.

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

Keep only jobs published in the last N days. Himalayas has 100k+ historic jobs; this bounds the crawl.

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

Stop after this many jobs have been saved. Bounds your cost.

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

The same job is often posted on all three boards. When on, later copies are not saved and the saved copy lists them in alsoOn, so you pay once per job.

## `stripContactDetails` (type: `boolean`):

Remove e-mail addresses and phone numbers that recruiters paste into descriptions. Keeps the output free of personal data.

## `includeFullDescription` (type: `boolean`):

Include descriptionHtml and descriptionText. Off gives a compact feed with excerpt only. No extra charge either way.

## `incremental` (type: `boolean`):

Remember every job already delivered (in a named key-value store) and skip it on the next run. Use with a schedule to get only new jobs.

## `incrementalStoreName` (type: `string`):

Name of the key-value store that holds seen job ids for incremental mode.

## Actor input object example

```json
{
  "sources": [
    "remoteok",
    "remotive",
    "himalayas"
  ],
  "categories": [],
  "seniority": [],
  "employmentType": [],
  "locationRestrictions": [],
  "postedWithinDays": 7,
  "maxItems": 100,
  "deduplicate": true,
  "stripContactDetails": true,
  "includeFullDescription": true,
  "incremental": false,
  "incrementalStoreName": "remote-jobs-scraper-seen"
}
```

# Actor output Schema

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

No description

## `jobsCsv` (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",
        "remotive",
        "himalayas"
    ],
    "postedWithinDays": 7,
    "maxItems": 100
};

// Run the Actor and wait for it to finish
const run = await client.actor("borasipahi/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",
        "remotive",
        "himalayas",
    ],
    "postedWithinDays": 7,
    "maxItems": 100,
}

# Run the Actor and wait for it to finish
run = client.actor("borasipahi/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",
    "remotive",
    "himalayas"
  ],
  "postedWithinDays": 7,
  "maxItems": 100
}' |
apify call borasipahi/remote-jobs-scraper --silent --output-dataset

```

## MCP server setup

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