# Remote Jobs API: Remotive, Jobicy, Himalayas, WWR, Remote OK (`ailu_studio/remote-jobs-api`) Actor

Aggregate remote job listings from Remotive, Jobicy, Himalayas, We Work Remotely and Remote OK into one deduplicated JSON feed with title, company, category, location, salary, dates and links. Public feeds, no API key.

- **URL**: https://apify.com/ailu_studio/remote-jobs-api.md
- **Developed by:** [Ailu Studio](https://apify.com/ailu_studio) (community)
- **Categories:** Jobs, Automation, Agents
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.50 / 1,000 result rows

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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 API: Remotive, Jobicy, Himalayas, WWR, Remote OK

Get the newest remote job listings from five public job feeds in one clean, deduplicated JSON table: Remotive, Jobicy, Himalayas, We Work Remotely (RSS) and Remote OK. Every row has the same fields (title, company, category, tags, employment type, location, salary, publish date, job URL), the same job found on several boards is merged into one row with the other boards listed in `alsoOn`, and the result is sorted newest first. No API key, no browser and no scraping of consumer sites: the Actor reads the sources' own public APIs and feeds and keeps their attribution (`url` and `sourceUrl` always point back to the source).

### Use cases

- Build a remote-job board, newsletter or Slack digest filtered by keyword, category or region.
- Feed fresh job postings into an AI agent that matches candidates or drafts outreach.
- Track hiring activity for a set of skills (`categories: ["python", "devops"]`) over time with a scheduled run.
- Collect salary ranges for remote roles (`salaryOnly: true`) for market research.
- Compare which boards a company posts on (`alsoOn`) and how fast new jobs appear per source.

### Input

| Field | Default | Meaning |
|---|---|---|
| `sources` | all five | Any of `remotive`, `jobicy`, `himalayas`, `weworkremotely`, `remoteok`. Each is fetched independently. |
| `keywords` | `[]` | Keep jobs whose title contains at least one of these (case-insensitive). Empty = all. |
| `categories` | `[]` | Keep jobs whose category, industry or tags contain one of these texts, e.g. `software`, `marketing`, `python`. Empty = all. |
| `locationContains` | `""` | Keep jobs whose location/region text contains this, e.g. `USA`, `Europe`, `Worldwide`. Jobs without location text are dropped when set. |
| `sinceDays` | `14` | Only jobs published within the last N days. `0` disables the date filter. |
| `includeDescription` | `false` | When true, `descriptionText` holds the plain-text description (max 3,000 characters). |
| `salaryOnly` | `false` | Keep only jobs with a salary figure or salary text. |
| `maxItems` | `200` | Maximum charged rows after merging and sorting; also limits how many pages are fetched from paginated sources. |

Example input:

```json
{ "keywords": ["engineer", "developer"], "locationContains": "worldwide", "sinceDays": 7, "salaryOnly": true, "maxItems": 100 }
```

### Output

One row per job after deduplication (lower-cased company + title across all selected sources; the earliest-published copy is kept).

| Field | Type | Meaning |
|---|---|---|
| `id` | string | Stable key `<source>:<id or slug>`, e.g. `remotive:2091165`, `himalayas:<company>/<job-slug>`. |
| `source` | string | `remotive`, `jobicy`, `himalayas`, `weworkremotely` or `remoteok`. |
| `title`, `company` | string | null | Job title and company name. |
| `companyLogoUrl` | string | null | Logo image URL when provided. |
| `category` | string | null | Source category or industry (naming differs per source; null for Remote OK). |
| `tags` | string\[] | Skill/topic tags from the source (empty array when none). |
| `employmentType` | string | null | `full_time`, `part_time`, `contract`, `freelance`, `internship`, `temporary` or the source value in snake_case. |
| `seniority` | string | null | Seniority when stated (Jobicy, Himalayas). |
| `location` | string | null | Allowed candidate location/region text, e.g. `Worldwide`, `USA Only`. |
| `salaryMin`, `salaryMax` | number | null | Salary bounds in `salaryCurrency` per `salaryInterval`. Parsed from text for Remotive. |
| `salaryCurrency` | string | null | ISO code such as `USD`, `EUR`, `GBP`. |
| `salaryInterval` | string | null | `year`, `month`, `week`, `day`, `hour` or null when the source does not say. |
| `salaryRaw` | string | null | Salary as the source expressed it. |
| `publishedAt` | ISO 8601 | null | Publish time in UTC. |
| `url` | string | null | Job page on the source site. |
| `applyUrl` | string | null | Application link when the source gives one (Himalayas, Remote OK). |
| `descriptionText` | string | null | Plain-text description (max 3,000 chars) when `includeDescription` is true. |
| `sourceUrl` | string | Homepage of the source. |
| `alsoOn` | string\[] | Other sources where the same company + title was found. |
| `fetchedAt` | ISO 8601 | When the row was fetched. |

Sample row:

```json
{
  "id": "remotive:2091165", "source": "remotive", "title": "Full-Stack Developer", "company": "Fluence International, Inc.",
  "companyLogoUrl": "https://remotive.com/job/2091165/logo", "category": "Software Development",
  "tags": ["api", "AWS", "backend", "docker", "javascript", "react"], "employmentType": "contract", "seniority": null,
  "location": "Worldwide", "salaryMin": 3500, "salaryMax": 5000, "salaryCurrency": "USD", "salaryInterval": "month", "salaryRaw": "$3.5k–$5k/month",
  "publishedAt": "2026-10-29T18:22:41.000Z", "url": "https://remotive.com/remote-jobs/software-development/full-stack-developer-2091165",
  "applyUrl": null, "descriptionText": null, "sourceUrl": "https://remotive.com", "alsoOn": [], "fetchedAt": "2026-11-03T15:10:00.000Z"
}
```

The run summary (rows fetched and matched per source, duplicates merged, failed sources, errors) is stored in the `OUTPUT` record of the default key-value store.

### How it works and limits

- Sources and endpoints: Remotive `remotive.com/api/remote-jobs` (JSON), Jobicy `jobicy.com/api/v2/remote-jobs` (JSON, 50 per page, cursor pagination), Himalayas `himalayas.app/jobs/api` (JSON, 20 per page, cursor pagination), We Work Remotely `weworkremotely.com/remote-jobs.rss` (RSS, newest 100 jobs), Remote OK `remoteok.com/api` (JSON, newest ~100 jobs). All are fetched in parallel, each host at most one request per 250 ms, with retries and backoff on 429/5xx.
- Pagination (Jobicy, Himalayas) stops when a page is older than `sinceDays`, after 25 pages, or once the source has yielded its share of matching rows: `maxItems` divided by the number of selected sources (at least 20, at most 500). Himalayas alone publishes more than a thousand jobs a day, so this share keeps the default result a mix of all boards; select fewer sources or raise `maxItems` to get more from one board.
- Freshness: Remotive states that its public feed is delayed by 24 hours and currently returns only its 10 most recent listings; Remote OK and We Work Remotely expose their newest ~100 jobs; Jobicy and Himalayas are near real time. A run with defaults takes about 10-20 seconds.
- Filters (`keywords`, `categories`, `locationContains`, `salaryOnly`, `sinceDays`) are applied by the Actor after fetching, uniformly across sources. Category and location texts are not normalised across sources (for example "USA Only" vs "United States"), so use short substrings.
- Deduplication is by lower-cased company + title only; the same role posted with slightly different titles stays as separate rows.
- Not covered: boards other than these five, jobs behind logins, applicant data, full HTML descriptions (plain text only), and historical listings beyond what the feeds expose.
- One failing source never fails the run: its error is listed in the summary and the status becomes `partial`. The run fails only when every selected source fails.

### Pricing

Pay per result: **$0.50 per 1,000 rows** (`result` event). Platform usage is billed separately by Apify. Removed/free rows are not charged.

### Legal

Not affiliated with Remotive, Jobicy, Himalayas, We Work Remotely or Remote OK. Uses only their public APIs and feeds, and the rows keep each source's job URL so that attribution can be preserved. Each source sets conditions for reusing its data, which you must follow when you display or republish rows:

- Remotive: link back to the job URL on Remotive and name Remotive as the source; do not submit the jobs to third-party job aggregators; do not use the listings to collect sign-ups; the feed is delayed by 24 hours. Terms (stated in every API response; documentation at https://remotive.com/api-documentation, which redirects to their GitHub docs).
- Jobicy: credit Jobicy with a direct link and send applicants to the original job URL from the feed. Terms and API docs: https://jobicy.com/jobs-rss-feed
- Himalayas: public jobs API; link to the job page on Himalayas. API: https://himalayas.app/api, terms: https://himalayas.app/terms
- We Work Remotely: public RSS feed (https://weworkremotely.com/remote-jobs.rss); link back to the listing on weworkremotely.com and check the site's terms before republishing.
- Remote OK: their API terms require a follow link back to the job URL on Remote OK and naming "Remote OK" as the source; the Remote OK logo may not be used without permission. Terms (in the API response): https://remoteok.com/api, legal: https://remoteok.com/legal

### FAQ

**Why do I get fewer rows than `maxItems`?** The feeds expose a limited window (about 10 Remotive, 100 We Work Remotely, 100 Remote OK jobs) and `sinceDays` plus your filters remove more. Set `sinceDays: 0` or lower the filters to see everything the feeds offer.

**Why is `salaryInterval` null for some Remotive jobs?** Remotive publishes salary as free text such as "$170k - $200k"; the Actor extracts the numbers and currency but only sets an interval when the text states one (per hour, per month, per year).

**How do I get only US-friendly jobs?** Use `locationContains: "us"` (matches "USA", "United States", "US timezones"); note that Remote OK leaves the location empty for many worldwide jobs, and those rows are dropped by any location filter.

**Can I get the full HTML description?** No, only plain text (max 3,000 characters) via `includeDescription: true`. Follow `url` for the full posting.

**Is it safe to republish these jobs on my site?** Only if you keep the links and credits each source requires (see Legal). Remotive in particular forbids pushing its jobs into other job aggregators.

# Actor input Schema

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

Which job feeds to read. Each source is fetched independently; if one fails the others are still returned and the run status becomes "partial". Default: all five.

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

Keep only jobs whose title contains at least one of these words or phrases (case-insensitive substring match, e.g. "engineer", "product manager"). Empty list = all jobs.

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

Keep only jobs whose category, industry or tags contain one of these texts (case-insensitive substring match, e.g. "software", "marketing", "python"). Category names differ per source, so prefer short generic words. Empty list = all.

## `locationContains` (type: `string`):

Keep only jobs whose location or allowed region text contains this (case-insensitive), e.g. "USA", "Europe", "Worldwide". Jobs without any location text are dropped when this is set. Leave empty for all locations.

## `sinceDays` (type: `integer`):

Only jobs published within the last N days are returned; this also stops pagination early. Use 0 to disable the date filter. Default 14.

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

When true, each row includes descriptionText: the job description as plain text (HTML removed), truncated to 3,000 characters. When false, descriptionText is null and rows are much smaller.

## `salaryOnly` (type: `boolean`):

When true, jobs without any salary figure or salary text are dropped. We Work Remotely never provides structured salary data, so its jobs are excluded by this filter.

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

Upper limit on returned (charged) rows for this run after merging, deduplication and sorting by publish date (newest first). Also limits how many pages are fetched from paginated sources. Default 200.

## Actor input object example

```json
{
  "sources": [
    "remotive",
    "jobicy",
    "himalayas",
    "weworkremotely",
    "remoteok"
  ],
  "keywords": [],
  "categories": [],
  "locationContains": "",
  "sinceDays": 14,
  "includeDescription": false,
  "salaryOnly": false,
  "maxItems": 200
}
```

# Actor output Schema

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

All rows 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 = {
    "sources": [
        "remotive",
        "jobicy",
        "himalayas",
        "weworkremotely",
        "remoteok"
    ],
    "keywords": [],
    "categories": [],
    "locationContains": "",
    "sinceDays": 14,
    "maxItems": 200
};

// Run the Actor and wait for it to finish
const run = await client.actor("ailu_studio/remote-jobs-api").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": [
        "remotive",
        "jobicy",
        "himalayas",
        "weworkremotely",
        "remoteok",
    ],
    "keywords": [],
    "categories": [],
    "locationContains": "",
    "sinceDays": 14,
    "maxItems": 200,
}

# Run the Actor and wait for it to finish
run = client.actor("ailu_studio/remote-jobs-api").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": [
    "remotive",
    "jobicy",
    "himalayas",
    "weworkremotely",
    "remoteok"
  ],
  "keywords": [],
  "categories": [],
  "locationContains": "",
  "sinceDays": 14,
  "maxItems": 200
}' |
apify call ailu_studio/remote-jobs-api --silent --output-dataset

```

## MCP server setup

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

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/jdKn8Z3gr8G34JhWg/builds/uJFN7e5uxwnJj5gx8/openapi.json
