# Remote Jobs — 7 boards in one feed, with expiry history (`hyperbach/remote-jobs-feed`) Actor

Every remote job from seven boards and ATSs — Himalayas, RemoteOK, WeWorkRemotely, Ashby, Greenhouse, Jobicy, Remotive — in one deduplicated feed of 100,000+ live postings, kept after they close. The only remote-jobs aggregator with expiry history. Database speed, incremental.

- **URL**: https://apify.com/hyperbach/remote-jobs-feed.md
- **Developed by:** [Hyperbach](https://apify.com/hyperbach) (community)
- **Categories:** Jobs
- **Stats:** 6 total users, 3 monthly users, 98.8% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$3.00 / 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.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#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 Jobs — 7 boards in one feed, with expiry history

**Every remote job from seven boards and ATSs — Himalayas, RemoteOK, WeWorkRemotely, Ashby, Greenhouse, Jobicy, Remotive — in one deduplicated feed, kept after they close.** 100,000+ live postings, with the one thing no aggregator gives you: when and why each job ended. Served at database speed.

### Why this feed, not seven scrapers

- **Seven sources, one feed.** 100,000+ live remote postings from Himalayas, RemoteOK, WeWorkRemotely, Ashby, Greenhouse, Jobicy and Remotive — deduplicated by URL into a single stream. Run one Actor instead of seven, with nothing to maintain when a board changes.
- **Kept after they close.** `status=gone` returns removed postings with `gone_reason` — *filled* vs *expired* where the board publishes a deadline (Himalayas), the close date everywhere else. Measured 72% filled / 28% unknown in a live sample. No other remote-jobs aggregator on the store has this; a live scrape structurally can't.
- **Enriched where the source allows.** The employer's own web domain (about half the feed), a salary band (about a third), employment type (~90%), location, category and full description — all as typed fields. Coverage varies by source, and it's null, never guessed, where a source doesn't provide it.
- **A real feed, not a re-scrape.** Leave `postedSince` empty and each run returns only the postings that appeared since the last one — the Actor tracks its own cursor. Incremental by design, so you're billed for new jobs, not the whole archive every run.
- **Database speed, and unblockable.** You read one maintained archive, not seven live boards, so a run answers as a query and can never be rate-limited or blocked.

### Who it's for

- **Job-board & SaaS builders** — power a remote-jobs site or app from one clean, deduplicated, incremental feed spanning seven sources — with an archive envelope to build history features on.
- **Recruiters & talent researchers** — one feed of all remote hiring across the major boards and top-company ATSs, with employer domains where available and a record of what closed.
- **Labor-market & trend researchers** — the whole remote market with open-and-close dates — measure hiring velocity, role churn and which postings fill fast, not just a snapshot of what's live.
- **Sales & lead-gen teams** — hiring signals across every board at once, with the employer's real domain on the roughly half of postings that resolve it — a company you can act on, not just a job row.

### Quick start

**Newest across all sources**

```json
{
  "maxItems": 20
}
```

**Filtered**

```json
{
  "keyword": "engineer",
  "salaryMin": 120000,
  "remoteOnly": true,
  "maxItems": 50
}
```

**Notification mode — only what's new**

```json
{
  "postedSince": ""
}
```

### Output

One record per posting:

| field | meaning |
|---|---|
| `posted_at` | When the posting first appeared. |
| `title` | Job title. |
| `company` | Employer name as the board lists it. |
| `company_domain` | The employer's own web domain, resolved from the posting. |
| `company_logo_url` | Employer logo, when the board provides one. |
| `salary_min` | Lower bound of the advertised band. |
| `salary_max` | Upper bound of the advertised band. |
| `salary_currency` | ISO currency of the advertised band. |
| `salary_unit` | Period the band refers to (e.g. `YEAR`, `HOUR`). |
| `employment_type` | Full time, contract, and so on, as declared by the employer. |
| `location_restriction` | Where the posting says the role may be based. |
| `categories` | Category tags the board assigned to the posting. |
| `description` | Full posting description. |
| `apply_url` | Where to apply for the role. |
| `url` | Canonical posting URL. |
| `status` | `live` while listed, `gone` once removed. |
| `gone_reason` | Why a posting left the board: `filled` if removed before its published expiry, `expired` if it ran to term (sources that date postings, e.g. Himalayas), or `unknown` where the board publishes no deadline (the ATS boards). `null` while the posting is live. |
| `enriched` | Whether salary, employment type and employer domain have been resolved yet — a posting is returned as soon as it is found, enriched or not. |

Example record:

```json
{
  "posted_at": "2026-07-30 11:50:24+00",
  "title": "Legal Conflicts Account Specialist",
  "company": "Latitude",
  "company_domain": "latitudeinc.net",
  "company_logo_url": "https://cdn-images.himalayas.app/6zhuor171tcjn6dnjv0ibkoy28cn",
  "salary_min": 60000.0,
  "salary_max": 75000.0,
  "salary_currency": "USD",
  "salary_unit": "YEAR",
  "employment_type": "Full Time",
  "location_restriction": "United States",
  "categories": [
    "Collections-Specialist",
    "Legal-Account-Specialist",
    "Accounts-Receivable-Specialist",
    "Account-Analyst",
    "Billing-Specialist",
    "Legal-Conflicts-Specialist",
    "Conflicts-Analyst",
    "Conflicts-Coordinator"
  ],
  "description": "<div>We are seeking a talented member to join its Collections department.The Client Accounts Analyst will coordinate and work with a professional and cohesive team, handling all co …(truncated for display)",
  "apply_url": "https://himalayas.app/companies/latitude/jobs/legal-conflicts-account-specialist-4386432817",
  "url": "https://himalayas.app/companies/latitude/jobs/legal-conflicts-account-specialist-4386432817",
  "status": "live",
  "gone_reason": null,
  "enriched": true
}
```

### Pricing

**$0.005 per result** — pay only for the postings a run returns, **no start fee**, and in notification mode you pay only for what's *new* since your last run. The flagship of the archive: seven sources, 100k+ live postings, employer enrichment, and expiry history none of the other aggregators carry. Competing remote-jobs aggregators run $0.003–0.02 with no history at all.

### Usage patterns

- **The whole live feed, incrementally** — Leave `postedSince` empty; each run continues from the previous one's cursor and returns only new postings across all seven sources — no duplicates, nothing skipped.
- **What closed, and why** — Set `status=gone` to study removals; `gone_reason` splits filled-early from ran-to-expiry where the board dates it — a signal you can't get from any live feed.
- **Filter across every source at once** — `keyword`, `salaryMin`, `remoteOnly` and the rest apply across all seven sources in one query.

### Input configuration

| field | type | default | what it does |
|---|---|---|---|
| `keyword` | `string` |  | Matches job title or category. |
| `company` | `string` |  | Employer name as the board lists it. Partial matches work. |
| `companyDomain` | `string` |  | Exact employer domain, e.g. stripe.com. The reliable way to track one employer. |
| `employmentType` | `FULL_TIME` / `PART_TIME` / `CONTRACTOR` / `TEMPORARY` / `INTERN` |  | Restrict to one contract type, as declared by the employer. |
| `salaryMin` | `integer` |  | Matches any posting whose advertised band reaches this figure. A $10-35/hr band matches a request for 35. |
| `postedSince` | `string` |  | ISO timestamp. Leave empty and the Actor remembers where it stopped, returning only new postings each run. |
| `status` | `live` / `gone` / `any` | `"live"` | 'Gone' returns postings that have been removed, with why — the part no live scraper can give you. |
| `enrichedOnly` | `boolean` | `false` | Off by default: a posting is returned as soon as it is discovered, and salary fills in shortly after. |
| `maxItems` | `integer` | `500` | Upper bound on results returned in a single run. |
| `fullRefresh` | `boolean` | `false` | By default each run returns only what appeared since your last one, so a schedule never re-delivers the same page. Tick this to ignore that bookmark and re-pull the full result set for your filters — and it clears the stored cursor, so your next ordinary run starts clean. |

### FAQ

**How is this different from a single-board scraper or another aggregator?**

It's the whole archive in one feed — seven sources deduplicated — and it's the only remote-jobs aggregator with an archive: postings are kept after they close (`status=gone` + `gone_reason`), so you get hiring *history*, not just today's snapshot. It's also incremental (a real feed with a cursor), so you don't re-pull and re-pay for the whole corpus each run.

**How complete is the salary and employer-domain data?**

It varies by source and is stated honestly: about half of postings resolve the employer's real domain (Himalayas, Jobicy, Remotive expose it; RemoteOK/WeWorkRemotely/Greenhouse don't), and about a third carry a salary band (Himalayas and Ashby post one; others don't). Fields are null, never fabricated, where a source doesn't provide them.

**Is it live?**

It's a continuously maintained archive: new postings appear within hours (RSS-tailed sources) to a day (ATS boards), and closed ones stay with their disappearance recorded.

**Will I see the same job twice?**

No — postings are deduplicated by URL across sources, and in notification mode (`postedSince` empty) each run continues where the last stopped.

### Integration

#### JavaScript

```javascript
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('hyperbach/remote-jobs-feed').call({"maxItems": 20});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

#### Python

```python
from apify_client import ApifyClient
client = ApifyClient('YOUR_TOKEN')
run = client.actor('hyperbach/remote-jobs-feed').call(run_input={'maxItems': 20})
items = client.dataset(run['defaultDatasetId']).list_items().items
```

#### CLI

```bash
apify call hyperbach/remote-jobs-feed --input '{"maxItems": 20}'
```

#### REST

```bash
curl -X POST "https://api.apify.com/v2/acts/hyperbach~remote-jobs-feed/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H 'Content-Type: application/json' -d '{"maxItems": 20}'
```

### Support

support@hyperbach.com

*This page is generated from the Actor's schemas and a live sample — it cannot describe a field the Actor does not have.*

# Actor input Schema

## `keyword` (type: `string`):

Matches job title or category.

## `company` (type: `string`):

Employer name as the board lists it. Partial matches work.

## `companyDomain` (type: `string`):

Exact employer domain, e.g. stripe.com. The reliable way to track one employer.

## `employmentType` (type: `string`):

Restrict to one contract type, as declared by the employer.

## `salaryMin` (type: `integer`):

Matches any posting whose advertised band reaches this figure. A $10-35/hr band matches a request for 35.

## `postedSince` (type: `string`):

ISO timestamp. Leave empty and the Actor remembers where it stopped, returning only new postings each run.

## `status` (type: `string`):

'Gone' returns postings that have been removed, with why — the part no live scraper can give you.

## `enrichedOnly` (type: `boolean`):

Off by default: a posting is returned as soon as it is discovered, and salary fills in shortly after.

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

Upper bound on results returned in a single run.

## `fullRefresh` (type: `boolean`):

By default each run returns only what appeared since your last one, so a schedule never re-delivers the same page. Tick this to ignore that bookmark and re-pull the full result set for your filters — and it clears the stored cursor, so your next ordinary run starts clean.

## Actor input object example

```json
{
  "keyword": "backend engineer",
  "companyDomain": "stripe.com",
  "postedSince": "2026-07-26T00:00:00Z",
  "status": "live",
  "enrichedOnly": false,
  "maxItems": 500,
  "fullRefresh": false
}
```

# Actor output Schema

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

All scraped records in the default dataset. One record per posting:

# 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("hyperbach/remote-jobs-feed").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("hyperbach/remote-jobs-feed").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).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 hyperbach/remote-jobs-feed --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=hyperbach/remote-jobs-feed",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/a9ZvFBsIINtDrBWCQ/builds/lZqMTPnH5JXXfez3D/openapi.json
