# RemoteOK Jobs Scraper — Remote Postings by Keyword (`accountable_eel/remoteok-jobs-lookup`) Actor

Search RemoteOK's live remote-job feed by keyword and get every matching posting — title, company, location, salary if listed, and the apply link. Filter by job title, location, or posted date, or watch a keyword for postings new since your last run. Charged per posting, never per search.

- **URL**: https://apify.com/accountable\_eel/remoteok-jobs-lookup.md
- **Developed by:** [Adrian Voss](https://apify.com/accountable_eel) (community)
- **Categories:** Jobs, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.50 / 1,000 job posting returneds

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 Jobs Lookup

You paste a list of search keywords — a skill, a role, a tool. For each one, this actor searches
**RemoteOK**'s live remote-job feed and returns every matching posting — title, company, location,
salary if listed, and the apply link — as one row per posting. No API key, no HTML scraping: it
reads RemoteOK's own public JSON feed directly. Optional filters narrow results by job title,
hiring location, or posting date, and an optional delta mode returns only the postings that
appeared since your last run, so you can put a keyword list on a schedule and get a "new postings"
alert instead of a full re-dump every time.

### Who it's for

Sourcers, recruiters, and outbound teams building a remote-role candidate or lead list, and anyone
watching a niche (a skill, a title, a tool) for new remote openings without checking a job board by
hand. Pricing is pay-per-event and per-posting: a fraction of a cent per returned row, and nothing
for a keyword with no matches, so a broad watchlist costs you next to nothing when it's quiet.

### Why this one

- **Direct from RemoteOK's own public feed.** No HTML scraping to break on a redesign — this calls
  `remoteok.com/api` the same JSON endpoint the site's own pages are built from.
- **You pay per posting returned, not per search.** A keyword that matches 40 postings and one that
  matches 2 both bill only for the postings actually delivered — a keyword with zero matches is a
  free miss.
- **Honest about the source's own limits.** RemoteOK's tag index is loosely curated — see "Tips"
  below — so this actor gives you a title-keyword filter to get precise results instead of quietly
  pretending the raw tag match is exact.
- **A delta mode for watching a niche over time.** Turn on `onlyNewSinceLastRun`, put the actor on
  a schedule, and every run after the first returns only postings you haven't seen — a quiet week
  still gets a row, so you can tell "nothing new" from "the run broke."

### What you get

One row per matching job posting by default (toggle "One row per job posting" off in the Input tab
to get one row per search keyword instead, with the full list nested in `jobs`). Every row carries
these fields, whether or not you've turned on filters or delta mode — the columns never move:

| Field | Type / format | Description |
| --- | --- | --- |
| `query` | text | The search keyword you passed in, unchanged. |
| `found` | boolean | `true` if the keyword matched at least one RemoteOK posting, `false` otherwise. |
| `status` | text | `OK` or `NOT_FOUND` (no postings currently match this keyword). |
| `searchTag` | text | The keyword actually searched (lowercased). |
| `jobCount` | number | How many postings matched, after your filters (and after delta filtering, if `onlyNewSinceLastRun` is on). |
| `totalJobCount` | number | How many postings RemoteOK's own tag search returned before any filtering — lets you see what a filter dropped. |
| `newJobs` | number | How many postings are new since your last scheduled run. Empty unless `onlyNewSinceLastRun` is on. |
| `firstRun` | boolean | `true` if this was the baseline run for this watchlist. Empty unless `onlyNewSinceLastRun` is on. |
| `jobs` | array | The full list of matching postings, each with the fields below. Present in every row; it's what gets expanded into separate rows in "one row per posting" mode. |
| `title` | text | Job title. |
| `company` | text | Hiring company. |
| `tags` | array | RemoteOK's own tags for the posting — see "Tips" for why these are noisy. |
| `location` | text | Where the employer is willing to hire from, as RemoteOK lists it. Empty for a fully "work from anywhere" posting. |
| `salaryMin` / `salaryMax` | number (USD) | Only present when the poster listed a range — most postings don't. |
| `postedAt` | date (ISO) | When RemoteOK published the posting. |
| `applyUrl` | link | Link to apply. |
| `description` | text (HTML) | The posting's full description, as RemoteOK returns it (raw HTML, not stripped). |
| `scrapedAt` | date (ISO) | When this actor fetched the row. |

A keyword with no current matches still comes back as one row with `found: false`, and that row is
never charged.

### Pricing

- **Job posting returned**: $1 per 1,000 job postings

Plus a $0.00005 start fee per run. Each event above is billed independently, only when it actually returns data — misses (`found:false`) are never charged.

### How to use

1. **In the Apify Console.** Open the actor page and click **Start** — the `keywords` field is already pre-filled with a working example. Results land in the run's dataset as soon as each item is found.
2. **Via the API.** Call it directly with a POST request — no Console needed once you have an API token:
   ```bash
   curl "https://api.apify.com/v2/acts/accountable_eel~remoteok-jobs-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \
     -X POST \
     -H "Content-Type: application/json" \
     -d '{"keywords":["kubernetes"]}'
   ```
3. **On a schedule.** Save this actor as an Apify **Task** with the input you want, then add a **Schedule** (hourly, daily, weekly) so it runs on its own — no server of your own required.

Paste one search keyword per line — a skill (`python`), a role (`marketing`), or a tool
(`kubernetes`). Each keyword is matched against RemoteOK's own tag search.

To narrow the postings you get back, open the **🎯 Job filters** section of the Input tab. All of
these are optional and combine with AND across fields, OR within a field:

| Input | What it does |
| --- | --- |
| `titleKeywords` | Keep only postings whose title contains at least one of these — `["engineer","designer"]`. Recommended: RemoteOK's tag matching is loose (see Tips below), so this is the reliable way to get precise results. |
| `excludeTitleKeywords` | Drop postings whose title contains one of these — `["intern","unpaid"]`. Applied after `titleKeywords`. |
| `locations` | Keep only postings whose hiring-location field contains one of these — `["United States","Europe"]`. Drops "work from anywhere" postings (no location listed) — leave empty to keep them. |
| `postedAfter` | Keep only postings published on or after this date — `"2026-06-01"`. |

Leave every filter empty and you get every posting RemoteOK's tag search returned for that keyword,
exactly as before filters existed. `jobCount` (matched) and `totalJobCount` (before filtering) both
come back on every row, so a filtered result is never ambiguous.

For a standing watchlist, turn on **`onlyNewSinceLastRun`** in the **🔁 Watch for new postings**
section and put the actor on a schedule. The first run baselines (returns everything matching your
filters, `firstRun: true`); every run after that returns only postings it hasn't shown you before,
counted in `newJobs`. A quiet run still returns a row per keyword (`jobCount: 0`, `newJobs: 0`) so
you can tell "nothing new" from "the run failed."

### Input

```json
{
  "keywords": [
    "kubernetes"
  ]
}
```

One per line. Matched against RemoteOK's tag index. Add a "Job title must contain" filter below for tighter, title-verified results.

### Sample output

| query | found | status | searchTag | jobCount | totalJobCount | newJobs | firstRun | jobs | title | company | tags | location | salaryMin | salaryMax | postedAt | applyUrl | description | scrapedAt |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| marketing | true | OK | marketing | 100 | 100 |  |  | \<all matching postings (full list)> | Customer Service Agent | Thrifty Car and Van Rental | \["testing","sys admin","education","finance","scheme","non tech","customer support","marketing","admin","ops"] | Exeter,  |  |  | 2026-08-30T03:31:01+00:00 | https://remoteOK.com/remote-jobs/remote-customer-service-agent-thrifty-car-and-van-rental-1137219 | <p><strong>Job Title:</strong><span><span><strong>Customer Service Agent</strong></span></span></p><p><strong>Location: Exeter Head Office - Full Time</strong></p><p><span><strong>Hours: 40 hours Monday-Friday + Saturday shift rota</strong></span></p><p><strong>Benefits:</strong></p><ul><li><strong>22 days holiday, increasing to 27 plus bank holidays</strong></li><li><strong><span>Free onsite parking</span></strong></li><li><strong><span>Free onsite gym</span></strong></li><li><strong><span>Company pension scheme</span></strong></li><li><strong><span>Employee discount scheme</span></strong></li><li><strong><span>Funded Summer and Christmas events </span></strong></li><li><strong><span>Cycle to Work Scheme</span></strong></li><li><strong><span>Discounted car hire rates</span></strong></li></ul><p><strong>The Role:</strong></p><p>You will be a valued member of a team, providing support through a variety of communications, accurate recording of data and various tasks that impact on our services, taking inbound and outbound calls from our branches and customers. You will be creating an exceptional experience for customers, whilst building trusting relationships and handling queries with confidence to provide all National Account customers with a dedicated vehicle hire reservation service.</p><p>No prior experience is necessary, full training will be given.</p><p>This is an office-based role. The candidate will be required to work 40 hours per week as well as Saturdays on a rota basis.</p><p><strong>Responsibilities:</strong></p><ul><li><span>To provide outstanding customer service and exhibit excellent communications skills when interacting with clients</span></li><li><span>Regularly monitor personal and team inboxes and respond to emails</span></li><li><span>Ensure all customer complaints are resolved to the customers satisfaction</span></li><li><span>To process data accurately using a variety of <span>inhouse </span>systems</span></li><li><span>Answer calls and promptly resolve any requests or queries</span></li></ul><p><span><span><strong>The Candidate:</strong></span></span></p><ul><li><span><span><span>Happy to work independently, as well as in a<span> part of the</span> team</span></span></span></li><li><span><span><span>Excellent communication skills</span></span></span></li><li><span><span><span>Strong organisational skills and attention to detail</span></span></span></li><li><span><span><span>Ability to multitask</span></span></span></li><li><span><span><span>IT literate</span></span></span></li></ul><ul><li><strong><span>About Us:</span></strong><span>Thrifty is the largest privately-owned leasing company in the UK, with 81 locations nationwide. Our Head Office is the central hub of our operations, and we are searching for an exceptional administrator to join our team.</span></li><li><span>As one of the leading rental companies in the UK, we owe our success to a comprehensive fleet and a commitment to excellent customer service. With our growth comes exciting opportunities to become part of our family business, where our peopleremainat the heart of everything we do.</span></li><li><span>Shape the future with us<strong>APPLY NOW</strong>and join our team!<br><br></span></li></ul><p>LNKD1\_UKTJ</p><br/><br/>Please mention the word **BOLSTER** and tag RMTczLjIzNC40OC45Ng== when applying to show you read the job post completely (#RMTczLjIzNC40OC45Ng==). This is a beta feature to avoid spam applicants. Companies can search these words to find applicants that read this and see they're human. | 2026-08-31T07:00:43.812Z |

### Use it from Clay, n8n, Make, or an AI agent

This actor runs synchronously over plain HTTP — call it directly from a script, a workflow tool, or an AI agent, no Apify Console needed once you have an API token.

```bash
curl "https://api.apify.com/v2/acts/accountable_eel~remoteok-jobs-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{"keywords":["kubernetes"]}'
```

**n8n.** Add an HTTP Request node: Method `POST`, URL `https://api.apify.com/v2/acts/accountable_eel~remoteok-jobs-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>`, Body Content Type `JSON`, JSON Body `{"keywords":["kubernetes"]}` (swap in an expression from an earlier node for a real value).

**Clay.** Add an "HTTP API" column: Method `POST`, URL `https://api.apify.com/v2/acts/accountable_eel~remoteok-jobs-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>`, Body `{"keywords":["{{search keyword}}"]}`, mapping the row's search keyword into the `keywords` array.

**MCP.** In Claude, Cursor, or any MCP client with the Apify MCP server, ask for "RemoteOK Jobs Lookup | Apify" — the agent will find and run this actor.

### Tips

- **Add a `titleKeywords` filter for precision.** RemoteOK's tag index is self-selected by posters
  and loosely curated — verified live: a search for `javascript` returned 100 postings carrying a
  literal "javascript" tag, but 0 of them had a JavaScript-relevant title. Set `titleKeywords` to
  the words you'd actually expect in a matching job title to cut through this.
- **Put delta mode on a daily or weekly schedule for a niche-hiring alert.** `onlyNewSinceLastRun`
  plus a fixed keyword list turns this into a "new remote roles for X" feed — you only see what's
  new since last time, and a quiet day still confirms the schedule is running.
- **`salaryMin`/`salaryMax` are usually empty.** Most RemoteOK postings don't list a range; treat a
  present value as a bonus, not something to filter on by default.
- **Every result is already remote.** There's no `remoteOnly` filter here (unlike this catalogue's
  ATS actors) because every RemoteOK posting already is one — `locations` filters *where the
  employer is willing to hire from*, not remote-vs-onsite.

### FAQ

**Why does `found` say false, or a row come back empty?**
The keyword currently matches zero postings in RemoteOK's tag search (`status: NOT_FOUND`). Try a
broader or different keyword.

**Was I charged for that row?**
Only for postings actually returned — `jobCount` postings per keyword found, at the per-posting
price. A keyword matching zero postings is free.

**I got charged for a keyword that returned `jobCount: 0` — why?**
That can only happen with filters or delta mode: RemoteOK's tag search found postings
(`totalJobCount` > 0), but your `titleKeywords`/`locations`/`postedAfter` filtered all of them out,
or none were new since your last delta run. Zero *rows* means zero *charge* either way — this
question only applies to the "one row per keyword" display mode, where a keyword still gets a
summary row even when its `jobs` list is empty.

**Why are the tag matches sometimes irrelevant to my keyword?**
See "Tips" above — RemoteOK's own tag index is loosely curated. Use `titleKeywords` for a
title-verified result.

**How does the delta / "new since last run" state work, and can I reset it?**
It lives in a named key-value store, `remoteok-jobs-lookup-state`, inside your own Apify account.
Delete that store to reset every watchlist. Changing your filters also starts a fresh watchlist
automatically, since filter values are part of what identifies a given seen-list.

**Does this actor hit rate limits?**
It calls RemoteOK's public JSON API once per keyword, with its own timeout and retry handling. No
proxies are needed — the endpoint is public and unauthenticated.

**Can an AI agent call this directly?**
Yes — it's registered with the Apify MCP server. See "Use it from Clay, n8n, Make, or an AI agent"
above.

***

*Data sourced from RemoteOK's public API. Per RemoteOK's API terms, this actor and its output link
back to [remoteok.com](https://remoteok.com) and credit RemoteOK as the source — please do the same
if you republish results.*

# Actor input Schema

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

One per line. Matched against RemoteOK's tag index. Add a "Job title must contain" filter below for tighter, title-verified results. You're only charged for the ones we actually find — a miss costs nothing.

## `testRun` (type: `boolean`):

Turn this on to test your input on a small sample before running the full list. Turn it off to process everything.

## `onlyFound` (type: `boolean`):

Only keep rows where something was actually found. Misses are always free, whether or not you show them here.

## `includeKeywords` (type: `array`):

Optional. Only keep results that mention at least one of these words (e.g. a job title, a city, a product name). Leave empty to keep everything.

## `excludeKeywords` (type: `array`):

Optional. Drop any result that mentions one of these words. Leave empty to skip nothing.

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

Optional. Stop the run once this many results have been found — useful for a quick, cheap sample. Leave blank for no limit.

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

Recommended. RemoteOK's own tag index is loosely curated — many postings carry broad generic tags for visibility, so a search keyword alone can return roles beyond the exact skill. Keep only postings whose title contains at least one of these words for a precise match. Case doesn't matter, partial words work. Leave empty to keep everything RemoteOK's tag search returned.

## `excludeTitleKeywords` (type: `array`):

Optional. Drop any posting whose title contains one of these words — e.g. "intern", "unpaid". Applied after the include list above, so a title matching both is dropped.

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

Optional. Keep only postings whose location mentions one of these — e.g. "United States", "Europe", "UK". Every RemoteOK posting is remote by definition; this filters WHERE the employer is willing to hire from, not whether it's remote. Postings with no location at all (fully "work from anywhere") are dropped by this filter, since they can't be shown to mention any specific place — leave this empty to keep them.

## `postedAfter` (type: `string`):

Optional. A date like 2026-06-01 (or a full timestamp). Keeps only postings RemoteOK published on or after it. Leave empty for no date limit.

## `onlyNewSinceLastRun` (type: `boolean`):

Turn this on for a scheduled watchlist: the first run returns everything that matches and remembers it, and every run after that returns only postings it hasn't shown you before. A keyword with nothing new still gets a row (with 0 postings), so a quiet schedule never looks like a broken one. Changing the filters above starts a fresh watchlist.

## `columns` (type: `array`):

Choose which pieces of information to include in each result row. All are included by default.

## `expandRows` (type: `boolean`):

When on, each job posting found gets its own row instead of being grouped under its search keyword. You're still only charged once per search keyword, no matter how many rows it produces.

## `maxConcurrency` (type: `integer`):

Parallel requests. Keep conservative — this target has no browser fallback, so getting blocked costs more than slow-and-steady.

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

Apify Proxy config. Residential recommended for anti-bot-sensitive targets.

## Actor input object example

```json
{
  "keywords": [
    "kubernetes"
  ],
  "testRun": false,
  "onlyFound": false,
  "includeKeywords": [],
  "excludeKeywords": [],
  "titleKeywords": [],
  "excludeTitleKeywords": [],
  "locations": [],
  "postedAfter": "",
  "onlyNewSinceLastRun": false,
  "columns": [
    "searchTag",
    "jobCount",
    "totalJobCount",
    "newJobs",
    "firstRun",
    "jobs",
    "title",
    "company",
    "tags",
    "location",
    "salaryMin",
    "salaryMax",
    "postedAt",
    "applyUrl",
    "description"
  ],
  "expandRows": true,
  "maxConcurrency": 5,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `results` (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 = {
    "keywords": [
        "kubernetes"
    ],
    "includeKeywords": [],
    "excludeKeywords": [],
    "titleKeywords": [],
    "excludeTitleKeywords": [],
    "locations": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("accountable_eel/remoteok-jobs-lookup").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": ["kubernetes"],
    "includeKeywords": [],
    "excludeKeywords": [],
    "titleKeywords": [],
    "excludeTitleKeywords": [],
    "locations": [],
}

# Run the Actor and wait for it to finish
run = client.actor("accountable_eel/remoteok-jobs-lookup").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": [
    "kubernetes"
  ],
  "includeKeywords": [],
  "excludeKeywords": [],
  "titleKeywords": [],
  "excludeTitleKeywords": [],
  "locations": []
}' |
apify call accountable_eel/remoteok-jobs-lookup --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,accountable_eel/remoteok-jobs-lookup"
        }
    }
}

```

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/ZfTji8XXkCQLNTjYg/builds/eDNgZSYgJGKxtmCPt/openapi.json
