# Greenhouse / Lever / Ashby Jobs Scraper - Personio & more (`tinyrex/ats-jobs-scraper`) Actor

Scrape all open jobs from Greenhouse, Lever, Ashby, Personio, Teamtailor and Recruitee career pages via official ATS feeds. Auto-detects the ATS from a company website. Pay per job.

- **URL**: https://apify.com/tinyrex/ats-jobs-scraper.md
- **Developed by:** [TinyRex](https://apify.com/tinyrex) (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.00 / 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.

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

## ATS Jobs Scraper – Greenhouse, Lever, Ashby, Personio, Teamtailor & Recruitee

Get **every open job from company career pages** in one clean, normalized dataset. Give it company websites, careers page URLs or job board links. The actor **auto-detects which applicant tracking system (ATS)** each company uses and pulls the jobs from the ATS's **official public job-board feed**. No browser, no login, no fragile HTML scraping.

**Why this one:** **$1 per 1,000 jobs**, while popular Greenhouse and ATS job APIs on the Store charge about $1.50–4 per 1,000. Companies without a supported ATS and jobs removed by your filters are **free**.

**Supported ATS:** Greenhouse · Lever (incl. EU) · Ashby · **Personio** · **Teamtailor** · **Recruitee**. The last three are the most popular in Europe and rarely covered by other scrapers.

- 🔎 **Auto-detection.** Just enter `stripe.com` or `polestar.com`. The actor scans the website and careers pages for ATS links, and checks the company name on each ATS when needed.
- 🧩 **One schema for all ATS.** Title, department, team, locations, country, remote/hybrid/on-site, salary (where published), posting date, URLs and description.
- 🔔 **Monitoring mode.** Schedule the actor and get **only new jobs since the last run**.
- 🎯 **Filters.** Title keywords (include/exclude), location, department, workplace type, posted within N days, max jobs per company.
- 💸 **Pay only for jobs.** Companies without a supported ATS cost nothing.
- 🤖 **API, MCP and AI-agent friendly.** Fast, structured JSON.

### Use cases

- **Job boards and aggregators.** Keep niche job boards (remote, EU tech, climate, fintech…) fresh from the source.
- **Sales intelligence.** Hiring signals. A company hiring 5 data engineers or its first salesperson in Germany is a lead.
- **Recruiting and talent intelligence.** Track competitors' openings, team growth and locations.
- **Market research.** Salary ranges (Ashby, Lever, Recruitee), remote share and hiring trends by company or department.
- **Job seekers and communities.** Monitor dream companies and get alerts for new roles.

### Input

| Field | Description |
|---|---|
| `companies` | One per line. Accepted formats: a website (`stripe.com`), a careers page or ATS URL (`https://jobs.lever.co/palantir`, `https://acme.jobs.personio.de`, `https://careers.acme.com`), an explicit board (`greenhouse:stripe`, `lever:palantir`, `lever-eu:qonto`, `ashby:ramp`, `personio:acme`, `teamtailor:acme`, `recruitee:acme`), or a company name (`Ramp`). |
| `titleKeywords` / `excludeTitleKeywords` | Include or exclude jobs by words in the title. |
| `locationKeywords` | E.g. `Berlin`, `Germany`, `DE`, `Remote`. |
| `workplaceTypes` | `remote`, `hybrid`, `onsite`. |
| `departmentKeywords` | E.g. `Engineering`, `Sales`. |
| `postedWithinDays` | Only recent jobs. 0 means all. |
| `maxJobsPerCompany` | Newest first. 0 means all. |
| `descriptionFormat` | `text` (default), `html`, `both` or `none`. |
| `onlyNewJobs` + `stateKey` | Monitoring mode: output only jobs not seen in previous runs. Use a separate `stateKey` per monitor, and a new key if you change filters. |
| `allowSlugGuess` | Look the company name up directly on each ATS if the website has no ATS link (default on). |
| `includeUnverifiedGuesses` | Also keep guessed boards whose company name cannot be verified (default off). |

Example:

```json
{
  "companies": ["stripe.com", "polestar.com", "https://jobs.ashbyhq.com/ramp", "personio:kb1", "bunq.com"],
  "titleKeywords": ["engineer", "developer"],
  "locationKeywords": ["Germany", "Remote", "Amsterdam"],
  "postedWithinDays": 30,
  "descriptionFormat": "text"
}
```

### Output

One item per job:

```json
{
  "company": "Ramp",
  "companyInput": "ramp.com",
  "ats": "ashby",
  "atsBoard": "ramp",
  "jobId": "baf0a4d2-e07e-4f76-af85-d85c91f479a4",
  "title": "Senior Manager, Account Manager | Mid-Market",
  "department": "Sales",
  "team": "Account Manager",
  "employmentType": "FullTime",
  "locations": ["New York, NY (HQ)", "San Francisco, CA"],
  "location": "New York, NY (HQ)",
  "country": "USA",
  "workplaceType": "hybrid",
  "salary": { "min": 290000, "max": 350000, "currency": "USD", "period": "1 YEAR", "text": "$290K - $350K" },
  "postedAt": "2026-09-23T16:31:53.863Z",
  "url": "https://jobs.ashbyhq.com/ramp/baf0a4d2-…",
  "applyUrl": "https://jobs.ashbyhq.com/ramp/baf0a4d2-…/application",
  "descriptionText": "About Ramp\nRamp is building…",
  "scrapedAt": "2026-10-04T07:15:18.313Z"
}
```

The key-value store also contains:

- **`COMPANIES`**: for every input, which ATS board was detected and how (direct URL, website scan or verified name guess), plus jobs found, filtered and saved, and notes for companies that were not found.
- **`SUMMARY`**: totals for the run.

Fields depend on what each ATS publishes. For example, salary is mostly available on Ashby, Lever and Recruitee, and the workplace type on Ashby, Lever, Recruitee and Teamtailor. Missing values are `null`.

### Pricing

Pay per event:

- **Job** (`job`): charged once per job saved to your dataset.
- **Free:** companies without a supported ATS, and jobs removed by your filters.

See the *Pricing* tab for the current price. Set a maximum cost per run and the actor stops gracefully when it is reached.

### How detection works

1. **Direct links.** ATS URLs and `ats:slug` inputs are used as they are.
2. **Website scan.** The homepage and up to 3 careers pages are scanned for Greenhouse, Lever, Ashby, Personio, Teamtailor or Recruitee links and embeds. Custom careers domains hosted on Teamtailor or Recruitee are recognized too.
3. **Verified name guess.** The company name (e.g. `datadog` from `datadoghq.com`) is tried on each ATS. A guessed board is accepted only if the company name published by the ATS matches, to avoid "same name, different company" mistakes.

### Limitations

- Only the six supported ATS. Companies on Workday, SmartRecruiters, Workable, SuccessFactors or their own systems are reported as "not found" and are free. More ATS are on the roadmap.
- Results reflect what the company publishes in its public job feed. Some companies hide postings from feeds or post only in their local language. For Personio, English is preferred, with German as fallback.
- Monitoring mode compares jobs by ATS job ID for the same set of companies and boards.

### Use with AI agents (MCP)

This Actor works as a tool for AI agents through the **Apify MCP server** at `https://mcp.apify.com`. Connect Claude, Cursor, VS Code, n8n or any other MCP client to `https://mcp.apify.com?tools=tinyrex/ats-jobs-scraper` and the agent can run it from a plain-language request, for example: *"Get all open engineering jobs in Germany at stripe.com, n26.com and personio.com"*. Results come back as clean, structured JSON at the same pay-per-result price, and failed inputs stay free.

📘 **Step-by-step guide with Python, JavaScript, curl and MCP examples:** [How to scrape Greenhouse, Lever and Ashby jobs (plus Personio, Teamtailor, Recruitee)](https://emirmrkaljevic.github.io/tinyrex-data-tools/scrape-greenhouse-lever-ashby-jobs/)

### Related actors

- [Arbeitsagentur & EURES Jobs Scraper](https://apify.com/tinyrex/dach-jobs-scraper): Jobs from Germany's Bundesagentur für Arbeit and EURES (Austria, Switzerland, EU) with salary and full descriptions.
- [Workday Jobs Scraper](https://apify.com/tinyrex/workday-jobs-scraper): Companies that hire on Workday (myworkdayjobs.com) instead: all jobs with full descriptions.
- [Remote Jobs Scraper](https://apify.com/tinyrex/remote-jobs-scraper): Remote jobs from Himalayas, Remote OK, We Work Remotely, Working Nomads and Arbeitnow in one run, with salary, location rules and full descriptions.
- [Tech Stack Detector](https://apify.com/tinyrex/tech-stack-detector): Enrich hiring companies with their tech stack, email provider and DMARC for sales outreach.
- [Shopify Products Scraper](https://apify.com/tinyrex/shopify-products-scraper): Export products and prices from Shopify stores.
- [WooCommerce Products Scraper](https://apify.com/tinyrex/woocommerce-products-scraper): Export products and prices from WooCommerce stores.

### FAQ

**Is this legal?** The actor reads the official public job-board feeds that the ATS vendors provide so companies can show their jobs on websites and job boards. It does not log in, does not bypass protections, and does not collect candidate data. Job postings may contain recruiter contact details written by the employer. Handle them according to your local data protection law.

**How fast is it?** Typically 50 companies and 4,000+ jobs in under a minute.

**Can I schedule it?** Yes. Use Apify Schedules with `onlyNewJobs: true` to get a daily feed of new jobs. Connect it to Slack, email, Google Sheets, Make, Zapier or n8n.

**My company isn't detected.** Pass the careers page URL or the job board URL directly, or open an issue with the company name and we will take a look.

# Actor input Schema

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

One company per line. Use a company website (stripe.com), a careers page or job board URL (https://jobs.lever.co/palantir, https://acme.jobs.personio.de, https://careers.acme.com), an explicit board like greenhouse:stripe / lever:palantir / ashby:ramp / personio:acme / teamtailor:acme / recruitee:acme, or just a company name (slug guess).

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

Keep only jobs whose title contains at least one of these words (case-insensitive), e.g. engineer, data, sales.

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

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

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

Keep only jobs whose location or country contains one of these strings, e.g. Berlin, Germany, Remote, DE.

## `workplaceTypes` (type: `array`):

Keep only remote, hybrid and/or on-site jobs. Jobs where the ATS does not say the workplace type are excluded when this filter is used.

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

Keep only jobs whose department or team contains one of these strings, e.g. Engineering, Marketing.

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

Keep only jobs posted (or first published) in the last N days. 0 = no limit.

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

Limit the number of jobs saved per company (newest first). 0 = all jobs.

## `descriptionFormat` (type: `string`):

Include the job description as plain text, HTML, both, or not at all (smaller output).

## `onlyNewJobs` (type: `boolean`):

Remember jobs between runs and output only jobs that were not seen before for the same companies. Ideal for scheduled runs. The first run outputs all jobs.

## `stateKey` (type: `string`):

Name of the monitoring state used by 'Only new jobs'. Use different keys for independent monitors. Stored in your named key-value store 'ats-jobs-scraper-state'.

## `allowSlugGuess` (type: `boolean`):

If no ATS link is found on the company website, try the company name directly on each supported ATS. Guessed boards are marked in the output (detectionNote).

## `includeUnverifiedGuesses` (type: `boolean`):

When a board is found by slug guess but the ATS does not expose a company name to verify it, include it anyway (marked with detectionNote). Off by default to avoid jobs from a different company with the same name.

## `personioLanguage` (type: `string`):

Preferred language code for Personio job descriptions (e.g. en, de). If empty, English is preferred and German is used as fallback.

## `maxConcurrency` (type: `integer`):

How many companies are processed in parallel.

## `proxyConfiguration` (type: `object`):

Usually not needed - public ATS feeds work without a proxy.

## Actor input object example

```json
{
  "companies": [
    "duolingo.com",
    "lever:spotify",
    "https://jobs.ashbyhq.com/ramp",
    "polestar.com",
    "bunq.com"
  ],
  "titleKeywords": [],
  "excludeTitleKeywords": [],
  "locationKeywords": [],
  "workplaceTypes": [],
  "departmentKeywords": [],
  "postedWithinDays": 0,
  "maxJobsPerCompany": 0,
  "descriptionFormat": "text",
  "onlyNewJobs": false,
  "stateKey": "default",
  "allowSlugGuess": true,
  "includeUnverifiedGuesses": false,
  "maxConcurrency": 10
}
```

# Actor output Schema

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

Normalized job postings from all detected job boards.

## `companies` (type: `string`):

For every input: detected ATS boards, detection method, number of jobs found/filtered/saved, notes.

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

Totals for the run.

# 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": [
        "duolingo.com",
        "lever:spotify",
        "https://jobs.ashbyhq.com/ramp",
        "polestar.com",
        "bunq.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("tinyrex/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": [
        "duolingo.com",
        "lever:spotify",
        "https://jobs.ashbyhq.com/ramp",
        "polestar.com",
        "bunq.com",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("tinyrex/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": [
    "duolingo.com",
    "lever:spotify",
    "https://jobs.ashbyhq.com/ramp",
    "polestar.com",
    "bunq.com"
  ]
}' |
apify call tinyrex/ats-jobs-scraper --silent --output-dataset

```

## MCP server setup

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