# Remote OK Scraper (`datascrapers/remoteok-scraper`) Actor

Scrape remote job listings from Remote OK with filters for role, location, benefits, salary, and sort order. Accepts listing/job URLs or structured filters and returns structured job JSON.

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

## Pricing

from $0.97 / 1,000 job scrapeds

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?

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 Scraper** creates a structured dataset of remote job records collected from remoteok.com. Each dataset item represents one job posting and can include the position, company, location, salary range, tags, benefits, employment type, posting date, and apply links. Query the source using a keyword search, role tag, location, benefit, or minimum-salary filter, or paste a pre-filtered Remote OK URL, control the result limit with `maxItems`, and retrieve records through the Apify Dataset API or export them as JSON, CSV, Excel, or XML.

### Dataset at a glance

| Property | Value |
|---|---|
| Source | remoteok.com |
| Record unit | One remote job posting |
| Input methods | `searchQueries`, `tags`, `locations`, `benefits`, `minSalary`, `startUrls` |
| Main identifiers | `id`, `url`, `applyUrl` |
| Delivery | Apify Dataset and API |
| Export formats | JSON, CSV, Excel, XML |
| Update model | Fresh records per Actor run |
| Pricing | $1 per 1,000 jobs |

### Coverage and available records

The Actor returns job postings from Remote OK's board, resolved from keyword searches, role tags, locations, benefits, salary thresholds, or direct URLs. Supported coverage includes:

- Keyword search results, scraped one query at a time.
- Role/category tags (for example `dev`, `backend`, `seo`), combined with AND.
- Location filters (countries or regions such as `US`, `NL`, `region_EU`, or `Worldwide`), each scraped as a separate search.
- Benefit filters such as `401k`, `async`, `unlimited_vacation`, and `4_day_workweek`.
- A minimum annual salary threshold in USD.
- Direct listing, tag, and single-job URLs via `startUrls`.
- Optional full job descriptions and direct apply links when `scrapeJobDetails` is enabled.

Salaries are returned only when Remote OK publishes them; when a salary is withheld (for example, behind a Premium upgrade), `salaryMin` and `salaryMax` are `null` rather than estimated.

### Data dictionary

| Field | Type | Nullable | Description | Example |
|---|---:|---|---|---|
| `id` | string | No | Remote OK job identifier | `"1137062"` |
| `slug` | string | No | URL slug for the job | `"remote-desarrollador-full-stack-..."` |
| `position` | string | No | Job title | `"DESARROLLADOR FULL STACK"` |
| `company` | string | No | Company name | `"Kruger NearShore LLC - Rekluti"` |
| `url` | string | No | Canonical job page URL | `"https://remoteok.com/remote-jobs/..."` |
| `applyUrl` | string | Yes | Direct apply link | `"https://remoteok.com/l/1137062"` |
| `companyUrl` | string | Yes | Company page URL | `"https://remoteok.com/company/..."` |
| `companyLogo` | string | Yes | Company logo URL | `""` |
| `location` | string | Yes | Display location text | `"Probably worldwide"` |
| `applicantLocations` | array | Yes | Accepted applicant locations | `["Anywhere"]` |
| `tags` | array | Yes | Role and skill tags | `["Full Stack", "Python", "DevOps"]` |
| `benefits` | array | Yes | Listed benefits | `["401k"]` |
| `salaryMin` | number | Yes | Minimum annual salary (USD), when published | `null` |
| `salaryMax` | number | Yes | Maximum annual salary (USD), when published | `null` |
| `salaryCurrency` | string | Yes | Salary currency code | `""` |
| `salaryPeriod` | string | Yes | Salary period, when published | `""` |
| `employmentType` | string | Yes | Employment type | `"FULL_TIME"` |
| `workHours` | string | Yes | Work-hours classification | `"Flexible"` |
| `industry` | string | Yes | Industry classification | `"Startups"` |
| `jobLocationType` | string | Yes | Location-type classification | `"TELECOMMUTE"` |
| `datePosted` | string | Yes | Posting timestamp (ISO 8601) | `"2026-08-22T00:00:12+00:00"` |
| `validThrough` | string | Yes | Listing expiry timestamp | `"2026-11-20T00:00:12+00:00"` |
| `postedAgo` | string | Yes | Relative age of the posting | `"16h"` |
| `description` | string | Yes | Full job description, only when `scrapeJobDetails` is on | `"Kruger NearShore LLC - Rekluti is hiring..."` |
| `isVerified` | boolean | Yes | Whether the listing is verified | `false` |
| `detailsFetched` | boolean | Yes | Whether full details were retrieved | `false` |
| `sourceQuery` | string | Yes | Query or filter that produced the record | `"python"` |

The most stable field for deduplication is `id`.

### Example dataset record

```json
{
  "id": "1137062",
  "slug": "remote-desarrollador-full-stack-kruger-nearshore-llc-rekluti-1137062",
  "url": "https://remoteok.com/remote-jobs/remote-desarrollador-full-stack-kruger-nearshore-llc-rekluti-1137062",
  "applyUrl": "https://remoteok.com/l/1137062",
  "position": "DESARROLLADOR FULL STACK",
  "company": "Kruger NearShore LLC - Rekluti",
  "companyUrl": "https://remoteok.com/company/kruger-nearshore-llc-rekluti",
  "location": "Probably worldwide",
  "applicantLocations": ["Anywhere"],
  "tags": ["Full Stack", "Front End", "Python", "Developer"],
  "benefits": [],
  "salaryMin": null,
  "salaryMax": null,
  "salaryCurrency": "",
  "employmentType": "FULL_TIME",
  "workHours": "Flexible",
  "jobLocationType": "TELECOMMUTE",
  "datePosted": "2026-08-22T00:00:12+00:00",
  "postedAgo": "16h",
  "isVerified": false,
  "detailsFetched": false,
  "sourceQuery": "python"
}
```

This record was produced by a search for `python` with `maxItems` capped, without full job details enabled.

### Query and input reference

| Input | Type | Required | Default | Accepted values | Description |
|---|---:|---|---|---|---|
| `startUrls` | array | No | none | Remote OK listing or job URLs | Direct URLs to scrape; when set, filter fields are ignored for those URLs. |
| `searchQueries` | array | No | none | Free-text terms | Keyword searches, scraped one query at a time. |
| `tags` | array | No | none | `dev`, `backend`, `seo`, etc. | Role/category tags, combined with AND. |
| `locations` | array | No | none | `Worldwide`, `US`, `NL`, `region_EU`, etc. | Location filters; each scraped as a separate search. |
| `benefits` | array | No | none | `401k`, `async`, `4_day_workweek`, etc. | Required benefits. |
| `minSalary` | integer | No | none | 0–250000 | Minimum annual salary in USD. |
| `sortBy` | string | No | `date` | `date`, `salary`, `views`, `applied`, `hot`, `benefits` | Sort order for the board. |
| `maxItems` | integer | No | 10 | `0` (unlimited) or a positive integer | Maximum jobs to scrape. |
| `scrapeJobDetails` | boolean | No | `false` | `true` / `false` | Fetch full descriptions and apply URLs; adds a `job-details` charge. |
| `proxyConfiguration` | object | No | Apify Residential | Apify proxy settings | Proxy configuration. |

Minimal request:

```json
{
  "searchQueries": ["python"],
  "maxItems": 10,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": ["RESIDENTIAL"]
  }
}
```

Advanced request with filters and full details:

```json
{
  "searchQueries": ["staff engineer"],
  "tags": ["dev", "backend"],
  "locations": ["US"],
  "benefits": ["401k"],
  "minSalary": 120000,
  "sortBy": "date",
  "maxItems": 100,
  "scrapeJobDetails": true,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": ["RESIDENTIAL"]
  }
}
```

### Retrieve the data through the API

1. Start the Actor with a JSON input containing search queries, filters, or start URLs.
2. Wait for the run to finish, or use the synchronous run endpoint.
3. Retrieve items from the run's default dataset via the Dataset API.
4. Paginate the dataset or export it in the required format.

The Apify Console generates ready-to-run code for Python, JavaScript, and other languages in the Actor's API tab; see that tab for the current endpoint and authentication details. Never place a real API token in a URL or example.

### Data quality and record handling

- Salaries and benefits are conditional and appear only when Remote OK publishes them; withheld values are returned as `null` rather than estimated.
- The Actor normalizes posting dates to ISO 8601 and adds `postedAgo` and `detailsFetched` flags alongside the source fields.
- Within a run, listings are deduplicated by `id`. Across runs, key on `id` to avoid storing duplicates.
- Source changes to Remote OK's layout or anti-bot measures can affect extraction; a failed fetch is skipped and logged rather than aborting the run.
- Enable Apify Residential proxies (the default) to reduce blocks and rate limiting.

### Export and pipeline examples

| Destination | Recommended method | Typical use |
|---|---|---|
| PostgreSQL/Supabase | Dataset API or webhook consumer | Store job records keyed on `id` |
| Google Sheets | Apify integration | Shareable job board snapshot |
| S3/cloud storage | Scheduled export or integration | Daily archive of new postings |

### Pricing and cost examples

Billing is pay-per-event. Each job written to the dataset is one `job` charge, and each successful full-page enrichment is one `job-details` charge. `apify-actor-start` is a small one-time per-run charge.

| Records | Estimated base cost |
|---:|---:|
| 1,000 jobs | $1.00 |
| 10,000 jobs | $10.00 |

Enabling `scrapeJobDetails` adds one `job-details` charge per successful fetch (also $1 per 1,000). Estimates assume the published pay-per-event model and do not include Apify platform usage; Apify subscription plan discounts may reduce the per-event rate.

### Limitations and responsible data use

- Only publicly accessible Remote OK listings are returned; salaries hidden by Remote OK are not recovered.
- Extraction depends on Remote OK's current availability and site structure, which can change without notice.
- Conditional fields may be missing for individual postings.
- No historical snapshots are stored unless the user archives dataset exports.
- Users are responsible for compliance with applicable terms of service and privacy obligations.

### Dataset questions

#### What does one dataset item represent?

One dataset item is a single remote job posting, identified by its `id`.

#### Which field should I use as a unique identifier?

Use `id`; it is unique per posting and stable across runs.

#### Are fields nullable or conditional?

Yes. `salaryMin`, `salaryMax`, `benefits`, `description`, and `companyLogo` depend on what Remote OK publishes for the posting; `description` is present only when `scrapeJobDetails` is enabled.

#### Can I retrieve the records as CSV or JSON?

Yes. The dataset can be exported as JSON, CSV, Excel, or XML from the Apify Console or Dataset API.

#### How do I paginate large datasets?

Raise `maxItems` (set `0` for unlimited) to collect more records in a single run, then paginate via the Dataset API.

#### What counts as a billable result?

Each job record written to the dataset is one `job` charge; each successful full-page enrichment adds one `job-details` charge.

### Related datasets from Data Scrapers

- [LinkedIn Job Scraper](https://apify.com/datascrapers/linkedin-job-scraper) — job postings from LinkedIn for the same talent-market analysis.
- [Glassdoor Jobs Scraper](https://apify.com/datascrapers/glassdoor-jobs-scraper) — job and company records for salary and employer benchmarking.
- [SEEK Jobs Scraper](https://apify.com/datascrapers/seek-au-jobs-scraper) — Australia and New Zealand job listings for regional hiring data.
- [ZipRecruiter Scraper](https://apify.com/datascrapers/ziprecruiter-scraper) — job postings for cross-board vacancy monitoring.

### Data Scrapers support

Need an additional field, record type, or export workflow? Contact Data Scrapers at stardustspotlight@gmail.com. Include a sample source URL, required fields, expected record volume, and preferred delivery format.

# Actor input Schema

## `startUrls` (type: `array`):

Remote OK listing or job URLs from your browser (e.g. "https://remoteok.com/?order\_by=date", "https://remoteok.com/remote-dev-jobs", "https://remoteok.com/remote-jobs-in-singapore", or a single job page). Recommended when you already have a filtered URL. When provided, tags/location/benefits/search filters below are ignored for those URLs.

## `searchQueries` (type: `array`):

Free-text searches on Remote OK (e.g. "python", "staff engineer", "nurse"). Each query is scraped separately. Ignored when Start URLs are provided. Primary input for AI agents when you do not have a URL.

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

Job tags/categories to filter by (e.g. "dev", "medical", "backend", "seo"). Multiple tags are combined (AND). Ignored when Start URLs are provided.

## `locations` (type: `array`):

Countries or regions where the job can be done (e.g. "Worldwide", "US", "NL", "SG", "region\_EU"). Each location is scraped as a separate search because Remote OK allows one location filter at a time. Ignored when Start URLs are provided.

## `benefits` (type: `array`):

Required job benefits (e.g. "401k", "async", "unlimited\_vacation", "4\_day\_workweek"). Multiple benefits are combined. Ignored when Start URLs are provided.

## `minSalary` (type: `integer`):

Minimum annual salary in USD (e.g. 30000, 80000, 150000). Matches the Remote OK salary filter (steps of $10,000, up to $250,000). Use 0 for no minimum. Ignored when Start URLs are provided.

## `sortBy` (type: `string`):

How to sort the job board (e.g. "date" for latest jobs, "salary" for highest paid). Applied to filter searches and to Start URLs that do not already include order\_by.

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

Maximum number of jobs to scrape (0 = unlimited)

## `scrapeJobDetails` (type: `boolean`):

When enabled, fetches each job's full page and merges richer fields (description, apply URL, benefits). You are charged per job (job) plus an additional event per successful details fetch (job-details).

## `proxyConfiguration` (type: `object`):

Proxy settings for anti-bot protection. Apify Residential is recommended.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://remoteok.com/?order_by=date"
    }
  ],
  "searchQueries": [
    "python"
  ],
  "tags": [],
  "locations": [],
  "benefits": [],
  "minSalary": 0,
  "sortBy": "date",
  "maxItems": 10,
  "scrapeJobDetails": false,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

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

JSON array of scraped job listings at {{links.apiDefaultDatasetUrl}}/items

## `runStats` (type: `string`):

Record count and timestamps for this run

## `run` (type: `string`):

Apify Console link to inspect this run

# 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 = {
    "startUrls": [
        {
            "url": "https://remoteok.com/?order_by=date"
        }
    ],
    "searchQueries": [
        "python"
    ],
    "maxItems": 10,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("datascrapers/remoteok-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 = {
    "startUrls": [{ "url": "https://remoteok.com/?order_by=date" }],
    "searchQueries": ["python"],
    "maxItems": 10,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("datascrapers/remoteok-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 '{
  "startUrls": [
    {
      "url": "https://remoteok.com/?order_by=date"
    }
  ],
  "searchQueries": [
    "python"
  ],
  "maxItems": 10,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call datascrapers/remoteok-scraper --silent --output-dataset

```

## MCP server setup

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