# Remote Jobs Scraper (`automation-lab/remote-jobs-multi-board-feed`) Actor

Collect, filter, normalize, source-attribute, and deduplicate current remote jobs from Remote OK and Remotive in one automation-ready feed.

- **URL**: https://apify.com/automation-lab/remote-jobs-multi-board-feed.md
- **Developed by:** [Stas Persiianenko](https://apify.com/automation-lab) (community)
- **Categories:** Jobs
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.48 / 1,000 item extracteds

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 Scraper

Collect remote jobs from Remote OK and Remotive in one normalized,
source-attributed feed.

The Actor fetches both public job APIs, applies the same filters to every record,
deduplicates matching company/title/location combinations, and exports data that
is ready for recruiting research, hiring-market monitoring, spreadsheets, or a
data warehouse.

It does not require a login, browser, or proxy.

### What does Remote Jobs Scraper do?

Remote Jobs Scraper turns two different public job-board response formats into
one stable dataset.

In one run, it can:

- collect current jobs from Remote OK;
- collect current jobs from Remotive;
- search titles, companies, locations, categories, and tags;
- filter by candidate location or geographic eligibility;
- filter by publication date;
- normalize salary, job type, category, tags, dates, and URLs;
- deduplicate matching jobs across boards;
- preserve every source ID and source endpoint on merged records;
- sort jobs newest first and limit output to a predictable number of results.

The default dataset contains one row per accepted job, ordered by publication time with undated jobs last.

### Who is it for?

#### Recruiters and talent teams

Monitor which companies are hiring remotely and build focused role lists without
manually checking two boards.

#### Labor-market analysts

Compare job titles, locations, salary signals, skills, and publication dates in a
consistent tabular format.

#### Job-search products

Feed public listings into an internal search index, newsletter workflow, or
candidate-matching pipeline while retaining source attribution.

#### Data and automation teams

Schedule recurring runs and send the dataset to Google Sheets, Airtable,
Make, Zapier, webhooks, cloud storage, or a warehouse.

### Why use a multi-board remote jobs scraper?

Remote OK and Remotive expose useful public records, but their field names and
shapes differ.

This Actor provides:

- **one schema** across both sources;
- **one set of filters** applied after normalization;
- **explicit attribution** through `source`, `sources`, source IDs, and source URLs;
- **cross-board deduplication** without hiding where a record came from;
- **fail-closed behavior** when a selected source changes shape or becomes unavailable;
- **lightweight execution** through public JSON APIs rather than a browser.

Choose a single-board Actor when you need source-specific features not included
here. Choose this Actor when the unified feed is the product you need.

### What remote job data can I extract?

| Field | Meaning |
| --- | --- |
| `recordId` | Stable source-qualified record ID |
| `source` | Primary board: `remoteok` or `remotive` |
| `sources` | Every board attributed to a merged record |
| `sourceJobId` | Primary source's job ID |
| `sourceJobIds` | All source IDs represented by the row |
| `sourceUrl` | Public API endpoint for the primary source |
| `sourceUrls` | All represented public source endpoints |
| `title` | Job title |
| `company` | Hiring company |
| `location` | Candidate location or eligibility text |
| `salary` | Source salary text or normalized salary range |
| `salaryMin`, `salaryMax` | Numeric bounds when Remote OK supplies them |
| `employmentType` | Employment type when available |
| `category` | Source category when available |
| `tags` | Skills and job tags |
| `publishedAt` | Normalized ISO publication time |
| `description` | Source description, which can contain HTML |
| `applyUrl` | Direct apply link or canonical job link |
| `jobUrl` | Canonical public listing URL |
| `dedupeKey` | Normalized company/title/location key |
| `scrapedAt` | Time this Actor collected the record |

Fields unavailable from a source are returned as `null` rather than guessed.

### How to scrape remote jobs

1. Open the Actor in Apify Console.
2. Keep both job boards selected for a unified feed, or choose one source.
3. Optionally enter keywords such as `software engineer`.
4. Optionally add a location or publication-date filter.
5. Choose the maximum number of jobs.
6. Keep deduplication enabled for monitoring workflows.
7. Click **Start**.
8. Open the **Remote jobs** dataset view.
9. Download JSON, CSV, Excel, XML, or RSS, or connect an integration.

A useful first run is:

```json
{
  "sources": ["remoteok", "remotive"],
  "query": "software engineer",
  "maxItems": 10,
  "deduplicate": true
}
```

### Input parameters

| Input | Type | Default | Description |
| --- | --- | --- | --- |
| `sources` | string array | both boards | One or both of `remoteok`, `remotive` |
| `query` | string | empty | All words must match normalized searchable fields |
| `location` | string | empty | Case-insensitive location/eligibility substring |
| `publishedAfter` | ISO date-time | empty | Excludes older and undated jobs |
| `deduplicate` | boolean | `true` | Merges matching company/title/location records |
| `maxItems` | integer | `100` | Saves 1–500 accepted jobs |

`query` uses AND semantics: every entered word must be present somewhere across
the title, company, location, category, or tags.

For example, `software engineer` requires both words but does not require them
to be adjacent.

### Output example

A current run returns rows shaped like this:

```json
{
  "recordId": "remotive:2090000",
  "source": "remotive",
  "sources": ["remotive"],
  "sourceJobId": "2090000",
  "sourceJobIds": ["2090000"],
  "sourceUrl": "https://remotive.com/api/remote-jobs",
  "sourceUrls": ["https://remotive.com/api/remote-jobs"],
  "title": "Senior Data Engineer",
  "company": "Sample Technology Company",
  "location": "Worldwide",
  "salary": null,
  "salaryMin": null,
  "salaryMax": null,
  "employmentType": "full_time",
  "category": "Software Development",
  "tags": ["python", "data"],
  "publishedAt": "2026-08-20T12:00:00.000Z",
  "description": "<p>Public job description...</p>",
  "applyUrl": "https://remotive.com/remote-jobs/software-dev/senior-data-engineer-2090000",
  "jobUrl": "https://remotive.com/remote-jobs/software-dev/senior-data-engineer-2090000",
  "dedupeKey": "sample technology company|senior data engineer|worldwide",
  "scrapedAt": "2026-08-25T14:00:00.000Z"
}
```

The exact records change as the boards publish and remove jobs.

### How deduplication works

When `deduplicate` is enabled, the Actor normalizes:

1. company name;
2. job title;
3. location or eligibility.

Records with the same normalized combination are merged.

The first record remains the primary record, while `sources`, `sourceJobIds`,
and `sourceUrls` retain all represented attribution.

This conservative key avoids claiming two loosely similar roles are identical.
It can leave near-duplicates when boards use materially different titles or
location wording.

Disable deduplication if you need one row per source listing.

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

The Actor uses pay-per-event pricing:

- one `start` event per run;
- one `item` event per saved dataset row;
- no charge for rejected, duplicate, or empty records.

The run-start price is $0.005. Per-item prices decrease by Apify plan:

| Plan | Price per saved job |
| --- | ---: |
| Free | $0.00667 |
| Bronze | $0.0058 |
| Silver | $0.004524 |
| Gold | $0.00348 |
| Platinum | $0.00232 |
| Diamond | $0.001624 |

Example Bronze-plan totals:

| Saved jobs | Estimated Actor charge |
| ---: | ---: |
| 10 | $0.063 |
| 50 | $0.295 |
| 100 | $0.585 |

Platform compute is included under Apify's pay-per-event model for this Actor.
The final charge is shown before and after each run in Apify Console.

### Scheduling remote hiring monitoring

Use an Apify schedule for repeat monitoring:

1. save a Task with both sources selected;
2. choose filters and a bounded `maxItems`;
3. schedule the Task hourly, daily, or weekly;
4. connect a webhook or integration;
5. compare `recordId`, `sourceJobIds`, and `publishedAt` with your stored snapshot.

The Actor returns the current public feed. It does not maintain history or send
change alerts by itself; your scheduled workflow owns comparison and retention.

### Integration ideas

- Append newly observed jobs to Google Sheets.
- Send matching roles to Slack through a webhook.
- Load normalized records into BigQuery or Snowflake.
- Build a remote-hiring company watchlist.
- Feed jobs into a search or recommendation index.
- Track title, location, and salary trends over time.
- Trigger enrichment only for unseen `recordId` values.

Use `sources` and `sourceUrls` whenever downstream attribution matters.

### Run with the Apify API

Replace `APIFY_TOKEN` with your token.

#### cURL

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/automation-lab~remote-jobs-multi-board-feed/runs?token=APIFY_TOKEN&waitForFinish=120" \
  -H "Content-Type: application/json" \
  -d '{"sources":["remoteok","remotive"],"query":"software engineer","maxItems":10}'
```

#### JavaScript

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

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('automation-lab/remote-jobs-multi-board-feed').call({
  sources: ['remoteok', 'remotive'],
  query: 'software engineer',
  maxItems: 10,
  deduplicate: true,
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

#### Python

```python
from apify_client import ApifyClient

client = ApifyClient("APIFY_TOKEN")
run = client.actor("automation-lab/remote-jobs-multi-board-feed").call(
    run_input={
        "sources": ["remoteok", "remotive"],
        "query": "software engineer",
        "maxItems": 10,
        "deduplicate": True,
    }
)
items = client.dataset(run["defaultDatasetId"]).list_items().items
print(items)
```

### Use with MCP and AI agents

Add the Apify MCP server to Claude Code:

```bash
claude mcp add --transport http apify \
  "https://mcp.apify.com?tools=automation-lab/remote-jobs-multi-board-feed"
```

#### Claude Desktop, Cursor, and VS Code MCP JSON

Use this configuration in Claude Desktop, Cursor, or VS Code:

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com?tools=automation-lab/remote-jobs-multi-board-feed"
    }
  }
}
```

Example prompts:

- “Run Remote Jobs Scraper for software engineer jobs across both boards.”
- “Collect 20 Remote OK jobs and summarize the companies and locations.”
- “Create a deduplicated remote hiring feed and group jobs by source.”

Your MCP client may request Apify authentication before the first run.

### Limits and responsible operation

- Each run returns current records exposed by the two public APIs.
- `maxItems` is limited to 500.
- Source APIs can change, rate-limit, or temporarily fail.
- A selected source failure fails the run rather than returning a silent partial feed.
- Transient network, HTTP 429, and temporary server failures are retried three times.
- The Actor uses direct HTTP and does not expose a proxy mode.
- Descriptions can contain HTML supplied by the source.
- Salary formats differ between boards; unavailable numeric bounds remain null.
- Deduplication is exact after conservative text normalization, not semantic matching.

Keep scheduled frequency reasonable and avoid unnecessary repeat runs.

### Legal and responsible use

Remote OK and Remotive make these job records publicly accessible, but public
access does not remove your responsibilities.

You should:

- follow the source sites' terms and applicable policies;
- respect database, copyright, and employment laws in your jurisdiction;
- retain source attribution and canonical links;
- avoid republishing descriptions beyond your legal rights;
- verify a job on its source page before acting on it;
- avoid automated applications or unsolicited outreach that violates law or policy;
- delete retained records when you no longer have a lawful purpose.

This Actor provides technical access to public data, not legal advice.

### Troubleshooting

#### The run returned no jobs

Remove filters one at a time. A multi-word query requires every word to match,
and `publishedAfter` excludes jobs without a usable publication date.

#### A selected board failed

Inspect the run log for the source name and HTTP or response-shape error. Retry
later if the public API is temporarily unavailable. Do not add a proxy: this
Actor intentionally uses the public API route.

#### I see fewer than `maxItems`

`maxItems` is a ceiling, not a guaranteed count. Filters, source feed size, and
deduplication can all reduce the result.

#### Salary bounds are null

Remotive generally supplies free-form salary text. Numeric `salaryMin` and
`salaryMax` are populated only when the source provides reliable numeric bounds.

### Related Automation Lab Actors

- [Jobgether Remote Jobs Scraper](https://apify.com/automation-lab/jobgether-remote-jobs-scraper) for Jobgether-specific remote listings.
- [Working Nomads Jobs Scraper](https://apify.com/automation-lab/working-nomads-jobs-scraper) for Working Nomads categories and listings.
- [Remote Rocketship Jobs Scraper](https://apify.com/automation-lab/remote-rocketship-jobs-scraper) for Remote Rocketship-specific data.

These are separate source products. They are not automatically included in this
Actor's two-board feed.

### FAQ

#### Does this Actor scrape every remote job board?

No. It supports exactly Remote OK and Remotive.

#### Does it need an API key for either board?

No. It uses their public JSON endpoints.

#### Does it use residential proxies or a browser?

No. Direct public API requests are sufficient and cheaper.

#### Can it find remote jobs near me?

Use `location` with the geography or eligibility wording you need. Remote jobs
are often listed as Worldwide, a country, or a region rather than a nearby city,
so results depend on how employers describe eligibility.

#### Can it find no-experience jobs automatically?

You can search `query` fields for relevant words, but the Actor does not infer
experience requirements from unstructured descriptions. Review the source job
before treating it as entry-level.

#### Can I monitor new or changed jobs?

Yes: schedule repeated Tasks and compare stable IDs in your own storage. The
Actor itself returns the current feed and does not persist historical snapshots.

#### Can I export CSV or Excel?

Yes. Use the dataset export controls in Apify Console or the dataset API.

#### Are duplicates guaranteed to be removed?

Exact normalized company/title/location matches are merged. Near-duplicates
with different wording can remain so the Actor does not hide distinct roles.

# Actor input Schema

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

Public job boards to include. Select both for a unified multi-board feed.

## `query` (type: `string`):

Optional words that must appear across the title, company, location, category, or tags.

## `location` (type: `string`):

Optional case-insensitive text filter for candidate location or geographic eligibility, such as Worldwide or United States.

## `publishedAfter` (type: `string`):

Optional ISO 8601 date-time. Jobs without a valid publication date are excluded when this filter is set.

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

Merge matching company, title, and location combinations across the selected boards while retaining all source attributions.

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

Maximum number of normalized jobs saved to the dataset after filtering and deduplication.

## Actor input object example

```json
{
  "sources": [
    "remoteok",
    "remotive"
  ],
  "query": "software engineer",
  "deduplicate": true,
  "maxItems": 20
}
```

# Actor output Schema

## `dataset` (type: `string`):

Dataset containing the filtered and deduplicated remote job feed.

# 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"
    ],
    "query": "software engineer",
    "deduplicate": true,
    "maxItems": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation-lab/remote-jobs-multi-board-feed").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",
    ],
    "query": "software engineer",
    "deduplicate": True,
    "maxItems": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("automation-lab/remote-jobs-multi-board-feed").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"
  ],
  "query": "software engineer",
  "deduplicate": true,
  "maxItems": 20
}' |
apify call automation-lab/remote-jobs-multi-board-feed --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automation-lab/remote-jobs-multi-board-feed"
        }
    }
}

```

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/c4dNbmIsjDQgO3Sgo/builds/ashyEyVo8B9YLSrS6/openapi.json
