# Greenhouse, Lever & Ashby Jobs Scraper (`wholesome_roadster/ats-salary-remote-jobs-feed`) Actor

Paste public Greenhouse, Lever or Ashby job-board URLs to get one clean job table with descriptions, apply links and published pay. Free: up to 100 jobs / 3 boards per run. Paid: 10,000 / 100. Export CSV/Excel/JSON. $3/1,000 results; platform usage included.

- **URL**: https://apify.com/wholesome_roadster/ats-salary-remote-jobs-feed.md
- **Developed by:** [Oreidi](https://apify.com/wholesome_roadster) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$3.00 / 1,000 job 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/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

## Greenhouse, Lever & Ashby Jobs Scraper

A public job-board feed for Greenhouse, Lever and Ashby. Give it board URLs or provider:slug identifiers; receive a consistent job schema in an Apify dataset. Useful for job-board ingestion, recruiting workflows and career research.

Collect public job listings from three ATS providers in one consistent feed. Salary and remote status are taken from each source when available; missing values are not invented.

### Who this helps

- **Job-board builders:** ingest Greenhouse, Lever and Ashby public postings using the same output columns instead of maintaining three separate connectors.
- **Recruiting and career research:** filter job titles and locations, then inspect the salary data actually published by the employer.
- **Automation and data teams:** export JSON, CSV or Excel, or connect the Apify dataset API to an existing workflow.

### Free sample and production scale

The free sample is a real dataset, with **all output fields, full descriptions, salary/remote/department/date filters and JSON/CSV/Excel exports**. We limit batch size rather than reducing data quality.

| Capability | Free Apify plan | Paid Apify plan |
| --- | --- | --- |
| Matching jobs per run | Up to 100 | Up to 10,000 |
| Distinct boards per run | First 3 supplied boards | Up to 100 supplied boards |
| Fields, filters, descriptions and exports | All available | All available |
| Published Actor rate | $3 / 1,000 returned jobs | $3 / 1,000 returned jobs |

These are **per-run limits**, not an expiring one-time trial or a promise of unlimited free runs. Available Apify account credit may cover your sample; check your balance and set a charge cap. A paid Apify subscription is separate from this Actor's per-result charges. We do not sell an additional premium subscription here.

Free accounts asking for more receive up to 100 jobs from the first 3 distinct boards in input order, without a policy error. `SUMMARY.planLimits` records the requested/effective limits and skipped boards; `SUMMARY.boardsNotVisited` includes omitted boards. `freePlanLimitReached` and the run status explain the cap. Failed source boards can still cause genuine source errors. An early board can fill the global result cap before later boards are visited; a capped sample is not complete coverage.

**When upgrading helps:** use a paid Apify plan when a useful sample becomes a wider job-board feed, a multi-company hiring review or a recurring dataset refresh. Larger batches let you collect more matching rows and boards in a single run. Missing salary or remote information is never filled in merely because a user paid. Existing runtime, request, budget and source limits still apply; the maximum is not a completeness guarantee.

**Make the first test useful:** run 10–100 jobs from your real target boards, check application links and published pay tiers, inspect SUMMARY, and export JSON/CSV. Repeat with the same settings later to see why fresh data matters. For larger ongoing coverage, use the [recurring-feed checklist](#recurring-feed-checklist) below.

### First run: collect 10 real jobs

1. Click **Try for free**. The form is prefilled with `https://jobs.ashbyhq.com/Ashby` and 10 results. Replace it with your own supported employer board when ready. Public listings can change.
2. Leave optional filter sections closed for the first test. Keep descriptions enabled if you want complete job text.
3. In run options, set a **$0.03 maximum charge** and at least **120 seconds**. Ten returned jobs cost at most $0.03 at the listed rate; available account credit may cover it. Click **Start**.
4. When finished, open **Output → Export** for CSV, Excel or JSON. **Run report — coverage and next steps** links to a readable `RUN_REPORT` record. Find it under Storage if your Console displays output links differently.
5. Read that report before scaling: it names failed or unvisited boards and explains filters, budget and result caps. `SUMMARY` contains the full diagnostics for integrations.

**Zero results?** First turn off optional filters and check the board URL. Remote-only excludes Greenhouse because its remote flag is unknown. Numeric Greenhouse pay requires pay details and the `unknown` salary period. A missing field means it was not available from the supported source structure.

This is an independent tool, with no affiliation with Greenhouse, Lever or Ashby.

### What you receive

- Stable provider/board/job IDs and duplicate suppression within a run, including saved dataset rows after a restart.
- Title, all provided locations, departments, employment type, application links and timestamps when available.
- Provider-asserted remote status: `true`, `false` or `null`. Hybrid is not remote. Greenhouse remote status is unknown; no guesses from descriptions.
- Published salary ranges, currency, interval, geographic tier labels and source field names. No currency conversion or invented pay periods. Multiple ranges stay separate.
- Complete Lever descriptions, including requirements lists and closing sections.
- A readable `RUN_REPORT` with next steps, plus a `SUMMARY` key-value record listing failed boards, salary-detail failures, skipped invalid jobs, limits and boards not visited.

This is not a company search engine: a company name or custom careers domain is not automatically resolved. It fetches jobs only from explicitly supplied public boards. No applicant data, source account login or proxy subscription is needed.

### Quick input

```json
{
  "boards": ["https://jobs.ashbyhq.com/Ashby"],
  "maxItems": 10,
  "titleKeywords": [],
  "locationKeywords": [],
  "remoteOnly": false,
  "requireSalary": false,
  "includeDescription": true,
  "greenhouseSalaryDetails": false
}
```

`leverdemo` is Lever's demonstration board, not an inventory of real vacancies. Replace it with your target organization's board for production use.

Accepted examples: `greenhouse:stripe`, `lever:company`, `lever-eu:company`, `ashby:Ashby`, or public URLs on `boards.greenhouse.io`, `job-boards.greenhouse.io`, `jobs.lever.co`, `jobs.eu.lever.co` and `jobs.ashbyhq.com`. Tokens are case-sensitive where the source requires it.

Title/location lists use case-insensitive substring matching: ANY keyword within one list, AND between different filters. Remote-only filtering excludes unknown remote status, including Greenhouse jobs. `requireSalary` includes source salary text even if numeric bounds cannot be determined.

### Precision filters

Use `departmentKeywords` for source departments and `postedWithinDays` for source publication dates. Zero disables the date filter. Missing, invalid and future dates are excluded when it is active; updated-at is not a fallback. Ashby publication time may describe republication, not original creation. All date comparisons use run start.

Set `minSalary` above zero with `salaryCurrency` (e.g. `USD`) and `salaryInterval` (`year`, `month`, `week`, `day`, `hour`, or `unknown`). At least one source range must have its **lower bound** at or above your threshold in that exact currency and period. Upper-bound-only and text-only salaries are excluded. All geographic tiers stay in output: one matching tier does not guarantee that pay for every location meets the threshold. No conversion, annualization or guessed periods. For Greenhouse, enable pay details and select `unknown` explicitly.

```json
{
  "boards": ["lever:your-board", "ashby:your-board"],
  "maxItems": 100,
  "departmentKeywords": ["engineering"],
  "postedWithinDays": 30,
  "minSalary": 100000,
  "salaryCurrency": "USD",
  "salaryInterval": "year",
  "includeDescription": false
}
```

Replace placeholder board IDs before running. Combine filters with care: stricter filters can legitimately return zero jobs. Filters run before result billing. `SUMMARY.filteredOut` and per-board `filteredOut` report how many valid jobs were rejected by filters.

### Input setup without coding

Use the visual **Input** form in Apify Console. Paste supported employer board URLs, choose the maximum jobs, and leave optional filters closed for your first test. The [Quick input](#quick-input) example is ready to copy into the JSON editor if you prefer. Open **Output** after the run and choose **Export** for CSV, Excel or JSON. See [First run](#first-run-collect-10-real-jobs) for the complete steps.

### Recurring-feed checklist

1. Test 10 jobs from the real boards you need. Check application links, descriptions and published pay tiers.
2. Read `RUN_REPORT` and `SUMMARY`: result limits, budgets or failed boards can leave incomplete coverage.
3. Save the working input as an Apify task. Add a schedule only when you want repeated runs; each run can incur charges.
4. Estimate Actor charges as **returned jobs per run × runs per month × $0.003**. Apify subscriptions are separate; do not assume account credit is unlimited.
5. Export complete JSON snapshots with the same boards and filters. Match rows by `id` to inspect additions and changes. A missing row can mean filtered, capped or failed coverage; it does not prove a vacancy closed.

### Worked examples

- [Collect 10 jobs and export one CSV](https://rentry.co/oreidi-ats-jobs-csv-guide): a step-by-step first run, exports and coverage checks.
- [Filter published pay without mixing currencies or periods](https://rentry.co/oreidi-published-salary-filter-guide): lower bounds, geographic tiers and missing source data.

These guides are written by this Actor's maintainer. They are not independent reviews. The hosted Actor keeps the pricing and coverage limits below.

### Salary accuracy

Lever: reads `salaryRange` and the optional salary explanation.

Ashby: requests `includeCompensation=true`, takes Salary components only, preserves each pay tier, and excludes equity/bonus components from salary bounds. Unlisted jobs are excluded.

Greenhouse: the default list feed does not promise numeric salary data. Enable `greenhouseSalaryDetails` to request `pay_transparency=true` once for each job passing title/location/remote filters. Cents are converted into currency units. The API does not specify a period in this structure, so `salaryInterval` stays null. This option can substantially increase request counts; it is off by default. Failed enrichment is visible in SUMMARY.

The convenient flat salary columns are populated only when there is exactly one numeric range. Use `salaryRanges` for multiple geographic tiers, currencies or periods. Missing data remains null. Salary hidden inside free-text descriptions is not parsed or estimated.

### Results and exports

Every dataset item represents one job. Main columns:

`id`, `provider`, `board`, `company`, `title`, `location`, `locations`, `department`, `team`, `employmentType`, `workplaceType`, `isRemote`, `remoteSource`, `jobUrl`, `applyUrl`, `postedAt`, `updatedAt`, `salaryMin`, `salaryMax`, `salaryCurrency`, `salaryInterval`, `salaryRanges`, `salaryText`, `hasSalary`, `description`, `scrapedAt`.

Company is the board token unless the source supplies a company name. Ashby's publishedAt is last publication time; timestamps have provider-specific semantics. `description` is omitted when disabled. Do not treat an empty dataset as proof that a board has no jobs without reading SUMMARY.

Apify provides JSON/CSV/Excel dataset downloads and API access. The default table view shows common flat fields; full JSON exports include nested salary tiers.

### Bounds and behavior

- Free Apify plan: up to 3 distinct boards and 100 matching jobs per run. Paid Apify plans: up to 100 boards and 10,000 matching jobs per run. Both use the same fields and filters.
- Boards processed in input order. A large early board can fill the global limit before later boards are visited.
- Lever pagination is explicit, up to 100 pages of 100 jobs per board, and stops fetching new pages once the result or charge limit is reached. A capped feed is flagged in SUMMARY.
- Up to 500 HTTP attempts per run, including retries and salary enrichment. Processing stops before a new request when fewer than 35 seconds remain before the platform timeout. SUMMARY identifies partial output with `stoppedEarly`; split large inputs into smaller runs.
- SUMMARY is checkpointed at board/page boundaries and on platform persistence, abort and migration events. An abrupt process termination can leave the latest checkpoint behind dataset rows.
- Responses are streamed with a 30 MB byte limit to bound response buffering.
- Sequential requests; request timeouts and bounded retries for network errors, HTTP 429 and 5xx. HTTP 403 is reported without retries or access-control evasion.
- Partial board failures allow successful boards to finish; if every requested board fails, the run fails with diagnostics.
- No automatic cross-run change detection, notifications, company discovery or persistent historical database inside the Actor. Compare saved exports by stable job IDs when needed.
- Read source usage terms and use boards whose access, processing and intended reuse you are permitted to perform. Availability and schemas may change.

### Source rights and privacy

Use your own organization's boards or boards for which your intended use is authorized under the applicable employer and provider terms. A public API or publicly visible job is not, by itself, a blanket license to redistribute descriptions, sell job content, or ignore privacy obligations. This Actor sells a retrieval and normalization service; it does not grant rights in employer content. Preserve source links and verify permissions before republication or resale.

The Actor makes HTTPS GET requests only to documented public job-posting endpoints. It does not submit applications, request applicant records, use account credentials, or bypass access controls. Ashby postings marked `isListed=false` are excluded. Source descriptions and salary text can nevertheless contain names, emails, phone numbers or other personal information. `includeDescription=false` omits full descriptions from output, but is not a guarantee that other fields contain no personal data. Collect only necessary data and establish any lawful basis required for your use.

Results and SUMMARY are stored in the run's Apify dataset and key-value store. The Actor has no additional external storage or telemetry endpoint. Manage output retention, access and deletion in your Apify account; do not put credentials or private candidate data in input or public Issues. The applicable Apify Standard Actor Contract remains in effect; this guidance does not replace it or reduce Creator obligations. Report functionality or rights concerns through this Actor's Issues tab without including sensitive details.

### Pricing

**$0.003 per returned job ($3 / 1,000)**, with platform usage included and no startup charge. One `job-result` event is charged for each dataset row after filters and deduplication. Failed boards do not produce billable result events. Repeated runs that return the same jobs are billed again.

Set a maximum charge for each run in Apify Console. The Actor stops adding results when that budget is reached. The run summary reports the boards processed, result count and any partial output.

#### Actor price versus Apify subscription

The published Actor rate is **$3 per 1,000 returned job results** on the Free, Starter, Scale and Business plans. Any monthly amounts displayed on the Pricing tab are Apify subscription prices, not an additional monthly subscription to this Actor. Apify plan credit can be used toward Actor charges; availability and remaining credit depend on your account and other usage. At this rate, 3 results cost $0.009, 10 cost $0.03 and 1,000 cost $3. You are charged for the results returned, not for buying a block of 1,000. Optional Greenhouse salary-detail requests do not add a separate event fee or change this per-result rate.

For developers deploying a copy: configure the `job-result` event and its actual price in Apify Console. Source code does not set marketplace prices. Do not also charge the automatic default-dataset-item event. This hosted Actor uses limited permissions.

### Common questions

**Can I use this without writing code?** Yes. Click Try for free, fill the input form with a supported board URL, start a run, then export the dataset as CSV or Excel. Charges apply at the price above; trial availability depends on your Apify account.

**Does it search every company automatically?** No. Supply the public boards you want. Custom careers domains and company names are not automatically resolved.

**Why are some salary or remote fields empty?** They were not published as supported structured fields. The Actor does not invent salaries or infer remote status from a description.

**Can I run it on a schedule?** Apify supports schedules and API integrations. Every new run is charged for its returned results; this Actor does not track changes between runs.

**Why did I receive fewer jobs?** Free Apify plans are capped at 100 results / 3 boards per run; SUMMARY explains these plan limits. Check SUMMARY for failed boards, filters, pagination caps and the maximum results or charge limit. Remote-only results exclude jobs with unknown remote status.

**How do I report a problem?** Use this Actor's Issues tab. Include the public board identifier and relevant input options. Do not post API tokens, credentials or private applicant data.

### References

- https://docs.apify.com/sdk/js/docs/concepts/pay-per-event
- https://docs.apify.com/actors/publishing/monetize/pricing-and-costs
- https://docs.greenhouse.io/job-board.html
- https://github.com/lever/postings-api
- https://developers.ashbyhq.com/docs/public-job-posting-api

# Actor input Schema

## `boards` (type: `array`):

Paste a supported public employer board URL, one per entry. Example: https://jobs.ashbyhq.com/Ashby. Greenhouse, Lever/EU and Ashby are supported. Company names and custom career domains are not resolved. Free accounts use the first 3 distinct boards; paid accounts up to 100.

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

Start with 10 jobs: at most $0.03 at the listed rate. This is a total limit across all boards, processed in order. Free accounts: up to 100; paid accounts: up to 10,000. Set a charge cap in run options too. API default remains 100.

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

Includes requirements, lists and closing sections from Lever.

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

Case-insensitive substring matching. Empty means all. Conditions across different filters are ANDed.

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

Case-insensitive substring matching. Empty means all. Conditions across different filters are ANDed.

## `remoteOnly` (type: `boolean`):

Requires remote=true from the provider. Unknown jobs, including Greenhouse jobs without a native remote flag, are excluded.

## `requireSalary` (type: `boolean`):

Keep records with published numeric salary ranges or source salary text. No salary estimation.

## `greenhouseSalaryDetails` (type: `boolean`):

Adds one API request per job matching title/location/department/date/remote filters. Increases runtime and request usage, not the published per-result price. Greenhouse salary period is unknown.

## `minSalary` (type: `number`):

At least one published numeric salary range must have its LOWER bound at or above this amount, in the selected currency and interval. Upper-bound-only and salary-text-only jobs are excluded. All source ranges remain in output. No currency conversion or annualization.

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

Three-letter currency code, e.g. USD, EUR, GBP or ILS. Used only when minSalary > 0.

## `salaryInterval` (type: `string`):

Compare only this published period. Use unknown explicitly for Greenhouse pay ranges, whose period is not specified. Missing periods are never assumed to be yearly.

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

Case-insensitive substring matching on the source department. Unknown departments are excluded when this filter is active.

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

Keep source publication timestamps from the last N days, relative to run start. Missing, invalid and future dates are excluded. Updated-at is not used as a fallback; Ashby publication dates may represent republication.

## Actor input object example

```json
{
  "boards": [
    "https://jobs.ashbyhq.com/Ashby"
  ],
  "maxItems": 10,
  "includeDescription": true,
  "titleKeywords": [],
  "locationKeywords": [],
  "remoteOnly": false,
  "requireSalary": false,
  "greenhouseSalaryDetails": false,
  "minSalary": 0,
  "salaryCurrency": "USD",
  "salaryInterval": "year",
  "departmentKeywords": [],
  "postedWithinDays": 0
}
```

# Actor output Schema

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

Normalized public job listings with salary provenance and remote status.

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

Per-board fetch status, counts, filters and run limits.

## `report` (type: `string`):

Readable summary of returned jobs, source failures, limits and what to do next.

# 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 = {
    "boards": [
        "https://jobs.ashbyhq.com/Ashby"
    ],
    "maxItems": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("wholesome_roadster/ats-salary-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 = {
    "boards": ["https://jobs.ashbyhq.com/Ashby"],
    "maxItems": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("wholesome_roadster/ats-salary-remote-jobs-feed").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 '{
  "boards": [
    "https://jobs.ashbyhq.com/Ashby"
  ],
  "maxItems": 10
}' |
apify call wholesome_roadster/ats-salary-remote-jobs-feed --silent --output-dataset

```

## MCP server setup

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

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/7Qa4Teh6RP5nmHUQR/builds/8NdE97EKlT9NXH1Sh/openapi.json
