# Company Jobs Scraper — Greenhouse, Lever, Ashby + Salaries (`tidyfeed/company-jobs-scraper`) Actor

Open jobs from any company's career board on Greenhouse, Lever, Ashby, Workable, SmartRecruiters or Recruitee in one clean table, with the salary ranges companies publish. Or one hiring-signal row per company: open roles, new in 7/30/90 days, remote share, top departments. No login, no proxies.

- **URL**: https://apify.com/tidyfeed/company-jobs-scraper.md
- **Developed by:** [tidyfeed](https://apify.com/tidyfeed) (community)
- **Categories:** Jobs, Lead generation, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.40 / 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.
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

## Company Jobs Scraper — Greenhouse, Lever, Ashby + Salaries

**Who is hiring, for what, where — and what do they pay?**

Paste a list of companies. Get every open job on their career boards in one clean table: title, department, location, remote flag, publish date, link — and **the salary range, as numbers, wherever the company publishes one**. Works across the six applicant tracking systems most tech companies use: **Greenhouse, Lever, Ashby, Workable, SmartRecruiters and Recruitee**, in one run, one schema.

Or flip the switch and get **one hiring-signal row per company**: how many roles are open, how many opened in the last 7, 30 and 90 days, what share is remote, which departments and cities are growing, and the median published salary range.

No login. No API key. No proxies. No browser.

***

### Why this one

- **Six systems, one table.** You do not need to know which system a company uses. Paste the board URL, the company's own careers page, or just a name — the actor works out the rest and tells you what it found.
- **Salaries as numbers.** `salaryMin`, `salaryMax`, `salaryCurrency`, `salaryInterval` — parsed from the pay-transparency data the company itself publishes, not guessed from the job text. On boards that publish pay, 80%+ of rows carry a range.
- **Only what is new.** `daysBack: 1` on a daily schedule returns just the jobs published since yesterday. No diffing on your side.
- **Hiring signals, ready to sort.** Summary mode turns 500 company boards into 500 rows you can rank by `newLast30d` — a sales-ready list of who is growing, and in which function.
- **It tells you when it could not.** A board that does not exist, a careers page with no board link, a run cut short — each is reported by name with the reason. Nothing fails silently, and nothing empty is billed.

***

### Two modes

**Jobs — one row per open job.** The default. Filter by title words (`engineer`, `account executive`), location words (`london`, `germany`), remote only, or publish date. Newest first.

**Summary — one hiring-signal row per company.** Reads each whole board and reports the counts. The same filters apply, so you can ask "how many *sales* roles does each of these 200 companies have open, and how many are new this month?"

***

### What you get

#### Jobs mode

**The job** — `title`, `department`, `team`, `employmentType`, `description` (optional, plain text)

**Where** — `location`, `locations` (all listed), `country`, `isRemote`, `workplaceType` (`remote`, `hybrid`, `onsite`)

**Pay** — `salaryMin`, `salaryMax`, `salaryCurrency`, `salaryInterval` (`year`, `month`, `hour` …), `salaryText` (the range as the company words it, e.g. `$211.4K – $290.6K • Offers Equity`)

**When** — `publishedAt`, `daysSincePublished`, `updatedAt`

**Links and IDs** — `jobUrl`, `applyUrl`, `companyName`, `company` (as you typed it), `ats`, `boardSlug`, `jobId`, `requisitionId`, `language`

#### Summary mode

`companyName`, `company`, `ats`, `boardUrl`, `openJobs`, `newLast7d`, `newLast30d`, `newLast90d`, `remoteJobs`, `remotePercent`, `hybridJobs`, `departmentsCount`, `topDepartments`, `topLocations`, `employmentTypes`, `jobsWithSalary`, `salaryCoveragePercent`, `usdSalaryJobs`, `medianSalaryMinUsd`, `medianSalaryMaxUsd`, `newestJobAt`, `oldestJobAt`, `medianJobAgeDays`, `filters`, `status`

***

### Typical uses

- **Sales and lead generation.** A company that opened twelve engineering roles this month has budget and a problem. Run summary mode weekly over your target accounts and sort by `newLast30d`; filter by `titleKeywords` to catch the function you sell to.
- **Recruiting and sourcing.** Track competitors' openings daily with `daysBack: 1`. See which teams they are building and in which cities.
- **Compensation benchmarking.** Pull every published range for `titleKeywords: ["product manager"]` across fifty companies into one sheet.
- **Job boards and aggregators.** A clean, deduplicated feed with stable IDs and apply links, straight from the source of truth.
- **Investors and analysts.** Hiring velocity across a portfolio or a sector, as a dated series when scheduled.

***

### Input

| Field | What it does |
|---|---|
| `companies` | One per line: a board URL (`https://job-boards.greenhouse.io/airbnb`, `https://jobs.lever.co/spotify`, `https://jobs.ashbyhq.com/openai`, `https://apply.workable.com/<company>`, `https://jobs.smartrecruiters.com/<Company>`, `https://<company>.recruitee.com`), a company careers page, `greenhouse:airbnb`, or a bare name. |
| `mode` | `jobs` (default) or `summary`. |
| `daysBack` | Jobs mode. `0` for all open jobs; `1` or `7` for what is new. |
| `titleKeywords` | Keep jobs whose title contains any of these. Both modes. |
| `locationKeywords` | Keep jobs whose location or country contains any of these. Both modes. |
| `remoteOnly` | Keep only jobs marked remote. Both modes. |
| `maxJobsPerCompany` | Jobs mode. Cap per company after filters, newest first. Default 50. |
| `includeDescription` | Jobs mode. Adds the full text. |

***

### What it costs

**$2.00 per 1,000 jobs** in jobs mode, **$10.00 per 1,000 company summaries** in summary mode, plus $0.005 per run. Apify's Starter, Scale and Business plans get 10%, 20% and 30% off.

You pay for rows delivered. A board that does not exist, a careers page with no board on it, a company with nothing open, or filters that match nothing cost nothing. Filters run before billing, so `daysBack: 1` on a daily schedule costs only the handful of new jobs.

***

### How it works

Every applicant tracking system publishes its customers' open jobs through a public JSON API — it is how companies embed the job list on their own website. This actor reads those APIs directly: Greenhouse's Job Board API, Lever's Postings API, Ashby's posting API, and the equivalent public endpoints of Workable, SmartRecruiters and Recruitee. No HTML is parsed for job data and no browser is launched, so a careers-page redesign does not break it, and a 600-job board reads in about a second.

When you paste a company's own careers page instead of a board URL, the actor fetches that one page and looks for the board link or embed the company put there.

***

### Honest limits

- **Six systems.** Companies on Workday, iCIMS, Taleo, SuccessFactors, BambooHR or a home-grown careers site are not covered. They are reported as "no supported job board link found" and cost nothing.
- **Salary is there only when the company publishes it** in the system's pay fields — common for US roles under pay-transparency laws, rare elsewhere. It is never extracted from the description text or estimated. Workable and SmartRecruiters expose no pay fields at all.
- **`salaryInterval` is empty when the company does not state the period** (Greenhouse has no period field; it is read from the range's own label, e.g. "Annual Pay Range"). In summary mode the median USD range uses ranges stated as yearly, plus unlabelled USD ranges of 15,000 or more; `usdSalaryJobs` says how many jobs that is, so you can judge a median built on three jobs.
- **Greenhouse has no remote flag.** `isRemote` is `true` there only when the company's own location text says "Remote", and empty otherwise — empty means unknown, not on-site.
- **Careers-page lookup reads one page without running JavaScript.** Pages that load their board with scripts, or sit behind bot protection, will not reveal it. Paste the board URL in that case; it is the link behind any "Apply" button.
- **A bare company name is a guess at the board name.** `figma` is tried on Greenhouse, Lever, Ashby, Workable and Recruitee in turn and the first board by that name wins; the status column says where it matched, and `companyName` lets you check it is who you meant. SmartRecruiters is skipped here because it answers "unknown company" and "nothing open" identically.
- **`publishedAt` is what each system calls the publish date**: first publication on Greenhouse, Ashby, Workable, SmartRecruiters and Recruitee; creation time on Lever. Companies sometimes re-post old roles, which resets it.
- **Date windows need the whole board.** If a run is cut short (rate limit, or the 10,000-posting safety cap on very large SmartRecruiters companies), `newLast7d/30d/90d` are left empty rather than reported as a floor, and `status` says why.
- **What is listed is what is public.** Internal-only and unlisted postings are not in these APIs.

***

### Scheduling

Schedule jobs mode daily with `daysBack: 1` for a feed of new openings, or summary mode weekly for a dated record of hiring velocity per company. Both are cheap: the daily run bills only the new jobs, and a weekly summary of 500 companies is $5.

***

### Legal

This actor reads only the public job-board APIs that applicant tracking systems provide for embedding job lists on company websites, without authentication. It requires no account and bypasses no access control. Job postings are public business information published for the purpose of being found; the actor returns no personal data about applicants or employees. You remain responsible for how you use the exported data.

***

Questions, a system you want added, or a board that behaves oddly? Open an issue on the actor — issues are read and answered.

# Actor input Schema

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

One company per line. Best: the job board URL — https://job-boards.greenhouse.io/airbnb, https://jobs.lever.co/spotify, https://jobs.ashbyhq.com/openai, https://apply.workable.com/<company>, https://jobs.smartrecruiters.com/<Company>, https://<company>.recruitee.com. Also accepted: a company's own careers page (the board link is looked up on it), the short form greenhouse:airbnb, or a bare name like figma (tried on each system in turn; the row says where it matched).

## `mode` (type: `string`):

"Jobs" gives one row per open job: title, department, location, remote flag, publish date, published salary range and link. "Summary" gives one hiring-signal row per company: open roles, new roles in the last 7/30/90 days, remote share, top departments and locations, salary coverage and median published USD range.

## `daysBack` (type: `integer`):

Jobs mode. Leave at 0 for every open job. Set 1 or 7 on a schedule to get only what is new since the last run. Summary mode ignores this and reports its own 7/30/90-day windows.

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

Keep only jobs whose title contains at least one of these words or phrases, case-insensitive — e.g. engineer, account executive, data. Leave empty for all titles. Applies in both modes, so a summary can count only sales roles, say.

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

Keep only jobs whose location text or country contains at least one of these, case-insensitive — e.g. london, germany, remote, US. Leave empty for all locations. Applies in both modes.

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

Keep only jobs the company marks as remote. On Greenhouse, which has no remote flag, that means the location text says "Remote". Applies in both modes.

## `maxJobsPerCompany` (type: `integer`):

Jobs mode. Upper limit of rows per company, newest first, after filters. Large companies list hundreds of jobs; the status column says how many matched when the limit cuts the list. Summary mode always reads the whole board.

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

Jobs mode. Adds a description column. Free on Greenhouse, Lever, Ashby and Recruitee, where the text comes with the list; on Workable and SmartRecruiters it takes one extra request per job, so runs are slower.

## Actor input object example

```json
{
  "companies": [
    "https://job-boards.greenhouse.io/airbnb",
    "https://jobs.ashbyhq.com/openai",
    "https://jobs.lever.co/spotify"
  ],
  "mode": "jobs",
  "daysBack": 0,
  "titleKeywords": [
    "engineer",
    "account executive"
  ],
  "locationKeywords": [
    "london",
    "new york"
  ],
  "remoteOnly": false,
  "maxJobsPerCompany": 50,
  "includeDescription": false
}
```

# Actor output Schema

## `rows` (type: `string`):

No description

## `rowsCsv` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "companies": [
        "https://job-boards.greenhouse.io/airbnb",
        "https://jobs.ashbyhq.com/openai",
        "https://jobs.lever.co/spotify"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("tidyfeed/company-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 = { "companies": [
        "https://job-boards.greenhouse.io/airbnb",
        "https://jobs.ashbyhq.com/openai",
        "https://jobs.lever.co/spotify",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("tidyfeed/company-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 '{
  "companies": [
    "https://job-boards.greenhouse.io/airbnb",
    "https://jobs.ashbyhq.com/openai",
    "https://jobs.lever.co/spotify"
  ]
}' |
apify call tidyfeed/company-jobs-scraper --silent --output-dataset

```

## MCP server setup

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