# RemoteOK Jobs Scraper — Official Feed, Verified Remote (`axery/remoteok-jobs-scraper`) Actor

Scrape Remote OK's official public job feed with repaired text encoding, verified-remote detection, and the attribution their API terms require.

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

## Pricing

from $0.60 / 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

## Remote OK Jobs Scraper

Scrapes **remoteok.com**'s official public JSON feed over plain HTTP — no browser, no login, no cookies. This is an invited integration: Remote OK publishes and documents the feed itself.

### Read this first: a data-quality warning

**Remote OK's free public feed does not match its website.** In a live check of the first 12 listings, **none were actually remote roles** — they were on-site jobs like *Store Manager, Bunbury*, *Removalist Offsider, Albury NSW*, *Orderly, Alice Springs* and *Loss Prevention Officer, Malé*. Every one of them still arrives branded as remote, with a slug prefixed `remote-`.

This Actor does not pass that branding through as fact. It reads each listing's own title, tags and description and reports:

- `location.remote` — the evidence-based judgement
- `board_claims_remote` — always `true`, the board's assertion

Both are emitted so you can see the disagreement and decide which to trust. The `verifiedRemote` dataset view filters to rows where the text supports the claim.

The board's tags are similarly unreliable: `digital nomad` is auto-applied to listings including on-site retail jobs, so the detector ignores it.

**If you need genuinely remote roles at volume, use the Himalayas Remote Jobs Scraper instead** — same suite, same schema, ~100,000 listings whose remote status holds up.

### Attribution is a licence condition, not a courtesy

Remote OK's API terms, returned in the feed itself:

> 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. If you do not we'll have to suspend API access. Please don't use the Remote OK logo without written permission as it's a registered trademark, please DO use our name Remote OK though.

Two obligations follow, and this Actor is built around them:

1. Every row carries `apply_url` (the canonical Remote OK link) and an `attribution` block, so the obligation travels with the data. **If you republish, render that link as a followed link and credit Remote OK.**
2. The Remote OK logo is never emitted. `company_logo` is the advertiser's own asset, which is a different thing.

### What this Actor adds

**Repaired text.** The feed returns double-encoded text — `Bachelorâs` where the advertiser wrote `Bachelor's`. Titles, companies and descriptions are repaired, and the repair is applied only when the tell-tale byte sequences are present, so genuinely accented text is not corrupted.

**Readable descriptions.** HTML fragments are flattened to plain text with paragraph breaks preserved; the original HTML is kept alongside.

**Honest salary labelling.** `is_estimated` is `true` whenever a figure is present, because Remote OK derives these bands itself rather than taking a number the advertiser typed.

**One schema across boards.** Same record shape as the SEEK, JobStreet and Himalayas Actors, with a source-prefixed `job_id`.

### Input

| Field | Type | Notes |
|---|---|---|
| `queries` | array | Keywords over title, company, tags, description. Empty = whole feed. |
| `tags` | array | Weak filter — see the tag warning above. |
| `maxItems` | integer | The feed holds ~100 listings total; asking for more returns all of them. |
| `includeDescription` | boolean | Off gives a smaller dataset; the remote check is unaffected. |
| `incremental` | boolean | Only listings not seen in previous runs. |
| `proxyConfiguration` | object | Datacenter is enough. |

### Known limits

- **No pagination and no query parameters.** The public feed is a single snapshot of roughly the latest 100 listings. Keyword and tag filters are applied to that snapshot client-side. `maxItems` can only narrow it, never extend it.
- **Feed contents are largely not remote.** See the warning above.
- **No structured location.** The feed gives a bare place string, so `country_code` and `region` are null.

### Sister Actors

| Actor | Covers |
|---|---|
| Himalayas Remote Jobs Scraper | 100,000+ worldwide remote roles — the better choice for remote work |
| SEEK Jobs Scraper | Australia, New Zealand |
| JobStreet & JobsDB Jobs Scraper | Indonesia, Malaysia, Singapore, Philippines, Thailand, Hong Kong |

### Local development

```bash
pip install -r requirements.txt
python test_local.py --max 20 --out sample_output.json
python test_local.py engineer --tags python --max 10
```

`sample_output.json` in this folder is real output from a live run, kept so the schema — and the data-quality problem — can be reviewed without running anything.

# Actor input Schema

## `queries` (type: `array`):

Keywords matched against title, company, tags and description. Leave empty to return the whole current feed, which is the usual choice given how small it is.

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

Keep only listings carrying any of these Remote OK tags, e.g. `python`, `design`, `marketing`. Treat as a weak filter: the board auto-applies tags such as `digital nomad` to listings regardless of the actual role.

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

Maximum listings to return per keyword. The public feed holds roughly 100 listings in total, so asking for more simply returns everything available — that is not an error.

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

Include the full description as both plain text and HTML. The remote/on-site check reads the description, so turning this off does not affect its accuracy.

## `incremental` (type: `boolean`):

Remember job ids between runs and return only listings not seen before. You are charged only for the new rows. Ids are stored in this Actor's key-value store under `seen_job_ids`.

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

Apify Proxy settings. Remote OK publishes this feed openly, so Apify's automatic proxy selection is enough.

## Actor input object example

```json
{
  "queries": [
    "engineer"
  ],
  "tags": [
    "python"
  ],
  "maxItems": 100,
  "includeDescription": true,
  "incremental": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `jobs` (type: `string`):

One row per listing: title, company, location, estimated USD salary, tags, description and both the Remote OK and direct apply URLs.

## `coverage` (type: `string`):

What the run asked for versus what it returned, plus the attribution obligation that applies to this data.

# 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 = {
    "maxItems": 100
};

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

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

```

## MCP server setup

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