# Remote Jobs Scraper: Remote OK & Jobicy + New Job Alerts (`fairfetch/remote-jobs-scraper`) Actor

Remote jobs from Remote OK and Jobicy in one clean schema: salaries, where applicants may live, seniority, cross-board de-duplication and new/closed job tracking between runs. Board attribution built in, no personal-data fields. $1 per 1,000 jobs.

- **URL**: https://apify.com/fairfetch/remote-jobs-scraper.md
- **Developed by:** [Naveen Raja](https://apify.com/fairfetch) (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 Jobs Scraper: Remote OK & Jobicy in one feed, with new/closed job alerts

Get **remote jobs from Remote OK and Jobicy in one clean JSON format**: title, company, category, seniority, employment type, **where applicants may live**, **salary ranges**, posted date, a clean-text description and the board's own job link. The Actor reads each board's **official public API** (no browser, no login, no proxy), merges the same job posted on both boards into one row, and credits the board on every row.

Switch on **change tracking** and every later run returns only the jobs that are **new** since your last run (and the ones that **closed**). Schedule it daily and you have a remote-job alert.

**Price: $1 per 1,000 jobs** ($0.001 per job). You pay only for jobs returned. Apify platform costs are included.

| What you run | Jobs | Cost |
|---|---|---|
| 100 newest remote jobs | 100 | $0.10 |
| Everything both boards list right now (tested 18 Oct 2026) | about 920 | about $0.92 |
| Daily "only new jobs" alert across both boards, no filters | about 120 a day | about $0.12 a day |
| Daily alert for one role, e.g. `python` + Europe | a few a day | under $0.01 a day |

### What you get

- **One schema for both boards**: the same fields whether the job comes from Remote OK or Jobicy.
- **Cross-board de-duplication**: a job listed on both boards becomes one row; the other board's link is kept in `alsoListedOn`.
- **Eligibility you can filter on**: countries and regions applicants must be in (`locationRestrictions`, `isWorldwide`). Office cities are not mistaken for eligibility: they stay in `locationText`.
- **Salaries**: `salaryMin`, `salaryMax`, `salaryCurrency`, `salaryPeriod` (year / month / hour...).
- **Shared categories and seniority**: 19 categories (Software Development, Data & AI, Design, Customer Support...) and levels from intern to executive, taken from the board's category and the job title.
- **Change tracking**: `new`, `existing` and `closed` status with the time a job was first seen; "only new" mode so a daily alert costs cents.
- **Clean text descriptions** from HTML, with e-mail addresses and phone numbers in common formats removed. No personal-data fields (no recruiter names or contact details).
- **Reliable**: if one board is down or changes its format, the other still delivers and the run summary says what failed.
- Built for automation and AI agents: predictable schema, pay-per-event pricing without usage fees, limited permissions, a `SUMMARY` record per run, and a clean stop at your spending limit.

### Job boards and attribution

| Board | What the Actor reads | Typical size | Closed-job detection | Attribution in every row |
|---|---|---|---|---|
| [Remote OK](https://remoteok.com) | Official API (`remoteok.com/api`) | Newest ~100 jobs | Not available (the feed only shows the newest jobs) | `sourceName` "Remote OK" + job link on remoteok.com |
| [Jobicy](https://jobicy.com) | Official Jobs API v2 | Every job from the last 7 days (about 800) | Confirmed by Jobicy's status endpoint | `sourceName` "Jobicy" + canonical Jobicy job link |

Both boards allow their public API to be used in other products **as long as the board is credited with a link to its job page**. Every row therefore carries `source`, `sourceName`, `sourceUrl`, the board's job `url`, an `applyUrl` that goes through the board, and a ready-made `attribution` line. When you display the jobs, keep the board's name and link (see [Legal and attribution](#legal-and-attribution)).

Other remote boards (Remotive, Himalayas, We Work Remotely, Working Nomads, Arbeitnow) were checked and are **left out**, because their terms restrict, or don't clearly allow, passing their jobs on through a paid tool like this one.

### Use cases

- **Remote job alerts**: a daily run with your keywords, eligibility and "only new" returns just the fresh jobs, ready for e-mail, Slack or a spreadsheet.
- **Job boards, newsletters and communities**: a clean, de-duplicated feed with the attribution the boards ask for already in each row.
- **Salary and market research**: compare published pay by category, seniority and region.
- **Hiring signals**: see which companies are hiring remotely and for which roles.
- **AI agents and LLM apps**: a dependable tool for "find senior Python jobs open to Germany posted this week".

### How to use

1. Pick the **job boards** (both by default).
2. Optional: add **filters**: keywords, excluded words, categories, where you can work from, employment types, seniority, minimum salary, posted within N days.
3. Choose whether to **include the description** (on by default) and set **Max jobs** (newest first).
4. Optional: turn on **Track new and closed jobs** and **Return only new jobs**. Use the same **Tracking ID** for every run of the same alert.
5. Run it, then download the results as JSON, CSV or Excel, or read them through the API. A per-board summary is saved as the `SUMMARY` record in the run's key-value store.

#### Input examples

All remote jobs from both boards (the default):

```json
{}
```

Only Jobicy, newest 200 software jobs:

```json
{
  "sources": ["jobicy"],
  "categories": ["Software Development"],
  "maxJobs": 200
}
```

Senior Python or data engineering jobs that someone in Germany can apply for, posted in the last 3 days, paying at least $100,000 a year:

```json
{
  "keywords": ["python", "data engineer"],
  "locations": ["Germany"],
  "seniority": ["senior", "lead"],
  "minSalary": 100000,
  "minSalaryCurrency": "USD",
  "postedWithinDays": 3,
  "includeDescription": true
}
```

A daily alert that returns only new design and product jobs (schedule it once a day):

```json
{
  "categories": ["Design", "Product"],
  "excludeKeywords": ["intern"],
  "trackChanges": true,
  "onlyNew": true,
  "trackingId": "design-product-alert"
}
```

#### Filters in detail

- **Keywords** match whole words in the **job title** (`go` does not match "Google"); turn on **Also search keywords in the job description** to search descriptions too. Board tags and board categories are not searched: Remote OK tags many jobs with unrelated words (a sales job tagged "engineer" or "golang"), and Jobicy files sales and marketing jobs under one "Marketing & Sales" category. Use **Categories** to filter by field.
- **Exclude keywords** look at the title and the company name.
- **Where you can work from**: a job open to a region matches its countries (open to "Europe" matches "Germany"; open to "EMEA" matches "Nigeria"), and jobs open worldwide match too unless you switch **Include jobs open worldwide** off. A job limited to one country does not match a search for the wider region. Names are compared as whole names ("India" does not match "Indiana"); "EU" and "Europe" are treated as the same search.
- Remote OK's location is free text. Only recognised countries and regions become eligibility ("USA only" and "Remote UK" become the country; US states and Canadian provinces become their country). An office city such as "Boston" leaves the eligibility **unknown** (`locationRestrictions: []`, `isWorldwide: null`), with the text kept in `locationText`.
- **Seniority** comes from the job title (Senior, Staff, Lead, Head of, VP, Junior, Intern...), or from Jobicy's level where it is reliable.
- **Minimum salary** compares the published maximum, converted to a yearly amount (hourly x 2,080, monthly x 12; an amount without a published period is read by its size, so 25 counts as hourly), in the currency you choose. There is no exchange-rate conversion: jobs paid in another currency are dropped.
- Boards don't publish every field (Remote OK rarely gives the employment type, for example). By default such jobs are **kept** when you filter on that field; switch on **Strict filters** to drop them.

#### Output example

A real item from a test run on 18 Oct 2026 (description shortened):

```json
{
  "id": "jobicy:155603",
  "source": "jobicy",
  "sourceName": "Jobicy",
  "sourceUrl": "https://jobicy.com",
  "sourceJobId": "155603",
  "title": "Engineering Manager, SupportX",
  "company": "SeatGeek",
  "companyLogo": "https://jobicy.com/data/server-nyc0409/galaxy/mercury/2025/06/72d8f3a6-221.jpeg",
  "category": "Software Development",
  "sourceCategory": "Software Engineering",
  "tags": [],
  "employmentType": "full-time",
  "seniority": "senior",
  "locationRestrictions": ["United States"],
  "isWorldwide": false,
  "locationText": "USA",
  "timezones": [],
  "salaryMin": 171000,
  "salaryMax": 248000,
  "salaryCurrency": "USD",
  "salaryPeriod": "year",
  "postedAt": "2026-10-17T14:06:35Z",
  "url": "https://jobicy.com/jobs/155603-engineering-manager-supportx-2",
  "applyUrl": "https://jobicy.com/jobs/155603-engineering-manager-supportx-2",
  "attribution": "Job via Jobicy: https://jobicy.com/jobs/155603-engineering-manager-supportx-2",
  "alsoListedOn": [],
  "status": "open",
  "firstSeenAt": null,
  "scrapedAt": "2026-10-17T21:40:30Z",
  "description": "SeatGeek believes live events are powerful experiences that unite humans. With our technological savvy and fan-first attitude we’re simplifying and modernizing ..."
}
```

When the same job is on both boards, the Jobicy copy owns the row and the Remote OK copy is listed in `alsoListedOn` as `{"source": "remoteok", "sourceName": "Remote OK", "sourceJobId": "...", "url": "https://remoteok.com/remote-jobs/..."}`; fields Jobicy leaves empty are filled from the Remote OK copy.

A **closed** job (tracking on) has the same shape with `"status": "closed"`. It keeps the title, company, category, eligibility, seniority, employment type, salary, links, posted date and `firstSeenAt`; the description and tags are `null` / empty because the posting is gone.

#### Output fields

| Field | Description |
|---|---|
| `id`, `source`, `sourceJobId` | Unique key (`source:sourceJobId`), the board that owns the row, and the job ID on that board |
| `sourceName`, `sourceUrl`, `attribution` | Board name and home page, and a ready-made credit line |
| `title`, `company`, `companyLogo` | As published (the logo is the employer's, as hosted by the board) |
| `category`, `sourceCategory` | Shared category, and the board's own category (Jobicy) |
| `tags` | The board's tags as published (Remote OK; often unrelated to the role, not used for filtering) |
| `employmentType` | full-time, part-time, contract, freelance, internship or temporary |
| `seniority` | intern, junior, mid, senior, lead, director or executive |
| `locationRestrictions`, `isWorldwide`, `locationText` | Countries / regions applicants must be in (`["Worldwide"]` if anyone may apply, `[]` if unknown), and the board's raw location text |
| `timezones` | Time-zone requirements, e.g. `["USA timezones"]`, when the location text names them |
| `salaryMin`, `salaryMax`, `salaryCurrency`, `salaryPeriod` | Published pay range, currency and period (`null` when the board gives no period) |
| `postedAt` | ISO 8601 (UTC) |
| `url`, `applyUrl` | The job page on the board, and the apply link (always through the board) |
| `alsoListedOn` | The other board listing the same job: `source`, `sourceName`, `sourceJobId`, `url` |
| `description` | Clean plain-text description (when **Include job description** is on) |
| `status` | `open` (tracking off), or `new` / `existing` / `closed` (tracking on) |
| `firstSeenAt`, `scrapedAt` | When this tracker first returned the job, and when this run read it |

The dataset has ready-made views: **Jobs**, **New & closed**, **Salaries** and **All fields**.

### How change tracking works

With **Track new and closed jobs** on, the Actor remembers the jobs it returned in a named key-value store in your Apify account (`remote-jobs-tracking`, one record per board and Tracking ID). On the next run:

- **new**: a job ID this tracker has not returned before. A new posting on the same board is new even if an earlier one had the same title; a job this tracker already returned from the **other** board (same company and title) is not new.
- **existing**: returned before and still listed
- **closed**: returned before and now gone, reported once and then forgotten. How "gone" is confirmed depends on the board:
  - **Jobicy**: its feed only covers the last 7 days, so a job that leaves the feed is checked with Jobicy's status endpoint (at most once a day per job) and reported only when Jobicy says it is closed. If the same job is still on Remote OK, it is not reported as closed.
  - **Remote OK**: not available. Its feed only shows the newest ~100 jobs, so a job dropping out of it may still be open; such jobs are forgotten quietly after 30 days.

Good to know:

- The first run returns every job as `new`. **Return only new jobs** then skips `existing` jobs, so a daily alert costs only for what changed.
- Only jobs that pass your filters are remembered. If you widen the filters later, newly matching jobs show up as `new`.
- Closed jobs are reported for every job the tracker returned, whatever today's filters say: each one matched your filters when it was returned.
- If a board can't be read in a run, its stored state is left untouched: you get no false "closed" or "new" rows for it.
- If a run stops at your spending limit, the jobs not yet returned are reported in the next run.
- Use different **Tracking IDs** for independent alerts.

### Pricing

Pay per event, no subscription and no usage fees:

| Event | Price |
|---|---|
| `job`: one job in the dataset (open, new, existing or closed) | **$0.001** ($1 per 1,000) |
| Actor start (standard Apify event) | $0.00005 per run (one event per GB of run memory; 1 at the default 256 MB) |

- Apify platform costs (compute, storage, data transfer) are included.
- The run summary (`SUMMARY` record), merged duplicates and failed boards cost nothing; a run with an invalid input is charged only the Actor start event.
- Set **Maximum cost per run** in the run options (or `maxTotalChargeUsd` via API) to cap spending. The Actor stops cleanly at the cap, and everything returned until then is in the dataset.

### Use it from the API, an app or an AI agent

Run synchronously and get the jobs in one call (keep your own API token in the `APIFY_TOKEN` environment variable):

```bash
curl -X POST "https://api.apify.com/v2/acts/fairfetch~remote-jobs-scraper/run-sync-get-dataset-items?maxTotalChargeUsd=0.50" \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"keywords": ["python"], "locations": ["Europe"], "postedWithinDays": 7, "includeDescription": false}'
```

Python (`pip install apify-client`, version 3):

```python
import os
from decimal import Decimal

from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("fairfetch/remote-jobs-scraper").call(
    run_input={"categories": ["Data & AI"], "trackChanges": True, "onlyNew": True, "trackingId": "data-jobs"},
    max_total_charge_usd=Decimal("0.20"),
)
for job in client.dataset(run.default_dataset_id).iterate_items():
    print(job["status"], job["title"], job["company"], job["url"])
```

The Actor runs with limited permissions and pay-per-event pricing without usage fees, so AI agents can call it through the Apify MCP server and pay per run.

### Performance and limits

- A full read of both boards takes about 5 seconds of work (about 9 seconds of platform run time) and 6 requests: one for Remote OK and about 5 pages of 200 jobs for Jobicy. Peak memory was about 110 MB.
- With **Max jobs** and no tracking, Jobicy paging stops as soon as enough newer matching jobs are found (a 10-job search needs one request per board and runs in about 4 seconds).
- **Jobicy allows one automated pass over its feed per hour.** The Actor enforces this per Apify account: the last pass is saved in a named key-value store in your account (`remote-jobs-cache`, compressed, 0.4-2 MB; the record stays there until the next pass replaces it, and the store is only created by runs that read Jobicy). A run that starts less than 55 minutes after that pass reuses it, continuing the same pass with its cursor if it needs more pages, so Jobicy jobs can be up to about an hour old in such a run. An hourly schedule gets a fresh pass on every run; runs scheduled more often share one pass per hour. The `SUMMARY` record says when a pass was reused.
- Requests within a run are polite: one at a time per board, at least 1 second apart (Remote OK's crawl delay), with retries and back-off for errors and rate limits.
- Volume depends on the boards: roughly 900 to 1,000 distinct jobs at a time, about 120 new ones a day. Remote OK's API only shows its newest ~100 jobs; Jobicy's covers 7 days (with a 3-hour delay).
- Runs from all users share Apify's IP addresses, so very frequent schedules by many users could still make a board throttle requests. Daily runs are plenty for alerts.

### FAQ

**Do I need accounts or API keys for these boards?**
No. The Actor uses the boards' public APIs, which need no key.

**Do I need proxies?**
No. The APIs are public and the Actor keeps request rates low, so there is no proxy option.

**Why are some fields empty?**
Boards publish different data. Remote OK has tags and free-text locations but rarely an employment type; Jobicy has salary, level, type and eligibility but no tags. The Actor fills what each board publishes and, for copies on both boards, merges the gaps.

**How is a duplicate detected?**
Same company and same job title (after normalising spelling such as "Sr" / "Senior", "Full-stack" / "Full stack" and removing "(Remote)"). Two jobs on the same board are never merged.

**Can I get only jobs I can apply to from my country?**
Yes: put your country in **Where you can work from**. Jobs open to your region or worldwide are included; switch on **Strict filters** to also drop jobs whose eligibility is unknown.

**Why isn't Remotive, Himalayas, We Work Remotely or Working Nomads included?**
Their terms restrict, or don't clearly allow, redistributing their listings through a paid third-party tool or aggregator, so they are left out on purpose.

**What happens if a board is down?**
The other board still runs. The failure and its reason are in the `SUMMARY` record and the status message. The run only fails if every selected board failed on its side; an invalid input finishes with a clear message instead.

### Legal and attribution

This Actor reads job postings only through the official public APIs that Remote OK and Jobicy offer for reuse. Per Apify account it starts a new Jobicy pass about once an hour at most, following Jobicy's one-pass-per-hour guidance (see Performance and limits), and it paces requests within each run. It does not log in or bypass access controls, and it has no personal-data fields. E-mail addresses and phone numbers in common formats in descriptions are replaced with `[email removed]` / `[phone removed]`; descriptions are otherwise the employer's own text and may still name a contact person.

The boards' terms ask that you credit them when you show their jobs. If you publish or display the data:

- **Keep the link back** to the job's page on the board (`url`) and **name the board** as the source (`sourceName`, or use the `attribution` line). Remote OK asks for a normal followed link (no `nofollow`) and does not allow use of its logo without permission.
- Send applicants through the board's job page (`applyUrl`), not straight to the employer.
- Jobicy asks that you don't present its listings as your own postings.

You are responsible for how you use the data and for following the boards' terms and the laws that apply to you.

This Actor is not affiliated with, endorsed by or sponsored by Remote OK or Jobicy. Their names are used only to describe where the data comes from.

# Actor input Schema

## `sources` (type: `array`):

Boards to read; leave empty (or omit via API) for both. Remote OK: its newest ~100 jobs. Jobicy: every job published in the last 7 days (the largest feed). Jobicy allows one automated read per hour, so a run of your account less than 55 minutes after the last Jobicy read reuses it.

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

Keep jobs whose title contains at least one of these words (whole words, case-insensitive: "python" does not match "pythonic", "go" does not match "Google"). Board tags are not searched because Remote OK tags many jobs with unrelated words. Leave empty for all jobs.

## `searchDescription` (type: `boolean`):

Match keywords in the job description too. Finds more jobs, but also jobs that only mention the word in passing.

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

Drop jobs whose title or company contains any of these words, e.g. "senior", "intern" or a company name.

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

One shared category list for both boards. Jobicy's category is mapped to it (the title decides within a combined one such as "Marketing & Sales"); Remote OK has no categories, so its jobs are categorised from the title. Jobs whose category can't be told are dropped by this filter. The board's own category is kept in sourceCategory.

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

Countries or regions applicants must be in, e.g. "Germany", "United States", "Europe", "LATAM" or "Worldwide". A job open to a region matches its countries (open to Europe matches Germany); names are matched as whole words ("India" doesn't match "Indiana"). Jobs whose eligibility is unknown (no location, or only an office city on Remote OK) are kept unless "Strict filters" is on.

## `includeWorldwide` (type: `boolean`):

When you filter by location, also keep jobs that accept applicants from anywhere.

## `employmentTypes` (type: `array`):

Remote OK rarely publishes the type, so its jobs are kept unless "Strict filters" is on.

## `seniority` (type: `array`):

Taken from the job title (Senior, Staff, Lead, Head of, VP, Junior, Intern...) or the board's level where it is reliable. Jobs without a clear level are kept unless "Strict filters" is on.

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

Keep jobs whose published maximum salary, as a yearly amount, is at least this much. Hourly pay counts x 2,080, monthly x 12; amounts without a published period are read by size (e.g. 25 as hourly). Only salaries in the currency below are compared (no exchange-rate conversion): jobs paying in another currency are dropped, jobs without a salary are kept unless "Strict filters" is on.

## `minSalaryCurrency` (type: `string`):

Currency of the minimum salary (ISO code).

## `postedWithinDays` (type: `integer`):

Keep only jobs published in the last N days. Leave empty for no limit.

## `strictFilters` (type: `boolean`):

Drop jobs that don't publish the field you filter on (location, employment type, seniority, salary, posted date) instead of keeping them.

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

Maximum number of jobs returned (newest first). 0 = no limit. Closed jobs reported by change tracking don't count against it.

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

Add the full job description converted from HTML to plain text. E-mail addresses and phone numbers in common formats are replaced with \[email removed] / \[phone removed].

## `trackChanges` (type: `boolean`):

Remember the jobs returned so far in a named key-value store in your account (remote-jobs-tracking). Later runs mark each job "new" or "existing" and return jobs that have closed since as "closed" (Jobicy: confirmed by Jobicy's status check; Remote OK: not available, its feed only shows the newest jobs). Schedule the Actor, e.g. daily, for job alerts.

## `onlyNew` (type: `boolean`):

With tracking, skip jobs already returned in an earlier run, so you only pay for what changed. The same job seen earlier on the other board is not new; a new posting on the same board is, even with the same title. The first run returns every job as new. Turns tracking on automatically.

## `reportClosed` (type: `boolean`):

With tracking, also return jobs that closed since the previous run (status "closed"), whatever the current filters: every stored job matched the filters when it was returned.

## `trackingId` (type: `string`):

Name of the tracking state. Runs with the same ID share what has been seen; use different IDs for independent alerts (e.g. one per filter set). Letters, numbers, "-" and "\_".

## `requestTimeoutSecs` (type: `integer`):

Timeout for one HTTP request.

## `maxRetries` (type: `integer`):

Retries for network errors, HTTP 429 and 5xx responses (exponential backoff, honours Retry-After).

## Actor input object example

```json
{
  "sources": [
    "remoteok",
    "jobicy"
  ],
  "keywords": [
    "python",
    "data engineer"
  ],
  "searchDescription": false,
  "excludeKeywords": [
    "intern",
    "sales"
  ],
  "locations": [
    "Germany",
    "Worldwide"
  ],
  "includeWorldwide": true,
  "minSalary": 100000,
  "minSalaryCurrency": "USD",
  "postedWithinDays": 3,
  "strictFilters": false,
  "maxJobs": 10,
  "includeDescription": true,
  "trackChanges": false,
  "onlyNew": false,
  "reportClosed": true,
  "trackingId": "default",
  "requestTimeoutSecs": 60,
  "maxRetries": 4
}
```

# Actor output Schema

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

All job items from this run as JSON (status is open, new, existing or closed).

## `summary` (type: `string`):

Per-board counts (listed, matching, merged, pushed, new, closed) and any errors.

# 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 = {
    "sources": [
        "remoteok",
        "jobicy"
    ],
    "keywords": [
        "engineer"
    ],
    "maxJobs": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("fairfetch/remote-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 = {
    "sources": [
        "remoteok",
        "jobicy",
    ],
    "keywords": ["engineer"],
    "maxJobs": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("fairfetch/remote-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 '{
  "sources": [
    "remoteok",
    "jobicy"
  ],
  "keywords": [
    "engineer"
  ],
  "maxJobs": 10
}' |
apify call fairfetch/remote-jobs-scraper --silent --output-dataset

```

## MCP server setup

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