# Remote Job Aggregator - Remotive, Himalayas, Arbeitnow, WWR (`thedeadpoet/remote-job-feed-aggregator`) Actor

Search five official public remote-job feeds at once (Remotive, Himalayas, Arbeitnow, We Work Remotely, Hacker News Who Is Hiring), deduplicated into one normalised schema.

- **URL**: https://apify.com/thedeadpoet/remote-job-feed-aggregator.md
- **Developed by:** [Pablo D](https://apify.com/thedeadpoet) (community)
- **Categories:** Jobs, Automation
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 job listing returneds

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?

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 Job Feed Aggregator — Remotive, Himalayas, Arbeitnow, We Work Remotely & HN

One search across **five public remote-job feeds**, deduplicated into a single schema. No API keys, no proxies, no login.

Running five separate scrapers and merging their output by hand is the usual way to do this. This Actor does the merging — including collapsing the same job when two boards both carry it.

### What it does

| | |
|---|---|
| **Input** | Optional keywords, location and date filters |
| **Output** | One row per job, identical fields across every feed |
| **Sources** | Remotive · Himalayas · Arbeitnow · We Work Remotely · Hacker News "Who is hiring" |
| **Speed** | 4 feeds × 3 pages finishes in well under a minute |

### Use cases

- **Run a job board or newsletter** for a niche (Python, design, EU-timezone) without maintaining five integrations.
- **Job hunting at scale** — schedule a daily run with `postedWithinDays: 1` and get only what is new.
- **Labour-market research** — who is hiring remotely, for what, at what salary, over time.
- **Feed an LLM or a RAG index** with fresh, structured, permissively-sourced job text.

### Input

```json
{
  "keywords": ["python", "data engineer"],
  "sources": ["remotive", "himalayas", "arbeitnow", "weworkremotely"],
  "locationKeywords": ["Europe", "Worldwide"],
  "postedWithinDays": 14,
  "excludeKeywords": ["senior manager"],
  "includeDescription": false,
  "deduplicate": true,
  "maxItems": 200,
  "maxPagesPerSource": 3,
  "requestDelaySeconds": 1
}
```

Leave `keywords` empty to pull everything the feeds currently publish.

`hackernews` is off by default: it reads the current *Ask HN: Who is hiring?* thread, which is free-text rather than structured, so titles are the comment's first line and company names are best-effort.

### Output

```json
{
  "source": "himalayas",
  "jobId": "https://himalayas.app/companies/netomi/jobs/software-development-engineer-in-test-i",
  "title": "Software Development Engineer In Test - I",
  "companyName": "Netomi",
  "companyLogo": "https://cdn-images.himalayas.app/...",
  "url": "https://himalayas.app/companies/netomi/jobs/...",
  "applyUrl": "https://himalayas.app/companies/netomi/jobs/...",
  "locationText": "India",
  "locationRestrictions": ["India"],
  "employmentType": "Full Time",
  "category": "Developer",
  "tags": ["SDET", "QA-Automation-Engineering"],
  "seniority": "Mid-level",
  "salaryText": null,
  "salaryMin": null,
  "salaryMax": null,
  "currency": null,
  "postedAt": "2026-09-09T14:12:17+00:00",
  "descriptionText": null,
  "isRemote": true,
  "alsoSeenOn": ["remotive"],
  "scrapedAt": "2026-09-10T10:31:02.884000+00:00"
}
```

`alsoSeenOn` lists the other feeds that carried the same job when `deduplicate` is on. `RUN_SUMMARY` in the key-value store records how many rows each feed returned and any feed that failed.

### Honest limitations

- **Field coverage differs by feed.** Salary comes mostly from Himalayas; `employmentType` is missing from We Work Remotely; Hacker News has no structured fields at all. Fields a feed does not publish are `null`, never guessed.
- **Keyword filtering is client-side.** The feeds are fetched first and filtered after, so a narrow keyword with a low `maxPagesPerSource` can return very little. Raise `maxPagesPerSource` before you conclude there are no matches.
- **Deduplication is heuristic** — normalised company + title. Two genuinely different roles with the same title at the same company will collapse into one row; turn `deduplicate` off if that matters.
- **Remote-only by nature.** These are remote-job boards. Arbeitnow carries some on-site European roles (`isRemote` is `false` for those); the rest are remote by definition.
- **Feed depth is finite and varies a lot by feed.** Remotive returns its whole current list in one request — and that list is small (it was 17 jobs on 2026-09-10). Himalayas serves **20 jobs per page**, Arbeitnow 250, We Work Remotely one RSS file per category. So `maxPagesPerSource` buys you very different amounts of data per feed. This is a *current openings* tool, not a historical archive.
- **These feeds are free services.** They can rate-limit or change shape. The Actor retries with backoff and records feed errors in `RUN_SUMMARY` instead of failing the whole run.

### Sources and attribution

- **Remotive** — `remotive.com/api/remote-jobs`, the public API Remotive publishes for third parties. Jobs sourced from Remotive keep their original `url`; please credit remotive.com when you republish.
- **Himalayas** — `himalayas.app/jobs/api`, public JSON API with cursor pagination.
- **Arbeitnow** — `arbeitnow.com/api/job-board-api`, public job-board API.
- **We Work Remotely** — the public per-category RSS feeds.
- **Hacker News** — `hn.algolia.com/api/v1`, the official public HN search API.

`robots.txt` was checked for every host; none of these paths are disallowed. There is no login, captcha solving, proxy rotation or browser automation anywhere in this Actor.

### Changelog

See `CHANGELOG.md`.

### Support

Found a bug, or need a field this Actor does not return yet? Open an issue on the Actor's **Issues** tab, or email **pablodevigoalertas@gmail.com**. Issues are read first.

# Actor input Schema

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

Case-insensitive. A job is kept if its title, company, tags or excerpt contains at least one of these. Leave empty to keep every job the feeds return.

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

Which public feeds to query.

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

Hard cap on the number of rows pushed. Keeps a broad search from running away with your budget.

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

0 = no date filter. Jobs with no publication date are always kept.

## `locationKeywords` (type: `array`):

Case-insensitive match against the location / hiring-region text, e.g. "Europe", "Worldwide", "USA", "Germany".

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

Case-insensitive. Jobs whose title contains any of these are dropped.

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

Adds the description text to every job. Makes the dataset much larger.

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

The same job often appears on more than one board. When on, near-duplicates (same company + normalised title) are collapsed into one row and the extra sources are listed in alsoSeenOn.

## `maxPagesPerSource` (type: `integer`):

Each page is one HTTP request (100-250 jobs). Raise it to reach deeper into the feeds.

## `requestDelaySeconds` (type: `number`):

Politeness delay between requests to the same feed.

## Actor input object example

```json
{
  "keywords": [
    "python",
    "data engineer"
  ],
  "sources": [
    "remotive",
    "himalayas",
    "arbeitnow",
    "weworkremotely"
  ],
  "maxItems": 200,
  "postedWithinDays": 0,
  "locationKeywords": [],
  "excludeKeywords": [],
  "includeDescription": false,
  "deduplicate": true,
  "maxPagesPerSource": 3,
  "requestDelaySeconds": 1
}
```

# Actor output Schema

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

One row per deduplicated remote job, with alsoSeenOn listing every feed carrying it.

# 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",
        "data engineer"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("thedeadpoet/remote-job-feed-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",
        "data engineer",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("thedeadpoet/remote-job-feed-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",
    "data engineer"
  ]
}' |
apify call thedeadpoet/remote-job-feed-aggregator --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,thedeadpoet/remote-job-feed-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/QgxsZJZRIhCFoLJ5A/builds/4UbqL7Me43JrNQxB8/openapi.json
