# Remote Jobs Search API: 140k+ Jobs from Career Sites (`jamie_developer/remote-jobs-search`) Actor

Search 140,000+ remote jobs that employers post on their own career sites (Greenhouse, Workday, Lever, Ashby, SmartRecruiters and 14 more ATSs), updated daily. Filter by title, the country a remote job is open to, salary and date. Only-new mode for remote job alerts.

- **URL**: https://apify.com/jamie_developer/remote-jobs-search.md
- **Developed by:** [JamieDoe](https://apify.com/jamie_developer) (community)
- **Categories:** Jobs, Automation, Developer tools
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.40 / 1,000 remote job 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?

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 Search: 140k+ Remote Jobs from Company Career Sites

**Find remote jobs where employers post them first: on their own career sites. 140,000+ remote openings from about 49,000 companies on 19 applicant tracking systems (Greenhouse, Workday, Lever, Ashby, SmartRecruiters, BambooHR, Rippling and more), refreshed every day and searchable in seconds.**

> **Why this one**
>
> - **Straight from the employer.** Every job comes from the company's own applicant tracking system (ATS), so there are no reposts or copies from other job boards, and every link goes to the real application form.
> - **"Remote, but where?" answered.** Filter by the country a remote job is tied to ("Remote - US", "Remote, Germany"), and decide whether to keep jobs that only say "Remote" or "Worldwide".
> - **Fast.** Your run searches a ready-made daily index instead of crawling sites, so a search over every remote job finishes in seconds.
> - **Alerts built in.** *Only jobs not returned before* returns just the new remote jobs on each scheduled run, and the skipped ones are free.

### What it does

Companies publish their openings through their ATS, which serves a public job-board feed for the company's careers page. A daily process reads those feeds for about 49,000 companies on 19 ATSs into one index (2,016,483 jobs on 31 October 2026). This Actor searches the **remote** jobs in that index (147,550 that day; 67,207 of them tied to the United States, and 20,766 more that name no country) with your filters and saves them as a dataset you can download as JSON, CSV or Excel, or read through the API.

A job counts as remote when the ATS marks it remote, or when its location or title says "remote" (for example "Remote - US", "US Based - Remote", "Anywhere (Remote)"). Hybrid jobs are not included.

Want the whole ad? Turn on **Include job description** and the text is fetched live from each company's job board during your run.

### Filters

| Filter | What it does |
|---|---|
| **Job title contains any of** / **none of** | e.g. `engineer`, `product designer`, `customer success`; exclude `senior`, `intern`. Matches the start of words: `engineer` also finds *Engineering*. |
| **Department or team**, **Employment type** | e.g. `engineering`, `support`; full-time, part-time, contract, internship, temporary |
| **Countries the job is in or open to** | `United States`, `Canada`, `DE`, `GB`... from the ATS's country field or the location text |
| **Also keep remote jobs that name no country** | adds jobs listed only as "Remote", "Worldwide" or "Anywhere" to a country search |
| **Location contains any of** | free text, e.g. `Europe`, `EMEA`, `Americas`, `Worldwide`, `CET` |
| **Posted within the last N days** | ATS posting date, or the day the job first appeared in the index |
| **Jobs with salary only**, **Minimum salary**, **Salary currency** | jobs that publish pay; the minimum is per year, with monthly and hourly pay converted |
| **Companies**, **Applicant tracking systems** | company names or job board URLs; platforms such as `greenhouse` or `ashby` |
| **Sort by**, **Max jobs** | newest first or company A-Z; cap the number of jobs, and so the cost |

#### Example input

Remote engineering jobs open to US residents, with a published salary of at least $150,000, posted in the last week:

```json
{
  "titleKeywords": ["engineer", "developer"],
  "excludeTitleKeywords": ["intern"],
  "countries": ["United States"],
  "salaryMinAtLeast": 150000,
  "salaryCurrency": "USD",
  "postedWithinDays": 7,
  "maxJobs": 200
}
```

### Output

One row per job, the same columns in every row (empty values are `null`):

| Field | Example | Notes |
|---|---|---|
| `title`, `company` | Full Stack Engineer, Expensify | |
| `location`, `locations` | US Based - Remote | all locations of the job |
| `country`, `countryCode` | United States, US | `countryCode` is ISO 3166, empty when the job names no country |
| `workplaceType` | remote | always `remote` here |
| `department`, `team`, `employmentType` | Engineering, Engineering, Full-time | as published |
| `postedAt`, `updatedAt`, `firstSeenAt`, `lastSeenAt` | 2026-10-31T00:53:16Z | ISO 8601, UTC |
| `salaryMin`, `salaryMax`, `salaryCurrency`, `salaryPeriod`, `salaryText` | 150000, 250000, USD, year | from the ATS or the pay range in the ad |
| `url`, `applyUrl`, `sourceUrl` | job ad, application form, company job board | |
| `ats`, `companySlug`, `jobId` | ashby, expensify, 096ff7eb-… | together they identify a job |
| `description`, `descriptionAvailable` | full ad as plain text | only with *Include job description* |
| `scrapedAt` | 2026-10-31T06:15:47Z | when your run returned the job |

```json
{
  "title": "Full Stack Engineer",
  "company": "Expensify",
  "ats": "ashby",
  "companySlug": "expensify",
  "jobId": "096ff7eb-2a55-4867-b0d1-b9c4743dc576",
  "location": "US Based - Remote",
  "locations": ["US Based - Remote"],
  "country": "United States",
  "countryCode": "US",
  "workplaceType": "remote",
  "department": "Engineering",
  "team": "Engineering",
  "employmentType": "Full-time",
  "postedAt": "2026-10-31T00:53:16Z",
  "updatedAt": null,
  "firstSeenAt": "2026-10-31T01:53:41Z",
  "lastSeenAt": "2026-10-31T01:53:41Z",
  "salaryMin": 150000,
  "salaryMax": 250000,
  "salaryCurrency": "USD",
  "salaryPeriod": "year",
  "salaryText": "$150K – $250K • Offers Equity",
  "url": "https://jobs.ashbyhq.com/expensify/096ff7eb-2a55-4867-b0d1-b9c4743dc576",
  "applyUrl": "https://jobs.ashbyhq.com/expensify/096ff7eb-2a55-4867-b0d1-b9c4743dc576/application",
  "sourceUrl": "https://jobs.ashbyhq.com/expensify",
  "scrapedAt": "2026-10-31T06:15:47Z"
}
```

### Remote job alerts

1. Set your filters and turn on **Only jobs not returned before**.
2. Schedule the Actor in Apify, for example every morning.
3. The first run returns everything that matches now; each later run returns only remote jobs that earlier runs of the same search haven't returned, and you aren't charged for the skipped ones.
4. Add an integration to post each run's jobs to Slack, email, Google Sheets, Make, Zapier, n8n or a webhook.

The memory of returned jobs is kept per set of filters in a key-value store named `remote-jobs-search-state` in your own account. Give the search a **Search name** to keep it while you edit the filters.

### Use cases

- **Remote job boards and newsletters:** fill a board or a weekly email with fresh remote roles in your niche (developers, designers, support, sales) with working apply links.
- **Job seekers and career coaches:** a daily list of new remote roles with published pay, limited to the countries you can work from.
- **Remote-work research:** which companies hire remotely, for which roles, in which countries, and at what pay.
- **Recruiting and sales intelligence:** companies hiring remote engineers or support staff are growing distributed teams; feed them into your CRM.

### Pricing

You pay per job saved to the dataset; the exact price is on the Pricing tab. Jobs removed by your filters, jobs skipped as already returned, and jobs dropped because they closed cost nothing. **Max jobs** and the run's maximum cost both cap a run, and the Actor stops cleanly at either.

### FAQ

**What exactly is "remote"?** Jobs the ATS flags as remote, plus jobs whose location or title says remote. A remote job tied to one country ("Remote - US") gets that country; one that says only "Remote" or "Anywhere" has no country. Use **Also keep remote jobs that name no country** or **Location contains** `worldwide` to include those.

**How fresh is it?** The index is rebuilt every day; the status message and the run's SUMMARY record show when. Most job boards are read on every rebuild; Workday's large employer sites are read in rotation, each at least every 2–3 days, and `lastSeenAt` on each job says when it was last seen open. With **Include job description** on, each job is checked against the live job board and jobs that closed since the last update are dropped.

**Is this legal?** The index is built only from the public job-board feeds that ATS vendors provide for companies to publish openings. No logins, nothing bypassed, no candidate or personal data. The ads belong to the employers, so check your use (for example republishing full descriptions) against their terms. This Actor is not affiliated with any ATS vendor or with any employer in the results.

**Do you include LinkedIn or Indeed jobs?** No, only company career sites. That is why the links go straight to the employer's application form.

**I need hybrid and on-site jobs too.** Use our **Job Search API** Actor, which searches the whole index with a workplace-type filter.

# Actor input Schema

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

Keep remote jobs whose title contains at least one of these keywords, e.g. engineer, product designer, customer success. Case-insensitive. A keyword matches at the start of a word, so engineer also finds Engineering; keywords of 1-2 letters (AI, QA, Go) must match a whole word. Leave empty for all titles.

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

Drop jobs whose title contains any of these keywords, e.g. senior, intern, manager. Same matching as above.

## `departmentKeywords` (type: `array`):

Keep jobs whose department or team contains one of these keywords, e.g. engineering, support, marketing. Jobs without a department are dropped when this is set.

## `employmentType` (type: `array`):

Keep jobs of these types. Each ATS words it differently (Full-time, Vollzeit, FULL_TIME...), so they are grouped. Jobs without a stated type are dropped when this is set. Leave empty for all.

## `countries` (type: `array`):

Keep remote jobs tied to these countries, e.g. 'Remote - US' or 'Remote, Germany'. Use English names (United States, Germany, United Kingdom) or ISO codes (US, DE, GB). The country comes from the ATS when it gives one, otherwise from the location text. Remote jobs that name no country at all ('Remote', 'Anywhere', 'EMEA') are dropped unless you tick the box below.

## `includeUnknownCountry` (type: `boolean`):

With a country filter set, also keep remote jobs whose location names no country, such as 'Remote', 'Worldwide' or 'Anywhere'. Many of them hire in several countries; check the ad.

## `locationKeywords` (type: `array`):

Keep jobs whose location text contains one of these words, e.g. Europe, EMEA, Americas, Worldwide, Anywhere, CET, or a city for remote jobs attached to an office. Matched against all locations and the country field.

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

Keep jobs posted in the last N days, using the ATS's posting date or, when the ATS gives none, the day the job first appeared in the index. 0 = any date.

## `hasSalary` (type: `boolean`):

Keep only jobs that state a pay range, either in the ATS's salary fields or written in the job ad.

## `salaryMinAtLeast` (type: `integer`):

Keep jobs whose pay range starts at or above this yearly amount. Monthly, weekly, daily and hourly pay is converted to a year (x12, x52, x260, x2080). Use it with Salary currency, otherwise amounts in different currencies are compared as-is. 0 = no minimum.

## `salaryCurrency` (type: `string`):

Keep only jobs that pay in this currency (3-letter code). Implies 'Jobs with salary only'.

## `companies` (type: `array`):

Keep remote jobs from these companies only. Use the company name (GitLab, Automattic), its job board name or its job board URL (https://boards.greenhouse.io/gitlab). Names are matched exactly, ignoring case, spaces and suffixes like Inc or GmbH.

## `ats` (type: `array`):

Keep jobs from these job board platforms only, one per line. The index covers greenhouse, smartrecruiters, workday, lever, ashby, workable, teamtailor, paylocity, bamboohr, recruitee, softgarden, breezy, personio, rippling, jobvite, join, comeet, pinpoint and dvinci. Every run's SUMMARY record lists the platforms and job counts in the current index. Leave empty for all.

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

For scheduled runs and remote job alerts: return only jobs that earlier runs of the same search haven't returned. The first run returns the current matches; later runs return only new ones. Skipped jobs are not charged. The memory is kept per set of filters in a key-value store named remote-jobs-search-state in your account.

## `stateName` (type: `string`):

Name for the 'already returned' memory. Leave empty to derive it from the filters; set it to keep the memory when you edit the filters.

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

Add the full job description as plain text, fetched live from each job's own board at run time: one request per company for most platforms, one per job for platforms whose feeds carry no descriptions (such as Workday or SmartRecruiters), up to 500 jobs per company and 5,000 per run; jobs over that come back without a description. Makes runs slower. Jobs that closed since the last index update are dropped and not charged.

## `sortBy` (type: `string`):

Newest first (by posting date, else first seen), or company name A-Z (newest first within a company).

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

Stop after this many jobs (up to 200,000). You pay per job saved, so this caps the cost of a run.

## Actor input object example

```json
{
  "titleKeywords": [
    "developer",
    "engineer"
  ],
  "excludeTitleKeywords": [
    "senior",
    "staff",
    "principal"
  ],
  "departmentKeywords": [
    "engineering",
    "data"
  ],
  "employmentType": [
    "full-time",
    "contract"
  ],
  "countries": [
    "United States",
    "CA"
  ],
  "includeUnknownCountry": false,
  "locationKeywords": [
    "Europe",
    "EMEA",
    "Worldwide"
  ],
  "postedWithinDays": 7,
  "hasSalary": false,
  "salaryMinAtLeast": 100000,
  "salaryCurrency": "USD",
  "companies": [
    "GitLab",
    "https://jobs.ashbyhq.com/posthog"
  ],
  "ats": [
    "greenhouse",
    "ashby"
  ],
  "onlyNewSinceLastRun": false,
  "includeDescription": false,
  "sortBy": "newest",
  "maxJobs": 50
}
```

# Actor output Schema

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

The remote jobs that matched your filters (dataset items).

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

Index date and size, jobs per platform, jobs matched and saved, warnings.

# 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 = {
    "titleKeywords": [
        "engineer"
    ],
    "postedWithinDays": 7,
    "maxJobs": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("jamie_developer/remote-jobs-search").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 = {
    "titleKeywords": ["engineer"],
    "postedWithinDays": 7,
    "maxJobs": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("jamie_developer/remote-jobs-search").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 '{
  "titleKeywords": [
    "engineer"
  ],
  "postedWithinDays": 7,
  "maxJobs": 50
}' |
apify call jamie_developer/remote-jobs-search --silent --output-dataset

```

## MCP server setup

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

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/AcvLSAhhrLEz81j5f/builds/Ld6Tbr4Ix8TFipmIq/openapi.json
