# Remote OK Jobs Scraper: Only New Remote Jobs (`quicksloth/remoteok-jobs-scraper`) Actor

Get remote job postings from Remote OK in one clean schema: title, company, location, tags, salary range, date and the link back to each job. Filter by tag and keywords. On scheduled runs, pay only for new or changed jobs. No personal-data fields.

- **URL**: https://apify.com/quicksloth/remoteok-jobs-scraper.md
- **Developed by:** [Quicksloth](https://apify.com/quicksloth) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 jobs

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 OK Jobs Scraper: Only New Remote Jobs

Get the latest **remote jobs from [Remote OK](https://remoteok.com)** in a clean, predictable schema: title, company, location, tags, salary range, posted date and the link to each job on Remote OK. Pick the tags you care about (python, devops, design, customer-support…), filter by title or location keywords, and download the results as JSON, CSV or Excel.

- **Only new or changed jobs.** Schedule it daily and, after the first run, get (and pay for) only the jobs that appeared or changed since the last run.
- **Several tags in one run.** Jobs listed under more than one of your tags are saved once, not twice.
- **Clean text.** Titles and descriptions come with broken characters fixed ("für", not "fÃ¼r"); salaries of 0 become empty.
- **No personal-data fields**: the output has no names, emails or phone numbers of people, and job descriptions are off unless you ask for them.
- **Fast and light**: one request per tag to the public Remote OK API, no browser, no proxy.

Free Apify plan: up to 100 jobs per run, enough to try it on your tags.

### Who is it for?

- **Job seekers and job-alert builders**: send new remote jobs for your stack to a spreadsheet, an email digest or a chat channel with Apify schedules and integrations.
- **Remote-work job boards and newsletters**: get fresh postings with the link back to Remote OK that its API terms ask for (see [Using the data](#using-the-data-link-back-to-remote-ok)).
- **Recruiters and market researchers**: follow which companies hire remotely, with which tags and which published salary ranges.
- **AI agents and data pipelines**: a predictable JSON schema that is easy to feed into LLM workflows, spreadsheets or databases, also through [Apify's MCP server](https://docs.apify.com/integrations/mcp).

### Input

**Basic example**: the latest Python and DevOps jobs with "engineer" in the title.

```json
{
    "tags": ["python", "devops"],
    "titleKeywords": ["engineer"],
    "maxResults": 100
}
```

**Scheduled monitoring: only new or changed jobs**

```json
{
    "tags": ["python"],
    "titleKeywords": ["engineer", "developer"],
    "onlyNewJobs": true,
    "stateKey": "daily-python"
}
```

Leave `tags` empty to get the latest jobs of all tags.

#### Input fields

| Field | Type | Description |
|---|---|---|
| `tags` | list of strings | Remote OK tags, as in the tag pages of remoteok.com (`python`, `devops`, `customer-support`). Up to 50. Empty = latest jobs of all tags. |
| `titleKeywords` | list of strings | Keep only jobs whose title contains at least one of these words (case-insensitive). |
| `locationKeywords` | list of strings | Keep only jobs whose location contains at least one of these words. The location is free text written by the employer ("London,", "Remote - US", "Worldwide") and often empty, so this filter keeps few jobs: prefer tags and title keywords, and avoid short words that match inside longer ones. |
| `includeDescription` | boolean | Add the job description as plain text. Default `false`. |
| `onlyNewJobs` | boolean | Return only jobs that are new or changed since the previous run with the same filters. Default `false`. |
| `stateKey` | string | Optional name for the "only new jobs" history, so different scheduled tasks keep separate histories. |
| `maxResults` | integer | Stop after this many jobs in total. Empty or 0 = no limit. |

### Output

Each job is one item in the dataset. Example:

```json
{
    "id": "remoteok:1136795",
    "jobId": "1136795",
    "source": "Remote OK",
    "tag": "python",
    "title": "Senior Software Engineer Case Execution",
    "company": "Pivotal Health",
    "location": "New York City",
    "tags": ["dev", "python", "senior", "engineer", "backend"],
    "postedAt": "2026-08-16T00:00:03.000Z",
    "salaryMin": 190000,
    "salaryMax": 220000,
    "salaryCurrency": "USD",
    "url": "https://remoteok.com/remote-jobs/remote-senior-software-engineer-case-execution-pivotal-health-1136795",
    "applyUrl": "https://remoteok.com/remote-jobs/remote-senior-software-engineer-case-execution-pivotal-health-1136795",
    "descriptionText": null,
    "scrapedAt": "2026-10-09T06:51:05.967Z"
}
```

| Field | Description |
|---|---|
| `id` | Unique, stable id (`remoteok:<jobId>`). |
| `jobId` | Remote OK job id. |
| `source` | Always `Remote OK`. |
| `tag` | The input tag the job was found with; `null` for the latest jobs of all tags. |
| `title`, `company` | As published. |
| `location` | Location or region as written by the employer (for example `Worldwide`); `null` when not given. |
| `tags` | Remote OK tags of the job. |
| `postedAt` | When the job was posted (ISO 8601). |
| `salaryMin`, `salaryMax`, `salaryCurrency` | Salary range as published on Remote OK, in USD. Usually yearly; a few employers enter small numbers that are probably hourly. `null` when not given. |
| `url` | The job on Remote OK. |
| `applyUrl` | Where to apply, as given by the API (often the same page on Remote OK). |
| `descriptionText` | Plain-text description, only with `includeDescription`. |
| `scrapedAt` | When the job was fetched (ISO 8601). |

#### Run summary

The `OUTPUT` record in the key-value store lists every tag with its status (`ok`, `failed` or `skipped`), how many jobs it gave and a note, if any (for example a tag with no jobs). A tag that fails does not stop the run and costs nothing.

```json
{
    "tags": [{ "input": "python", "status": "ok", "jobs": 100, "error": null }],
    "totalJobs": 100,
    "newJobs": null,
    "stoppedBecause": null
}
```

### Pricing

**$1.00 per 1,000 jobs** ($0.001 per job saved to the dataset), plus Apify's standard start charge of $0.00005 per run. You pay only for jobs you get: tags with no jobs, jobs removed by your filters and jobs already saved under another tag cost nothing.

| Jobs saved | Cost |
|---|---|
| 100 | $0.10 |
| 1,000 | $1.00 |
| 10,000 | $10.00 |

- **Example:** a run that saves 100 jobs costs $0.10. With *Only new or changed jobs* on and a daily schedule, you pay only for the jobs that are new or changed since the previous run.

You can set a maximum cost for each run in Apify Console; the Actor stops cleanly when it reaches it and says so in the status message.

#### Free plan limit

Users on the free Apify plan get up to **100 jobs per run**. The run log and status message say when this limit is reached. Paid plans have no such limit.

### Only new jobs (scheduled runs)

Turn on `onlyNewJobs` and schedule the Actor (for example daily). The first run returns all matching jobs and remembers them. Later runs return only jobs that are **new** or whose title, company, location, tags, salary or link **changed**. Changes in the description do not count. Jobs that were not returned because of a limit stay "unseen" and come back in the next run. `newJobs` in the run summary says how many were returned in this mode.

The history is one for all the tags of the run, so a job listed under several of your tags is returned (and paid) once, also across runs. It is kept in a named key-value store in your account (`remoteok-jobs-scraper-seen-state`) and is separate for each combination of tags and filters: if you change the tags, the next run starts a new history and returns everything again (unless you set `stateKey`, which names the history and keeps it when the tags or filters change). If you run several scheduled tasks with the same tags and filters, give each one its own `stateKey`.

### Limits

- The Remote OK API returns about the **latest 100 jobs per tag** (and about 100 for "all tags"). For a complete stream of a busy tag, run the Actor on a schedule with `onlyNewJobs`.
- The API is refreshed periodically, not live: it can lag the Remote OK website by a day or more, so a job posted this morning may appear in a later run.
- Up to 50 tags per run. The Actor waits one second between requests, as Remote OK's robots.txt asks.
- Many jobs have no location or salary: those fields are `null`.

### Using the data: link back to Remote OK

Remote OK's API terms ask everyone who uses the data to **mention Remote OK as the source and link back to the job on Remote OK** (with a normal, followed link). Each result has `source` and `url` for this. Remote OK also asks not to use its logo without written permission. If you publish the jobs, keep the link; otherwise Remote OK may suspend access to its API.

### Legal and data protection

- The Actor reads only the **public [Remote OK API](https://remoteok.com/api)**, which Remote OK offers for this use, with the attribution described above ([legal page](https://remoteok.com/legal)). No login, no private data.
- **No personal-data fields**: the output has no names, emails or phone numbers of people.
- **Descriptions are off by default.** When you turn them on, they are free text written by the employer: email addresses, phone numbers in common formats and names after labels such as "Recruiter:" or "Contact:" are removed, but other mentions of people may remain. Check that you have a legal basis before storing them, and that your use of the employer's text is allowed.
- The Actor respects Remote OK's robots.txt, including its one-second crawl delay.
- Remote OK is a trademark of its owner, named here only to say which source the Actor reads. This Actor is not affiliated with or endorsed by Remote OK.
- You are responsible for how you use the data, including compliance with the laws that apply to you.

### FAQ

**Which tags can I use?** The ones in Remote OK's tag pages, for example `python`, `javascript`, `devops`, `design`, `marketing`, `customer-support`, `non-tech`. Spaces become dashes. The Actor keeps only jobs that really carry the tag: Remote OK's API also returns jobs whose tags merely contain the word (for `ux` it returned Linux jobs), and those are dropped without charge. A tag with no jobs gives an empty result and a note in the run summary.

**Why are some fields empty?** A field is `null` when the employer did not publish it (location and salary are often missing).

**Can I get older jobs?** No. The API gives the latest jobs only. Schedule the Actor with `onlyNewJobs` to build your own history.

### Support

Found a problem or need a new field? Open an issue in the **Issues** tab of this Actor and include the run ID.

### Changelog

- **0.1 (2026-10-09)**: first version.

# Actor input Schema

## `tags` (type: `array`):

Remote OK tags, one per line (for example python, devops, design, customer-support), as in the tag pages of remoteok.com. Each tag returns its latest jobs. Leave empty for the latest jobs of all tags.

## `titleKeywords` (type: `array`):

Keep only jobs whose title contains at least one of these words (case-insensitive). Empty = all titles.

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

Keep only jobs whose location contains at least one of these words (case-insensitive). The location is free text and often empty, so this filter keeps few jobs: prefer tags and title keywords.

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

Add the full job description as plain text. Descriptions are the employer's own text: email addresses, phone numbers in common formats and names after labels such as "Recruiter:" are removed, but other mentions of people may remain. Makes the dataset much larger.

## `onlyNewJobs` (type: `boolean`):

For scheduled runs: return only jobs that are new or have changed since the previous run with the same filters. The first run returns everything. You pay only for what is returned.

## `stateKey` (type: `string`):

Optional. Give each scheduled task its own name so their "only new jobs" histories stay separate. By default the history is shared by runs with the same filters.

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

Stop after this many jobs in total. Empty or 0 = no limit (the run also stops at the maximum cost you set for the run).

## Actor input object example

```json
{
  "tags": [
    "python",
    "devops"
  ],
  "titleKeywords": [
    "senior",
    "engineer"
  ],
  "locationKeywords": [
    "United States",
    "London"
  ],
  "includeDescription": false,
  "onlyNewJobs": false,
  "stateKey": "daily-python",
  "maxResults": 100
}
```

# Actor output Schema

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

No description

## `runSummary` (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 = {
    "tags": [
        "python",
        "devops"
    ],
    "maxResults": 100
};

// Run the Actor and wait for it to finish
const run = await client.actor("quicksloth/remoteok-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 = {
    "tags": [
        "python",
        "devops",
    ],
    "maxResults": 100,
}

# Run the Actor and wait for it to finish
run = client.actor("quicksloth/remoteok-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 '{
  "tags": [
    "python",
    "devops"
  ],
  "maxResults": 100
}' |
apify call quicksloth/remoteok-jobs-scraper --silent --output-dataset

```

## MCP server setup

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