# Personio, Workable, Recruitee & Teamtailor Jobs Scraper (`swissalpinedata/eu-ats-jobs`) Actor

Collects open jobs from company career sites on Personio, Workable, Recruitee, SmartRecruiters and Teamtailor through their public job feeds.

- **URL**: https://apify.com/swissalpinedata/eu-ats-jobs.md
- **Developed by:** [Swiss Alpine Data](https://apify.com/swissalpinedata) (community)
- **Categories:** Jobs, Lead generation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$2.00 / 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

**EU ATS Jobs Scraper** is a **Personio jobs scraper, Workable jobs scraper, Recruitee scraper, SmartRecruiters scraper and Teamtailor scraper in one actor**. Paste company career pages and get every open job in one flat format, for hiring signals, sales leads, competitor monitoring or a job board.

Collects every open job from company career sites that run on the five applicant tracking systems most used by European companies: **Personio, Workable, Recruitee, SmartRecruiters and Teamtailor**. One actor, one flat output format, straight from the public job feeds that power the career pages. No browser, no login, no proxy.

### What it does

You paste a list of company career pages (or short slugs). The actor recognizes the ATS from the URL, reads the public job feed of that career site, and stores each open position as one flat JSON record: title, company, department, location, remote mode, employment type, seniority, publication date, job link, apply link and the full description as plain text. A bare slug such as `thermondo` is tried on all five systems and the first one with open jobs wins.

Optional keyword and location filters keep only the jobs you care about, and you pay only for the jobs that pass them.

### Why use it

- **One schema for five ATS.** Personio sends XML, Teamtailor RSS, the others JSON with different field names. You get the same 18 fields from all of them.
- **European coverage.** Most ATS scrapers in the Store target the US systems Greenhouse, Lever and Ashby. Personio is the leading HR platform for German speaking SMEs, Recruitee and Workable are common in the Netherlands, Greece and the UK, Teamtailor in the Nordics, SmartRecruiters at large groups such as Bosch.
- **Fresh data from the source.** Career site feeds update the moment a recruiter publishes or closes a job, usually days before the job reaches the big boards.
- **Typical uses:** monitor hiring at competitors or target accounts, sales triggers ("company X is hiring 5 engineers in Berlin"), job board aggregation, labor market research, recruiting agency lead lists.

### Input

| Field | Type | Description |
| --- | --- | --- |
| `companies` | list of strings, required | One company per line. A careers URL on the ATS domain, `ats:slug`, or a bare slug (tried on all five). |
| `keywords` | string | Comma separated words. Keeps a job when its title or department contains any of them. |
| `locations` | string | Comma separated cities or countries. Keeps a job when its location contains any of them. |
| `includeDescription` | boolean | Adds the full job description as plain text. Default true. |
| `maxItems` | integer | Maximum number of jobs across all companies. Default 100. |

Accepted company formats:

| ATS | Careers URL | Short form |
| --- | --- | --- |
| Personio | `https://ottonova.jobs.personio.de` | `personio:ottonova` |
| Workable | `https://apply.workable.com/blueground` | `workable:blueground` |
| Recruitee | `https://bunq.recruitee.com` | `recruitee:bunq` |
| SmartRecruiters | `https://jobs.smartrecruiters.com/BoschGroup` | `smartrecruiters:BoschGroup` |
| Teamtailor | `https://polestar.teamtailor.com` | `teamtailor:polestar` |

Example input:

```json
{
  "companies": [
    "https://thermondo.jobs.personio.de",
    "https://jobs.smartrecruiters.com/BoschGroup",
    "polestar"
  ],
  "keywords": "engineer, ingenieur, entwickler",
  "locations": "germany, berlin, sweden",
  "maxItems": 200
}
```

### Output example

One record per open job. This is a real record from a test run, with the description shortened.

```json
{
  "source": "recruitee",
  "company_slug": "bunq",
  "company_name": "bunq",
  "job_id": "2800490",
  "title": "Data Scientist Marketing",
  "department": "Growth",
  "location": "Amsterdam, Noord-Holland, Netherlands",
  "city": "Amsterdam",
  "region": "Noord-Holland",
  "country": "Netherlands",
  "remote": "hybrid",
  "employment_type": "fulltime_permanent",
  "seniority": "experienced",
  "published_at": "2026-10-28T13:21:58+00:00",
  "url": "https://careers.bunq.com/o/data-scientist-marketing-3",
  "apply_url": "https://careers.bunq.com/o/data-scientist-marketing-3/c/new",
  "description": "At bunq, we're on a mission to Make Life Easy for our users by creating the most user-centric banking experience out there. ...",
  "scraped_at": "2026-10-30T23:15:28.145188+00:00"
}
```

Field notes:

- `source` is the ATS (`personio`, `workable`, `recruitee`, `smartrecruiters`, `teamtailor`). `company_slug` is the career site identifier you can reuse as input.
- `remote` is `onsite`, `hybrid`, `remote`, or null when the career site does not say.
- `employment_type` and `seniority` keep the wording of each ATS (for example `permanent, full-time` on Personio, `Full-time` on SmartRecruiters).
- `country` is a name on most systems and an ISO code on SmartRecruiters. Personio feeds carry only the office name, so `country` is null there.
- `published_at` and `scraped_at` are ISO 8601 timestamps. Missing values are null, never guessed.

You can download the dataset as JSON, CSV, Excel or HTML, or read it through the Apify API.

### Pricing

This Actor uses pay per result. You pay **USD 0.002 per job**, which is **USD 2.00 per 1,000 jobs**. There is no charge per run or per company, and jobs removed by your filters are not charged.

| Jobs | Price |
| --- | --- |
| 100 | USD 0.20 |
| 1,000 | USD 2.00 |
| 10,000 | USD 20.00 |

Tracking 50 companies with about 20 open jobs each every week costs about USD 2 per run, USD 8 per month. Set `maxItems` to cap the cost of a run.

### Limitations

- Only career sites hosted on the five ATS domains are supported. A company that shows its jobs on its own domain usually still has the ATS address behind it (look for links to `jobs.personio.de`, `recruitee.com`, `teamtailor.com`, `workable.com` or `smartrecruiters.com` on its careers page).
- Teamtailor career sites on a fully custom domain are not detected from that domain. Use `teamtailor:slug` with the company's Teamtailor slug.
- SmartRecruiters needs one extra request per job for the description, so large SmartRecruiters companies take about one second per job. Turn off `includeDescription` for fast overview runs.
- Keyword and location filters run after the feed is read, so very large companies are still read in full before filtering.

### Legal

The actor reads only the public job feeds that each ATS publishes for the career sites it hosts, the same data any visitor sees on the careers page. No login, paywall or bot protection is bypassed and no proxies are used. Job ads can contain names and contact details of recruiters. You are responsible for processing the collected data in line with the GDPR and the terms of the respective career sites.

### FAQ

**How do I find which ATS a company uses?**
Open the company's careers page and click a job. The address or the apply button usually points to one of the five ATS domains. You can also just enter the company name as a slug and the actor tries all five.

**Why does a company return no jobs?**
Either it has no open jobs right now, or the slug is different from the company name. The log tells you which ATS was tried.

**Can I monitor companies daily?**
Yes. Create a task with your company list and schedule it. Each run returns the currently open jobs, so new `job_id` values compared to the last run are the new openings.

**Does it support Greenhouse, Lever or Ashby?**
Not in this actor, which focuses on European systems. Tell us in the Issues tab if you need them.

### Related actors

More public data actors by Swiss Alpine Data, all pay per result with no login:

- [Swiss Jobs Scraper](https://apify.com/swissalpinedata/swiss-jobs-scraper): Swiss jobs from jobs.ch, jobup.ch, JobScout24 and job-room.ch in one schema
- [Arbeitsagentur Jobs Scraper](https://apify.com/swissalpinedata/arbeitsagentur-jobs-scraper): German jobs from the Bundesagentur für Arbeit Jobbörse (Jobsuche API)
- [Zefix Swiss Company Register Scraper](https://apify.com/swissalpinedata/swiss-company-register-scraper): Swiss Handelsregister (Zefix) search and daily new companies from SHAB
- [Austria Jobs Scraper](https://apify.com/swissalpinedata/austria-jobs-scraper): Austrian jobs from karriere.at and jobs.at in one schema
- [Swiss Rentals Scraper (Flatfox)](https://apify.com/swissalpinedata/swiss-rentals-scraper): Swiss apartments and houses for rent or sale from Flatfox

### Changelog

**0.1** (2026-10-31): Initial release. Personio, Workable, Recruitee, SmartRecruiters and Teamtailor with URL detection, slug probing, keyword and location filters, and plain text descriptions.

# Actor input Schema

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

One company per line. Paste the careers page URL on the ATS domain (for example https://ottonova.jobs.personio.de, https://apply.workable.com/blueground, https://bunq.recruitee.com, https://jobs.smartrecruiters.com/BoschGroup, https://polestar.teamtailor.com), or write ats:slug (for example personio:ottonova). A bare slug is tried on all five systems.

## `keywords` (type: `string`):

Optional. Comma separated words; a job is kept when its title or department contains any of them (case insensitive). Leave empty to keep all jobs.

## `locations` (type: `string`):

Optional. Comma separated cities or countries; a job is kept when its location contains any of them (case insensitive). Leave empty to keep all locations.

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

Adds the full job description as plain text. For SmartRecruiters this needs one extra request per job, so runs are slower.

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

Maximum number of jobs across all companies. You pay per job that passes the filters, so this also caps the cost. Default 100.

## Actor input object example

```json
{
  "companies": [
    "https://ottonova.jobs.personio.de",
    "https://apply.workable.com/blueground",
    "https://bunq.recruitee.com",
    "https://jobs.smartrecruiters.com/BoschGroup",
    "https://polestar.teamtailor.com"
  ],
  "keywords": "",
  "locations": "",
  "includeDescription": true,
  "maxItems": 20
}
```

# Actor output Schema

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

All records returned by this run, as JSON.

# 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://ottonova.jobs.personio.de",
        "https://apply.workable.com/blueground",
        "https://bunq.recruitee.com",
        "https://jobs.smartrecruiters.com/BoschGroup",
        "https://polestar.teamtailor.com"
    ],
    "keywords": "",
    "locations": "",
    "includeDescription": true,
    "maxItems": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("swissalpinedata/eu-ats-jobs").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://ottonova.jobs.personio.de",
        "https://apply.workable.com/blueground",
        "https://bunq.recruitee.com",
        "https://jobs.smartrecruiters.com/BoschGroup",
        "https://polestar.teamtailor.com",
    ],
    "keywords": "",
    "locations": "",
    "includeDescription": True,
    "maxItems": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("swissalpinedata/eu-ats-jobs").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://ottonova.jobs.personio.de",
    "https://apply.workable.com/blueground",
    "https://bunq.recruitee.com",
    "https://jobs.smartrecruiters.com/BoschGroup",
    "https://polestar.teamtailor.com"
  ],
  "keywords": "",
  "locations": "",
  "includeDescription": true,
  "maxItems": 20
}' |
apify call swissalpinedata/eu-ats-jobs --silent --output-dataset

```

## MCP server setup

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

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/WQGRQiI2uKXDhoLWF/builds/bBE5fNM0heXEhB6pW/openapi.json
