# RemoteOK Jobs Scraper | Remote Tech Jobs, Salary & Stack (`tqm/remoteok-scraper`) Actor

Scrape the whole RemoteOK board into structured rows: job title, company, salary range, tech stack, location and a link to the original posting. Reads RemoteOK's public JSON feed - no browser, no proxy, no login - so a full run takes seconds. Filter by tech stack, category, region or minimum salary.

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

## Pricing

from $0.80 / 1,000 results

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?

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

RemoteOK Job Scraper turns [RemoteOK](https://remoteok.com)'s remote-job board into clean, structured rows — job title, company, salary, tech stack, location and a link back to the original posting.

It reads RemoteOK's public JSON feed directly. **No browser, no proxy, no login**, which is why a full run finishes in seconds and costs a fraction of a browser-driven scraper.

### What data can I extract from RemoteOK?

| Field | Description |
|---|---|
| `title` | Job title as posted |
| `company` | Hiring company |
| `jobUrl` | Link to the original RemoteOK posting |
| `applyUrl` | Direct application link, when RemoteOK carries one |
| `salary` | Salary string exactly as posted |
| `salaryMin` / `salaryMax` | Parsed annual salary bounds |
| `currency` | Salary currency (`USD`) |
| `location` | Location as posted; `Remote` when the posting states none |
| `tags` | RemoteOK's own tags, lower-cased |
| `techStack` / `skills` | Technologies detected in the title and description (the two carry the same list) |
| `jobType` | Always `remote` — RemoteOK is a remote-only board |
| `category` | Job category, when one of RemoteOK's tags matches a known category |
| `postedAt` | Publication date from the feed |
| `description` | Posting body, HTML stripped, truncated to 1,000 characters |
| `jobId` | Stable RemoteOK id, useful as a deduplication key |
| `scrapedAt` | When this run collected the row |

### How do I use RemoteOK Job Scraper?

1. Click **Try for free**.
2. Leave every field empty to get the whole current feed, or set a filter or two — `techStackFilter: ["aws", "kubernetes"]` is a good first try.
3. Click **Save & Start**.
4. When the run finishes, open the **Storage** tab and export as JSON, CSV, Excel or feed it straight to the API.

A default run takes a few seconds and returns whatever RemoteOK is currently listing.

### Input reference

| Option | Type | Default | What it does |
|---|---|---|---|
| `maxJobs` | integer | `100` | How many postings to return. **100 is the real ceiling** — see below. |
| `techStackFilter` | array | *(none)* | Keep only postings mentioning these technologies, e.g. `["aws", "terraform"]`. Matched on **word boundaries over the title and description**, so `aws` does not match *laws*. |
| `categories` | array | *(none)* | Restrict to RemoteOK categories, e.g. `["devops", "backend"]`. |
| `minSalary` | integer | `0` | Drop postings below this annual USD figure. Postings with no stated salary are also dropped once this is set. |
| `regions` | array | *(none)* | Restrict by region string, e.g. `["Europe"]`. |
| `failOnZeroResults` | boolean | `true` | Fail the run, with a reason, when it returns nothing. See below. |

#### Input sample

```json
{
  "maxJobs": 100,
  "techStackFilter": ["aws", "kubernetes", "terraform"],
  "minSalary": 80000,
  "failOnZeroResults": true
}
```

### Output sample

```json
{
  "source": "remoteok",
  "jobId": "1013845",
  "title": "Senior Platform Engineer",
  "company": "Acme Cloud",
  "jobUrl": "https://remoteok.com/remote-jobs/1013845",
  "applyUrl": "https://acmecloud.example/careers/senior-platform-engineer",
  "salary": "$120,000 - $160,000",
  "salaryMin": 120000,
  "salaryMax": 160000,
  "currency": "USD",
  "location": "Remote",
  "tags": ["devops", "aws", "kubernetes"],
  "techStack": ["aws", "kubernetes", "terraform"],
  "skills": ["aws", "kubernetes", "terraform"],
  "jobType": "remote",
  "category": "devops",
  "postedAt": "2026-09-01",
  "description": "We are looking for a senior platform engineer to own our AWS estate...",
  "scrapedAt": "2026-09-04T09:12:44.118Z"
}
```

### How many jobs can I scrape from RemoteOK?

**Roughly 100 — and that is the whole board, not a limit we imposed.**

RemoteOK's feed is a single un-paginated page carrying about 100 live postings at any time. Measured on 2026-09-04 it held **99**. `maxJobs` is capped at 100 for that reason.

If you see an actor advertising "up to 1,000 jobs" from RemoteOK, it cannot deliver them — there are not 1,000 to deliver. To build a larger dataset over time, schedule this actor daily and deduplicate on `jobId`; new postings appear continuously.

### A run that returns nothing tells you why

With `failOnZeroResults: true` (the default) an empty run **fails** and names the cause in its status message, for example:

> Read 99 postings but every one was removed by your filters (techStackFilter=99). Loosen the filters and re-run.

This is deliberate. A zero-row run that reports success is indistinguishable from a broken actor, and on pay-per-result pricing it is you paying for nothing. Set `failOnZeroResults: false` if you are polling on a schedule and quiet runs are expected.

### How much does it cost?

This actor is cheap to run because it never opens a browser: one HTTP request to a public JSON feed, then parsing. A full-board run is a single request regardless of how many filters you apply — a narrow `techStackFilter` costs exactly the same as no filter at all, because filtering happens after the fetch.

The most reliable way to price your own use case is to run it once with your real input and read the run's cost from the **Runs** tab, then multiply by how often you intend to schedule it.

### Who is this for?

- **Recruiters and talent teams** benchmarking remote salaries and tracking which stacks are in demand.
- **Job seekers** aggregating remote roles into a spreadsheet instead of refreshing a board, with direct `jobUrl` links to apply.
- **Market researchers** measuring remote-hiring trends, salary distributions and tech-stack adoption over time.
- **Lead generation** — every posting names a company that is actively spending money on engineering.
- **Developers** who want a job feed as JSON without writing and maintaining a scraper.

### FAQ

#### Do I need a RemoteOK account?

No. This actor reads RemoteOK's public feed. There is no login, no cookie and no account to be banned.

#### Will I get blocked?

Very unlikely. The actor makes a single request to a public endpoint that RemoteOK publishes for programmatic use, and it does not need a proxy.

#### Is it legal to scrape RemoteOK?

This actor collects only what RemoteOK publishes publicly for anyone to read, and extracts no private user data. **RemoteOK's API terms do require attribution**: if you republish or display these postings, link back to the original posting and name RemoteOK as the source. Every row carries `jobUrl` for exactly that purpose. Using this actor does not exempt you from that obligation — please read [RemoteOK's API terms](https://remoteok.com/api) before publishing what you collect.

Your results may contain personal data. You should not scrape or store personal data without a legitimate reason; if you are unsure whether yours qualifies, consult your lawyers.

#### Can I export to Excel or Google Sheets?

Yes. Every run's dataset downloads as JSON, CSV, XML or Excel from the **Storage** tab, with no code required.

#### Can I run this on a schedule?

Yes. Use Apify **Schedules** to run it daily or hourly, and deduplicate on `jobId` to build a growing dataset of everything RemoteOK has listed.

### Integrations and API

Runs can be triggered and results collected through the [Apify API](https://docs.apify.com/api/v2), the JavaScript and Python clients, or any of Apify's built-in integrations — **Zapier, Make, Slack, Google Drive, GitHub and generic webhooks**. Point a webhook at a run's completion event to push new remote jobs straight into your own system.

### Support

Found a bug, or a field you need that we do not return? Open an issue on this actor's **Issues** tab and it will reach the maintainers directly.

# Actor input Schema

## `maxJobs` (type: `integer`):

Maximum postings to return. RemoteOK's public feed is a single page of roughly 100 live postings, so that is the real ceiling — asking for more cannot return more.

## `techStackFilter` (type: `array`):

Only return jobs mentioning these technologies

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

Filter by categories (e.g., dev, design, sales)

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

Filter jobs with salary below this amount

## `regions` (type: `array`):

Filter by regions (e.g., USA, Europe, Asia)

## `failOnZeroResults` (type: `boolean`):

When on (default), a run that finds no postings FAILS with a message explaining why — too-narrow filters, an empty feed, or a change in RemoteOK's data format. Turn it off if you run this on a schedule and want quiet runs to stay green.

## Actor input object example

```json
{
  "maxJobs": 100,
  "techStackFilter": [],
  "categories": [],
  "minSalary": 0,
  "regions": [],
  "failOnZeroResults": true
}
```

# Actor output Schema

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

Every job posting this run returned from RemoteOK, one row each.

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("tqm/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 = {}

# Run the Actor and wait for it to finish
run = client.actor("tqm/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 '{}' |
apify call tqm/remoteok-scraper --silent --output-dataset

```

## MCP server setup

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