# Remote Jobs Aggregator: Jobicy and Remote OK Job Listings (`nightwave-owner/remote-jobs-open-feeds`) Actor

Returns remote job listings from the public Jobicy and Remote OK APIs in one schema: title, company, category, salary, region, date, job URL and description, with source attribution on every row. Keyword and category filters, deduplication, onlyNew for daily alerts.

- **URL**: https://apify.com/nightwave-owner/remote-jobs-open-feeds.md
- **Developed by:** [Viktor Wiberg](https://apify.com/nightwave-owner) (community)
- **Categories:** Jobs, Developer tools, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$2.00 / 1,000 job listings

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 Aggregator: Jobicy and Remote OK Job Listings

Returns remote jobs from the public APIs of [Jobicy](https://jobicy.com) and [Remote OK](https://remoteok.com) as one job feed with one schema: title, company, category, job type, salary, region, tags, publication date, job URL and a plain text description of at most 2 000 characters. Every row says where it comes from and what attribution the source asks for, so you can show the listings on a job board, in a newsletter or in an internal tool and keep the credit right.

No API key and no login. Only sources whose terms allow reuse in a tool like this are included (see "Source and license").

### What is covered

- **Jobicy:** remote jobs published in the last 7 days (the window the API offers), with a 3 hour publication delay set by Jobicy. Our test run on 5 October 2026 read 625 listings, about half of them with a salary range.
- **Remote OK:** the latest 100 listings from the Remote OK API, which usually go back 6 to 10 weeks.
- Filters for keywords, categories, sources and publication date. Results are sorted newest first.
- Duplicates are removed by job URL and by company plus title, so a job posted on both boards appears once.
- `onlyNew` turns the actor into a job alert: a scheduled run returns only listings it has not delivered before.
- **Not included:** Remotive, Arbeitnow and Himalayas. The actor never reads them, because their terms do not allow reuse in a tool like this (see "Source and license").

### Example from a real run

Input (run `ZCues2EGKLgZtvc5I`, 5 October 2026):

```json
{
  "keywords": ["python"],
  "maxResults": 5
}
```

The run returned 5 listings: CertiK, Bayesian Health, Upgrade, Affirm and Close. The first row, with the description cut short here:

```json
{
  "id": "jobicy:152447",
  "title": "Blockchain Security Engineer - Senior Level (Solidity / Rust / Golang )",
  "company": "CertiK",
  "companyUrl": "https://jobicy.com/company/certik",
  "category": "Cybersecurity",
  "jobType": "Full-Time",
  "salaryMin": 102000,
  "salaryMax": 180000,
  "salaryCurrency": "USD",
  "salaryPeriod": "yearly",
  "location": "USA",
  "tags": ["Senior"],
  "publishedAt": "2026-10-04T04:30:40.000Z",
  "url": "https://jobicy.com/jobs/152447-blockchain-security-engineer-senior-level-solidity-rust-golang",
  "descriptionText": "About the Role:\n\nWe are seeking a Senior Blockchain Security Engineer with a strong security mindset and deep technical expertise across smart contracts, blockchain nodes, and decentralized infrastructure. You will play a critical role in s...",
  "source": "Jobicy",
  "sourceUrl": "https://jobicy.com",
  "license": "Jobicy public Jobs API fair use: credit Jobicy as the original source and keep the Jobicy job URL when displaying the listing."
}
```

A row from Remote OK (run `NoHvdnldQe0DfDCw5`, input `{"sources": ["remoteok"], "postedSince": 60}`):

```json
{
  "id": "remoteok:1137461",
  "title": "Head of Operations",
  "company": "Leverage Live Local",
  "companyUrl": null,
  "category": "Operations",
  "jobType": null,
  "salaryMin": 170000,
  "salaryMax": 350000,
  "salaryCurrency": "USD",
  "salaryPeriod": "yearly",
  "location": "Worldwide",
  "tags": ["non tech", "ops", "exec"],
  "publishedAt": "2026-10-03T20:20:32.000Z",
  "url": "https://remoteok.com/remote-jobs/remote-head-of-operations-leverage-live-local-1137461",
  "descriptionText": "Leverage Live Local prepares and manages Florida Live Local Act property tax exemptions for multifamily owners. ...",
  "source": "Remote OK",
  "sourceUrl": "https://remoteok.com",
  "license": "Remote OK API terms: link back to the job URL on Remote OK (no nofollow) and mention Remote OK as the source. Do not use the Remote OK logo."
}
```

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `keywords` | array of strings | `[]` | Words that must all appear in the title, company, category, tags or description. Matches the start of words, so `develop` finds developer. |
| `categories` | array of strings | `[]` | Keep listings whose category or tags contain any of these words, for example `marketing` or `customer support`. |
| `sources` | array | `["jobicy", "remoteok"]` | Which APIs to read. |
| `postedSince` | integer | `7` | Only listings published in the last number of days, 1 to 90. |
| `maxResults` | integer | `50` | Maximum number of listings, newest first, 1 to 1 000. |
| `onlyNew` | boolean | `false` | Return only listings that earlier runs with the same input did not deliver. See "Monitoring and scheduling". |

A run with empty input returns the 50 newest listings from both sources from the last 7 days.

Example input: customer support jobs from the last 3 days.

```json
{
  "categories": ["customer support"],
  "postedSince": 3,
  "maxResults": 100
}
```

Example input: every Remote OK listing the API currently offers.

```json
{
  "sources": ["remoteok"],
  "postedSince": 90,
  "maxResults": 100
}
```

### Output

One row per listing. The dataset view "Remote jobs" shows the most important columns.

| Field | Type | Description |
|---|---|---|
| `id` | string | Source and the source's own id, for example `jobicy:152447`. Stable between runs. |
| `title` | string | Job title. |
| `company` | string | Hiring company. |
| `companyUrl` | string or null | Company page on the source site (Jobicy only). |
| `category` | string or null | Job family. Jobicy's own industry, or for Remote OK the first tag that names a job family (for example `dev` becomes Software Development). |
| `jobType` | string or null | Full-Time, Part-Time, Contract, Freelance or Internship, when the source gives it. |
| `salaryMin`, `salaryMax` | number or null | Salary range as published. Null when the listing has none. |
| `salaryCurrency` | string or null | ISO currency code, for example USD or PLN. |
| `salaryPeriod` | string or null | `yearly`, `hourly` or another period given by Jobicy. Remote OK salaries are yearly. |
| `location` | string | Where applicants must be, for example `USA`, `EMEA` or `LATAM, Canada, USA`. `Worldwide` when the listing has no restriction. |
| `tags` | array | The source's tags. For Jobicy: further industries and seniority. |
| `publishedAt` | string | Publication time in ISO 8601, UTC. |
| `url` | string | The listing on the source site. Link to this URL when you show the job. |
| `descriptionText` | string | Description as plain text, cut at 2 000 characters. |
| `source` | string | `Jobicy` or `Remote OK`. Mention it when you show the job. |
| `sourceUrl` | string | Home page of the source. |
| `license` | string | The attribution the source asks for. |

### Monitoring and scheduling

Set `onlyNew` to `true` to use the actor as a job alert. The actor then remembers which listings it has delivered for the same input, in a named key-value store in your Apify account (`nightwave-state-remote-jobs-open-feeds`, one record per input). Each run returns and charges only listings that earlier runs did not deliver, keyed on source plus id. The first run returns everything in the selection, up to `maxResults`. A run without news finishes successfully with 0 rows.

`onlyNew` and `maxResults` are not part of the remembered input, so you can change them without starting over. Changing any other field starts a fresh state. Set `maxResults` above the number of new listings your filters normally get between two runs; listings above the limit are kept for the next run.

Real example: with the input below, run `7ywnc1O3jmqk76vrh` returned 50 listings, run `57nGFfqE2jB7QK1bT` returned the 7 that were over the limit, and run `qvyyDBmWG74Sgmdrv` returned 0 because nothing new had been published. A second run straight after the first, with `onlyNew` and the same input, returns 0 rows like that and is not charged.

```json
{
  "keywords": ["customer support"],
  "onlyNew": true
}
```

Example: a daily run at 07:00 that returns new Python jobs. In Apify Console, open **Schedules**, create a schedule with the cron expression `0 7 * * *` and your time zone, and add this actor with the input below.

```json
{
  "keywords": ["python"],
  "onlyNew": true,
  "maxResults": 200
}
```

The same schedule through the Apify API:

```sh
curl -X POST "https://api.apify.com/v2/schedules?token=<YOUR_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"name": "daily-remote-python-jobs", "cronExpression": "0 7 * * *", "timezone": "Europe/London", "isEnabled": true, "isExclusive": true,
       "actions": [{"type": "RUN_ACTOR", "actorId": "nightwave-owner~remote-jobs-open-feeds",
                    "runInput": {"contentType": "application/json; charset=utf-8", "body": "<the input above as a JSON string>"}}]}'
```

Connect a webhook or an integration (Slack, e-mail, Google Sheets) to the actor in Apify Console if you want the new listings sent somewhere when the run finishes. Both sources ask that their API is not polled too often; one to four runs a day is enough.

### Limitations

- **Two sources.** Only Jobicy and Remote OK are included, because their published terms allow reuse with attribution. Remotive, Arbeitnow and Himalayas are left out (see below).
- **Windows set by the sources.** Jobicy offers the last 7 days and delays new listings by 3 hours. Remote OK returns its latest 100 listings only. Older jobs cannot be fetched.
- **Keyword matching** is done on the listing text the APIs return. It matches the start of words, so a search for `go` also finds "good". Use longer words or a category when that matters.
- **Categories differ by source.** Jobicy has its own industries. Remote OK has no category field, so the category is derived from its tags and is sometimes broad.
- **Salaries** are passed on as published. Remote OK gives yearly USD amounts; a listing without salary has null.
- **Descriptions** are converted from HTML to plain text and cut at 2 000 characters. Long dashes are written as hyphens.
- **Application links** point to the listing on the source site, as both sources require.

### Use cases

- Fill a niche job board or a careers page with fresh remote listings, with source credit built in.
- A weekly remote jobs newsletter: run with `onlyNew` and the categories your readers want.
- Recruiters and talent teams: watch which companies hire remotely for a skill, with salary ranges where given.
- Labour market research: count listings by category, region and salary over time.

### Source and license

The actor reads two sources: Jobicy and Remote OK. The table also lists three other remote job APIs that we checked and **do not include**: Remotive, Arbeitnow and Himalayas are never queried and never appear in the output, because their terms do not allow reuse in a commercial tool like this one. The quoted terms show why.

| Source | API | Terms (quoted) | Included |
|---|---|---|---|
| Jobicy | `https://jobicy.com/api/v2/remote-jobs` | "You may use Jobicy listings in your own products and user experiences without requesting individual permission. Keep Jobicy as the original source and preserve the canonical Jobicy job URL when displaying listings." Also: "Do not present Jobicy listings as your own original job postings or remove source attribution." (Fair use section of the [Jobicy API documentation](https://jobicy.com/jobs-rss-feed)) | Yes |
| Remote OK | `https://remoteok.com/api` | "Please link back (with follow, and without nofollow!) to the URL on Remote OK and mention Remote OK as a source, so we get traffic back from your site." and "Please don't use the Remote OK logo without written permission" (notice in the API response). The [terms](https://remoteok.com/legal) say: "You agree to link back with a web hyperlink or in-app hyperlink to our site on the page or app screen where you use the data from our APIs or site." | Yes |
| Remotive | `https://remotive.com/api/remote-jobs` | "Please do not submit Remotive jobs to third Party websites, including but not limited to: Jooble, Neuvoo, Google Jobs, LinkedIn Jobs." Remotive sells a private API for other uses, and its robots.txt disallows `/api/*`. | No, not read |
| Arbeitnow | `https://www.arbeitnow.com/api/job-board-api` | The API notice says "By using the API, you agree to the terms of service present on Arbeitnow.com", and those terms grant use "for personal, non-commercial transitory viewing only". Most listings are also on-site jobs in Germany. | No, not read |
| Himalayas | `https://himalayas.app/jobs/api` | The API documentation asks for a link back, but the site terms say you may not "use the materials for any commercial purpose" and may not use "robots, screen scraping, or similar automated data gathering". | No, not read |

When you show listings from this actor, link each job to its `url` and name the `source`, as the `license` field on every row says. The job texts belong to the employers and the sources; this actor passes them on and does not add or change requirements.

This actor is not affiliated with or endorsed by Jobicy or Remote OK.

### Pricing

Pay per result: 0.002 USD per listing returned (event `job`), which is 2 USD per 1 000 listings. Platform usage is included, so you pay only per result. `maxResults` caps how many rows a run returns, so you always know the highest possible cost.

Rows are delivered only after they have been charged. If you set a maximum cost per run (maxTotalChargeUsd), the run stops there and its status message says how many rows were delivered.

### Contact

Built and maintained by Nightwave AB. Questions, bugs and feature requests: kontakt@nightwave.se

### På svenska

Actorn hämtar distansjobb från de öppna API:erna hos Jobicy och Remote OK och lämnar dem i ett gemensamt format: titel, företag, kategori, anställningsform, lön, region, taggar, publiceringsdatum, länk till annonsen och en beskrivning på högst 2 000 tecken.

- Filtrera på nyckelord, kategorier, källa och antal dagar. Dubbletter tas bort på länk och på företag plus titel.
- Tom input ger de 50 senaste annonserna från båda källorna de senaste 7 dagarna.
- Med `onlyNew: true` blir actorn en jobbevakning: en schemalagd körning lämnar bara annonser som inte levererats tidigare, och bara de debiteras.
- Varje rad anger källan och vilken attribution källan kräver. Länka till annonsens `url` och nämn källan när du visar jobbet.
- Bara källor vars villkor tillåter vidareanvändning är med. Remotive, Arbeitnow och Himalayas är uteslutna på grund av sina villkor (se tabellen ovan).
- Pris: 0,002 USD per annons, 2 USD per 1 000.

# Actor input Schema

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

Words that must all appear in the title, company, category, tags or description, for example \["python"] or \["customer support", "spanish"]. Matches the start of words, so "develop" finds developer. Empty returns all listings.

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

Keep listings whose category or tags contain any of these words, for example \["marketing", "sales"]. Jobicy uses categories such as Software Engineering, Marketing & Sales and Customer Support & Success. Remote OK categories are taken from its tags. Empty returns all categories.

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

Job APIs to read: jobicy and remoteok. Defaults to both. Each row names its source and the attribution the source asks for.

## `postedSince` (type: `integer`):

Only listings published in the last number of days, for example 3. 1 to 90, defaults to 7. Jobicy keeps 7 days of listings. Remote OK returns its latest 100 listings, which usually go back 6-10 weeks, so use 60 to get all of them.

## `maxResults` (type: `integer`):

Maximum number of listings to return, newest first, for example 50. 1 to 1 000, defaults to 50.

## `onlyNew` (type: `boolean`):

Return only listings that an earlier run with the same input has not delivered, for example true for a daily job alert. The first run returns all matching listings. Defaults to false.

## Actor input object example

```json
{
  "keywords": [
    "python"
  ],
  "categories": [
    "marketing",
    "sales"
  ],
  "sources": [
    "remoteok"
  ],
  "postedSince": 3,
  "maxResults": 50,
  "onlyNew": true
}
```

# Actor output Schema

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

One row per job listing, as JSON. Open in Apify Console or download via the dataset API.

# 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": [],
    "categories": [],
    "sources": [
        "jobicy",
        "remoteok"
    ],
    "postedSince": 7,
    "maxResults": 50,
    "onlyNew": false
};

// Run the Actor and wait for it to finish
const run = await client.actor("nightwave-owner/remote-jobs-open-feeds").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": [],
    "categories": [],
    "sources": [
        "jobicy",
        "remoteok",
    ],
    "postedSince": 7,
    "maxResults": 50,
    "onlyNew": False,
}

# Run the Actor and wait for it to finish
run = client.actor("nightwave-owner/remote-jobs-open-feeds").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": [],
  "categories": [],
  "sources": [
    "jobicy",
    "remoteok"
  ],
  "postedSince": 7,
  "maxResults": 50,
  "onlyNew": false
}' |
apify call nightwave-owner/remote-jobs-open-feeds --silent --output-dataset

```

## MCP server setup

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

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/PWOFrL8ldcIk8VUQ4/builds/WYxLwfzmvOkDVULv0/openapi.json
