# Career Site & ATS Jobs Scraper – Greenhouse, Lever, Ashby (`glidepath/ats-jobs-scraper`) Actor

Jobs from company career sites on Greenhouse, Lever, Ashby, SmartRecruiters, Recruitee, Personio, Breezy & Workable. Input: company names or career-page URLs. Output: jobs with salary, location, remote flag. $1.75/1k jobs.

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

## Pricing

from $1.75 / 1,000 job listings

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

## Career Site & ATS Jobs Scraper – Greenhouse, Lever, Ashby & more

Get **every open job from a company's own careers page** in one clean, consistent format — no matter which applicant tracking system (ATS) the company uses. Give it company names, career-page URLs or `provider:slug` pairs; get back structured job listings with title, department, location, remote/hybrid flag, employment type, salary (when published), dates, apply link and the full description.

It reads the **official public job-board feeds** that these ATS vendors publish for career sites (no login, no cookies, no browser), so runs are fast, cheap and stable.

| Supported ATS | Example input |
|---|---|
| Greenhouse | `greenhouse:figma` or `https://boards.greenhouse.io/figma` |
| Lever (US & EU) | `lever:palantir`, `https://jobs.lever.co/palantir`, `lever-eu:acme` |
| Ashby | `ashby:ramp` or `https://jobs.ashbyhq.com/ramp` |
| SmartRecruiters | `smartrecruiters:Equinox` or `https://careers.smartrecruiters.com/Equinox` |
| Recruitee | `recruitee:bunq` or `https://bunq.recruitee.com` |
| Personio | `personio:acme` or `https://acme.jobs.personio.de` |
| Breezy HR | `breezy:acme` or `https://acme.breezy.hr` |
| Workable | `workable:huggingface` or `https://apply.workable.com/huggingface` |

Don't know which ATS a company uses? Just enter the **company website** (`stripe.com`) or **name** (`Figma`) — the Actor looks for the job board on the site and tries each supported ATS.

### Who uses it

- **Recruiters and sourcing teams** — watch target companies and get new openings every morning.
- **Job boards and aggregators** — feed niche boards (remote, climate, AI, Web3, local) straight from employers' ATS.
- **Sales and market intelligence** — hiring is a buying signal: track which companies hire for which teams and where.
- **Researchers and analysts** — salary transparency, remote-work share, hiring trends by department or country.
- **AI agents** — a single, predictable tool for "what jobs does company X have open right now?".

### How to use it

1. Add companies to **Companies or job-board URLs** (one per line).
2. Optionally add filters: title keywords, locations, remote only, posted within N days.
3. Run it and download the jobs as JSON, CSV or Excel — or call it via API, schedule it, or connect it to Make, Zapier, n8n, Google Sheets or your AI agent (Apify MCP).

**Monitoring tip:** turn on **Only jobs that are new since the last run** and schedule the Actor daily. The first run returns all current jobs; every later run returns only new postings, so you pay only for what's new.

### Input example

```json
{
  "companies": ["greenhouse:figma", "https://jobs.lever.co/palantir", "ramp.com"],
  "keywords": ["engineer", "developer"],
  "excludeKeywords": ["intern"],
  "locations": ["New York", "Remote"],
  "postedWithinDays": 30,
  "onlyNewSinceLastRun": false,
  "includeDescription": true
}
```

### Output example

```json
{
  "provider": "ashby",
  "company": "ramp",
  "companyName": null,
  "jobId": "4859cd5e-f2a9-44d7-81f7-8bfc0e62369f",
  "title": "Mobile Engineer, iOS",
  "department": "Engineering",
  "team": "Software Engineering - Industry",
  "employmentType": "Full-time",
  "workplaceType": "hybrid",
  "isRemote": true,
  "location": "New York, NY (HQ)",
  "locations": ["New York, NY (HQ)", "Remote (Canada)", "San Francisco, CA", "Remote (US)"],
  "city": "New York City",
  "region": "NY",
  "country": "USA",
  "countryCode": null,
  "postedAt": "2025-07-31T21:50:51Z",
  "updatedAt": null,
  "url": "https://jobs.ashbyhq.com/ramp/4859cd5e-f2a9-44d7-81f7-8bfc0e62369f",
  "applyUrl": "https://jobs.ashbyhq.com/ramp/4859cd5e-f2a9-44d7-81f7-8bfc0e62369f/application",
  "salary": {"min": 189000, "max": 330000, "currency": "USD", "period": "year", "text": "$189K - $330K"},
  "descriptionText": "ABOUT RAMP\n\nRamp is building the smart infrastructure for finance teams…",
  "descriptionHtml": null,
  "language": null,
  "scrapedAt": "2026-09-25T09:30:53Z"
}
```

| Field | Description |
|---|---|
| `provider`, `company` | ATS and the company's board id there |
| `companyName` | Company name when the board publishes it |
| `jobId` | Stable job id at the ATS (use it to de-duplicate) |
| `title`, `department`, `team` | As published by the employer |
| `employmentType` | Normalised: Full-time, Part-time, Contract, Internship, … |
| `workplaceType`, `isRemote` | `remote` / `hybrid` / `onsite` and a remote flag, when the employer states it |
| `location`, `locations`, `city`, `region`, `country`, `countryCode` | Location details as available per ATS |
| `postedAt`, `updatedAt` | ISO 8601 UTC |
| `url`, `applyUrl` | Public job page and application link |
| `salary` | `min`, `max`, `currency`, `period`, `text` when the employer publishes pay |
| `descriptionText`, `descriptionHtml` | Full description (plain text by default; HTML on request) |

A per-company summary (board found, jobs found, matched, saved, errors) is stored in the run's key-value store as `SUMMARY`.

### Pricing

Pay per result: **$1.75 per 1,000 job listings**, the same price on every Apify plan. There is no charge for companies that have no open jobs or that couldn't be found, and duplicates of the same board are never charged twice.

- Watch 50 companies daily with "only new" on: typically a few hundred new jobs a month → **well under $1/month**.
- Full export of 200 companies × ~150 jobs = 30,000 jobs → about **$52.50**.

Set a **maximum cost per run** in the run options and the Actor stops cleanly when it is reached.

### Limits and notes

- Only jobs the employer publishes on a supported public job board are returned. Companies on other systems (e.g. Workday, iCIMS, Taleo) are reported in `SUMMARY` as not found.
- Detection by company name picks the first supported ATS with a matching board name; for exact results use a career-page URL or `provider:slug`.
- Breezy HR descriptions are read from each public job page; everything else comes from the ATS feed in one request per company (SmartRecruiters: one extra request per job when descriptions are on).
- Descriptions are cleaned to plain text. E-mail addresses and phone numbers inside descriptions are removed by default.
- Workable occasionally rate-limits requests; the Actor waits and retries automatically.

### FAQ

**Is it legal to scrape job listings?** This Actor only reads job postings that employers publish on purpose through their ATS vendors' public job-board feeds, which exist so that the jobs can be shown on career sites and job boards. It doesn't log in, doesn't bypass any protection and doesn't collect personal data (recruiter names, e-mails and phone numbers are not extracted and are removed from descriptions). You are responsible for how you use the data; if you republish listings, respect the employer's terms and link back to the original job page.

**Can I get only new jobs?** Yes — enable *Only jobs that are new since the last run*. The Actor remembers job ids in a key-value store in your own account (name configurable, so you can keep several watchlists).

**Why is a company "not found"?** It uses a different ATS or a custom careers site. Try its career-page URL; if the page links to a supported job board, the Actor finds it.

**Can I use it from an AI agent?** Yes, via the Apify MCP server or API. Pass `companies` as a list; results are the dataset items above.

### Support

Open an issue on the Actor's **Issues** tab with the input you used and the run link — we reply within a few days.

Made by **Glidepath**. See the changelog for updates.

# Changelog

This Actor's version history is a separate document: https://apify.com/glidepath/ats-jobs-scraper/changelog.md

# Actor input Schema

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

One entry per company. Accepts (1) provider:slug, e.g. greenhouse:figma, lever:palantir, ashby:ramp, smartrecruiters:Equinox, recruitee:bunq, personio:personio, breezy:acme, workable:acme, lever-eu:acme; (2) a job-board URL, e.g. https://jobs.lever.co/palantir; (3) a company website, e.g. stripe.com (we look for the job board on the site); (4) a company name (we try each supported job board).

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

Keep jobs whose title contains any of these words (case-insensitive, whole words). Leave empty to keep all.

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

Drop jobs whose title contains any of these words, e.g. intern, senior.

## `searchInDescription` (type: `boolean`):

If on, a keyword found in the job description also counts as a match.

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

Keep jobs whose location contains any of these terms, e.g. Berlin, Germany, Remote, or a 2-letter country code like DE.

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

Keep only jobs the employer marks as remote (or whose location says Remote).

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

Keep only jobs published (or updated, if no publish date) in the last N days. Jobs without any date are dropped when this is set.

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

Stop after this many matching jobs per company. Leave empty for all jobs.

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

Stop the whole run after this many jobs. Leave empty for no limit (your maximum cost per run still applies).

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

Remember which jobs you already received (in a key-value store in your own account) and return only new ones. Ideal for scheduled monitoring. The first run returns all current jobs.

## `stateStoreName` (type: `string`):

Named key-value store used by 'Only new'. Use different names to keep separate watchlists.

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

Adds descriptionText. Turn off for smaller, faster output.

## `includeDescriptionHtml` (type: `boolean`):

Adds descriptionHtml with the original formatting.

## `redactContactInfo` (type: `boolean`):

Recommended. Job ads sometimes contain a recruiter's e-mail or phone number; this removes them.

## `detectFromWebsite` (type: `boolean`):

For entries like stripe.com, open the site (and /careers, /jobs) and look for a supported job board.

## Actor input object example

```json
{
  "companies": [
    "greenhouse:figma",
    "https://jobs.lever.co/palantir",
    "https://jobs.ashbyhq.com/ramp"
  ],
  "searchInDescription": false,
  "remoteOnly": false,
  "maxJobsPerCompany": 10,
  "onlyNewSinceLastRun": false,
  "stateStoreName": "glidepath-ats-jobs-state",
  "includeDescription": true,
  "includeDescriptionHtml": false,
  "redactContactInfo": true,
  "detectFromWebsite": true
}
```

# Actor output Schema

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

No description

## `summary` (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": [
        "greenhouse:figma",
        "https://jobs.lever.co/palantir",
        "https://jobs.ashbyhq.com/ramp"
    ],
    "maxJobsPerCompany": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("glidepath/ats-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": [
        "greenhouse:figma",
        "https://jobs.lever.co/palantir",
        "https://jobs.ashbyhq.com/ramp",
    ],
    "maxJobsPerCompany": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("glidepath/ats-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": [
    "greenhouse:figma",
    "https://jobs.lever.co/palantir",
    "https://jobs.ashbyhq.com/ramp"
  ],
  "maxJobsPerCompany": 10
}' |
apify call glidepath/ats-jobs-scraper --silent --output-dataset

```

## MCP server setup

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