# Personio Jobs Scraper & API: Any Company Careers Page (`mrslm_data/personio-jobs`) Actor

Scrape any company's Personio careers page: job title, office, department, employment type, seniority, schedule, salary band and full description. CSV/JSON.

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

## Pricing

from $0.80 / 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

## Personio Jobs Scraper & API: Any Company Careers Page

**Open roles from any company's Personio careers page: office, department, employment type, seniority, schedule, salary band and full description. $1.00 per 1,000 jobs.**

Personio is the leading HR system for German-speaking SMEs, so it is the best source for DACH hiring data.

### Quick start

1. Press **Start**. The form is already filled with this example input, so the first run returns real rows in under a minute:

```json
{
  "companies": [
    "tado",
    "https://1komma5grad.jobs.personio.de"
  ],
  "maxJobsPerCompany": 20
}
```

2. Swap in your own values, or call the Actor through the Apify API, Make, Zapier, n8n or the Python/JS clients with the same JSON.
3. Download the results as JSON, CSV or Excel from the **Output** tab, or schedule the run to repeat.

**Full guide: How to Scrape Personio Job Listings from Any Company Careers Page (Real Run: 31 Jobs from 4 German Companies)**

### What the Personio Jobs Scraper does

You give the Actor a list of companies, either the board name (`tado`) or the careers page URL (`https://1komma5grad.jobs.personio.de`, `.de` or `.com`). For each one it reads the company's public Personio job feed, `https://<company>.jobs.personio.de/xml?language=en`, the same list the careers page shows, and saves one row per open role.

Each row has the platform, the board name and the company's display name, the job ID, title, department and team (Personio's recruiting category), the first office and all offices, employment type, seniority, schedule (full-time, part-time), years of experience, keywords, occupation and occupation category, the salary band when published (minimum, maximum, currency and period), the posting date, the job page and apply links, and the board URL. With **Include description** on, the full job description is added as plain text (up to 5,000 characters, with e-mail addresses and phone numbers removed).

You can filter by title words (include or exclude), location, department or team, remote, and posting date, sort newest first, and cap the jobs per company and per run. Nothing needs a login, and the Actor reads only public job ads.

### Who it is for

- **Recruiters and staffing agencies** who want a daily list of new roles at target companies in DACH and Europe, including the ones that never reach LinkedIn.
- **Sales teams** that sell to companies that are hiring: a company opening five sales roles is growing; a company hiring its first Head of Finance is about to buy finance software.
- **Job seekers and job board builders** who want a clean feed of roles from a curated list of employers.
- **Investors and analysts** who track headcount plans of private companies through their open roles, departments and locations.
- **HR and compensation teams** who benchmark published salary bands.

### Real example: open roles at four German companies

Say you track hiring at a handful of German scale-ups and want the newest roles from each, capped at 10 per company so one big employer does not drown the rest.

#### Step 1: Enter the input

Open the [Personio Jobs Scraper](https://apify.com/mrslm_data/personio-jobs) in Apify Console, switch the input form to **JSON** and paste this. It is the exact input used for the run:

```json
{
    "companies": [
        "tado",
        "https://1komma5grad.jobs.personio.de",
        "ottonova",
        "chrono24"
    ],
    "maxJobsPerCompany": 10,
    "newestFirst": true
}
```

What each setting does:

- `companies`: four boards. `tado`, `ottonova` and `chrono24` are board names (the part before `.jobs.personio.de`); 1KOMMA5° is given as its full careers page URL, which works the same way. You can mix both styles in one list.
- `maxJobsPerCompany`: `10`, the most roles saved per company. 0 means all open roles.
- `newestFirst`: `true` (the default) sorts each company's jobs by posting date before the per-company cap is applied, so the cap keeps the newest roles.

The other inputs on the form:

- `titleKeywords`: keep jobs whose title contains any of these words, for example `engineer` or `sales`. `excludeTitleKeywords` drops titles with any of these words, for example `Werkstudent` or `intern`.
- `locationKeywords`: keep jobs whose location contains any of these, for example `Berlin`, `München` or `remote`.
- `departmentKeywords`: keep jobs whose department or team contains any of these, for example `engineering` or `finance`.
- `remoteOnly`: keep only remote jobs (see the note on remote below).
- `postedWithinDays`: keep only jobs posted in the last N days. 0 means any date.
- `includeDescription`: add the full description as plain text. Rows get much larger, so leave it off for lists.
- `maxResults`: a safety cap on jobs saved across the whole run, and so on cost.
- `proxyConfiguration`: usually not needed; the feeds are public.

#### Step 2: Run it

Press **Start**. The run finished with status `SUCCEEDED` in about 6 seconds and saved 31 rows. It was charged 31 `job` events plus the start event, $0.0311 on the Free/Bronze tier.

The run log shows what each board had open:

- tado (tado GmbH): 2 open roles, 2 saved.
- ottonova: 9 open roles, 9 saved.
- Chrono24 (Chrono24 GmbH): 12 open roles, 10 saved (the cap).
- 1KOMMA5° (1KOMMA5° GmbH): 311 open roles, 10 saved (the cap).

Only the saved rows are charged, so the 301 1KOMMA5° roles that the cap skipped cost nothing.

#### Step 3: Read the output

All 31 rows, cut to the most useful columns:

| company | title | department | location | employmentType | seniority | salary | postedAt |
|---|---|---|---|---|---|---|---|
| tado | Werkstudent:in Accounting (m/w/d) | Finance | Germany - Munich | intern | student | – | 2026-10-13 |
| tado | Team Lead Accounting (m/f/d) | Finance | Germany - Munich | permanent | experienced | 80,000–100,000 EUR/yearly | 2026-01-22 |
| ottonova | Senior Key Account Manager (m/w/d) - Partnervertrieb / Krank | B2B Sales | München | permanent | experienced | – | 2026-10-19 |
| ottonova | Werkstudent - Product Management / Health (m/w/d) | Product | München | intern | student | – | 2026-09-30 |
| ottonova | Quereinstieg Leistungssachbearbeitung (Zahn) als Medizinisch | Claims Management | München | permanent | experienced | – | 2026-09-28 |
| ottonova | Head of Finance & Controlling (m/w/d) | Finance | München | permanent | experienced | – | 2026-09-22 |
| ottonova | Key Account Manager/in, Maklerbetreuer/in Firmengeschäft für | B2B Sales | München | permanent | experienced | – | 2026-09-14 |
| ottonova | Werkstudent – Kommunikationsdesign (m/w/d) | Design | München | intern | student | – | 2026-09-10 |
| ottonova | Senior AI Engineer – Architecture & Platform (m/w/d) | Data & Analytics | München | permanent | experienced | – | 2026-06-29 |
| ottonova | (Senior) Aktuar (m/w/d) mit Schwerpunkt Solvency II (20h) (m | Risk Management & Actuary | München | permanent | experienced | – | 2026-03-23 |
| ottonova | Initiativ bewerben (m/w/d) | People & Culture | München | permanent | experienced | – | 2026-02-27 |
| chrono24 | Resolution Specialist (m/w/d) | Operations | Karlsruhe | temporary | experienced | 38,000–40,000 EUR/yearly | 2026-10-29 |
| chrono24 | Transfer Pricing Accountant (m/w/d) | Finance | Remote | permanent | experienced | – | 2026-10-08 |
| chrono24 | Prozessmanager Accounting – Schwerpunkt Automatisierung & KI | Finance | Karlsruhe | permanent | experienced | – | 2026-10-08 |
| chrono24 | Director of US Marketing | Marketing & Branding | New York City | permanent | experienced | – | 2026-10-05 |
| chrono24 | (Senior) Full Stack Engineer (Backend Focus) (m/f/d) | Product & Technology | Remote | permanent | experienced | – | 2026-10-05 |
| chrono24 | Complaint Specialist | Operations | Miami | permanent | experienced | – | 2026-10-01 |
| chrono24 | Customer Care Manager (1-year contract) | Operations | Japan | fixed-term | entry-level | – | 2026-08-10 |
| chrono24 | Customer Care Manager (1-year contract) | Operations | Hong Kong | fixed-term | entry-level | – | 2026-08-10 |
| chrono24 | AI Solution Engineer (m/f/d) | Data | Karlsruhe | permanent | experienced | – | 2026-07-30 |
| chrono24 | Android Developer (m/f/d) | Product & Technology | Remote | permanent | experienced | – | 2026-07-28 |
| 1komma5grad | Technische Betriebsleitung & Elektromeister / Elektroinstall | Elektronik & Technik | München | permanent | experienced | – | 2026-10-30 |
| 1komma5grad | Software Engineer - Edge & Cloud (m/f/d) | Tech | Remote | permanent | experienced | – | 2026-10-30 |
| 1komma5grad | Product Manager Energy Markets & Flexibility (m/f/d) | Tech | Remote | permanent | experienced | – | 2026-10-30 |
| 1komma5grad | Technischer Planer (m/w/d) Wärmepumpe - 1KOMMA5° Breidenbach | Planung & Projektmanagement | Breidenbach | permanent | experienced | – | 2026-10-30 |
| 1komma5grad | Werkstudent\*in Vertrieb & Kundenbetreuung (m/w/d) – Erneuerb | Backoffice | Bremen | intern | entry-level | – | 2026-10-30 |
| 1komma5grad | Werkstudent (m/w/d) Auftragsmanagement - 1KOMMA5° Riederich | Auftragsmanagement | Riederich | working-student | student | – | 2026-10-29 |
| 1komma5grad | Management Trainee (m/w/d) – Future Leadership Program / 1KO | Commercial GmbH | Hamburg | trainee | entry-level | – | 2026-10-28 |
| 1komma5grad | Leitender Elektriker / Elektroniker (m/w/d) für gewerbliche  | Elektronik & Technik | Frankfurt am Main | permanent | experienced | – | 2026-10-28 |
| 1komma5grad | Bauleiter für PV-Großanlagen, Batteriespeicher und Ladeinfra | Handwerk | Berlin | permanent | experienced | – | 2026-10-28 |
| 1komma5grad | Bauleiter für PV-Großanlagen, Batteriespeicher und Ladeinfra | Handwerk | Berlin | permanent | experienced | – | 2026-10-28 |

One full row as JSON:

```json
{
    "platform": "Personio",
    "company": "tado",
    "companyName": "tado GmbH",
    "jobId": "2502124",
    "title": "Team Lead Accounting (m/f/d)",
    "department": "Finance",
    "team": "People & Culture",
    "location": "Germany - Munich",
    "locations": [
        "Germany - Munich"
    ],
    "country": null,
    "workplaceType": null,
    "isRemote": null,
    "employmentType": "permanent",
    "seniority": "experienced",
    "salary": {
        "min": 80000,
        "max": 100000,
        "currency": "EUR",
        "period": "yearly",
        "text": null
    },
    "postedAt": "2026-01-22T09:48:25.000Z",
    "updatedAt": null,
    "url": "https://tado.jobs.personio.de/job/2502124",
    "applyUrl": "https://tado.jobs.personio.de/job/2502124#apply",
    "schedule": "full-time",
    "yearsOfExperience": "7-10",
    "keywords": [
        "Accounting",
        "Finance",
        "Buchhaltung",
        "general ledger",
        "account payables",
        "account receivables",
        "financial reporting",
        "IFRS",
        "UGB",
        "HGB",
        "audit"
    ],
    "occupation": "bookkeeping",
    "occupationCategory": "accounting_and_finance",
    "boardUrl": "https://tado.jobs.personio.de/"
}
```

What the 31 real rows show:

- **1KOMMA5° is hiring at scale.** 311 open roles, and all 10 newest were posted between 28 and 30 October 2026: site managers for large PV and battery projects in Berlin, electricians in Frankfurt, a heat pump planner in Breidenbach, software and product roles listed as Remote. The `team` field shows which local business each role belongs to (for example "1K5° Commercial" or "1K5° Augsburg"), which reflects the company's network of regional installers.
- **Salary bands are rare but useful.** 2 of 31 roles publish a band: tado's Team Lead Accounting in Munich at 80,000–100,000 EUR a year, and Chrono24's Resolution Specialist in Karlsruhe at 38,000–40,000 EUR a year. When a company does publish them, they are in a structured field, not buried in the text.
- **Seniority and schedule are structured.** Personio companies tag each role with `employmentType` (permanent, temporary, fixed-term, intern, trainee, working-student), `seniority` (student, entry-level, experienced) and `schedule` (full-time, part-time, full-or-part-time). Filtering out working students is one column filter, not a keyword guess.
- **Chrono24 hires beyond Germany.** Its 10 newest roles include a Director of US Marketing in New York City, a Complaint Specialist in Miami, and Customer Care Managers in Japan and Hong Kong, alongside Karlsruhe and Remote roles.
- **Titles are often German.** "Werkstudent", "Quereinstieg Leistungssachbearbeitung" and "Bauleiter" are typical. If you filter by title, include the German words (`Werkstudent`, `Entwickler`, `Vertrieb`) as well as the English ones.
- **Occupation categories make cross-company counts easy.** `occupationCategory` puts every role in a broad family such as `accounting_and_finance`, `it_software` or `sales_and_business_development`; in this sample, finance and accounting roles appear at three of the four companies.

### Finding a company's Personio board

Most companies link to their Personio careers page from the "Jobs" or "Karriere" page of their website; the address ends in `.jobs.personio.de` or `.jobs.personio.com`. The part before `.jobs` is the board name. If a name is wrong or the company has left Personio, the run reports that board as "not found" in the log and continues with the others, and nothing is charged for it.

A company's `companyName` comes from the feed and can be a legal entity or subsidiary name (for example "ottonova Krankenversicherung AG - 9698"), while `company` is always the board name you entered. Group by `company` when you count jobs per employer.

### A note on remote jobs

Personio's feed has no separate remote flag, so for Personio rows `isRemote` and `workplaceType` are always empty (`null`). `remoteOnly` keeps a job when its location text contains the word "remote", as Chrono24's and 1KOMMA5°'s "Remote" roles do. Roles that are remote but list a city as the office will not match. For a looser filter, use `locationKeywords: ["remote", "homeoffice"]` and check the description.

### Output fields

| Field | What it means | Example from this run |
|---|---|---|
| `platform` | Always Personio. | Personio |
| `company / companyName` | Board name you entered and the company name in the feed. | tado / tado GmbH |
| `jobId` | Personio job ID. | 2502124 |
| `title` | Job title as posted. | Team Lead Accounting (m/f/d) |
| `department / team` | Department and Personio recruiting category. | Finance / People & Culture |
| `location / locations` | First office and all offices. | Germany - Munich / \["Germany - Munich"] |
| `country / workplaceType / isRemote` | Not in Personio feeds, always null. | null / null / null |
| `employmentType` | permanent, temporary, fixed-term, intern, trainee, working-student and so on. | permanent |
| `seniority` | student, entry-level, experienced and so on. | experienced |
| `schedule` | full-time, part-time or full-or-part-time. | full-time |
| `yearsOfExperience` | Required experience band, when given. | 7-10 |
| `salary` | min, max, currency and period when the company publishes a band; otherwise null. | 80000 / 100000 / EUR / yearly |
| `keywords` | Keywords the company tagged the job with. | Accounting, Finance, Buchhaltung, IFRS, HGB… |
| `occupation / occupationCategory` | Personio occupation and broad category. | bookkeeping / accounting_and_finance |
| `postedAt / updatedAt` | When the job was created; updatedAt is not in the feed (null). | 2026-01-22T09:48:25.000Z / null |
| `url / applyUrl / boardUrl` | Job page, apply anchor and careers page. | https://tado.jobs.personio.de/job/2502124 |
| `description` | Full description as plain text, only with includeDescription. | (off in this run) |
| `scrapedAt` | When the row was read. | 2026-10-30T23:15:02.555Z |

### Export Personio jobs to CSV, Excel or Google Sheets

Every run saves its rows to a dataset, and a dataset can be downloaded in any common format without code.

**From Apify Console.** Open the run, go to the **Output** tab and click **Export**. Pick CSV, Excel (XLSX), JSON, XML, HTML or RSS, and tick only the columns you want. Nested values such as lists and objects are flattened into columns like `name/0` or `object/field` in CSV and Excel.

**From a URL.** The same export is a plain HTTPS link. Replace `<DATASET_ID>` with the dataset ID shown on the run's **Storage** tab:

```bash
## CSV with just the columns you need
curl -H "Authorization: Bearer $APIFY_TOKEN" \
  "https://api.apify.com/v2/datasets/<DATASET_ID>/items?format=csv&clean=1&fields=company,companyName,title,department,location,employmentType,seniority,schedule,salary/min,salary/max,salary/currency,postedAt,url" -o personio-jobs.csv

## Excel
curl -H "Authorization: Bearer $APIFY_TOKEN" \
  "https://api.apify.com/v2/datasets/<DATASET_ID>/items?format=xlsx&clean=1" -o personio-jobs.xlsx
```

**Into Google Sheets.** Three options, from simplest to most automatic:

1. **One-off:** download the CSV and use **File > Import > Upload** in Google Sheets. Choose "Replace current sheet" to refresh an existing tab.
2. **Automatic, no code:** use Make, Zapier or n8n as described in the next section, ending in a Google Sheets "Add row" step.
3. **Formula:** `=IMPORTDATA("https://api.apify.com/v2/actor-tasks/<USERNAME>~<TASK_NAME>/runs/last/dataset/items?status=SUCCEEDED&format=csv&token=<YOUR_TOKEN>")` always pulls the latest successful run of a saved task. The token sits in the formula, so anyone who can open the sheet can see it. Only use this in a private sheet.

### Use it from Make, Zapier or n8n

All three automation tools have an official Apify app, so you can run this Actor and pass its rows on without writing code.

1. **Trigger.** Use a schedule in the automation tool, or Apify's "Watch Actor runs" / "Watch task runs" trigger if you already schedule the task in Apify.
2. **Run.** Add the Apify "Run Actor" (or "Run task") action, choose `mrslm_data/personio-jobs` and paste the JSON input from Step 1. Turn on "wait for finish" so the next step gets the data.
3. **Get the rows.** Add "Get dataset items" and map the run's default dataset ID from the previous step.
4. **Send them on.** Add a Google Sheets "Add row" step for a hiring tracker, a HubSpot or Pipedrive step that tags the company as "hiring" when a sales or finance role appears, or a Slack message per new role with its title, location and link.

Each module only moves data; the Actor's pricing is the same as when you run it in Console.

### Schedule a daily new-roles alert

Companies add and close roles every day. A daily run with `postedWithinDays: 1` (or 2, to be safe across weekends) returns only roles created in the last one or two days, so each run is a short list of what is new (remove repeats by `url` if the windows overlap). Closed roles simply disappear from the feed.

1. In the Actor's input form, set up the input from Step 1 and click **Save as new task**. Give it a clear name such as `personio-new-roles-daily`.
2. Go to **Schedules > Create new**, set the cron expression to `0 8 * * 1-5` and pick your timezone, so it runs at 8 AM every weekday.
3. Add the task to the schedule and save. Each run gets its own fresh dataset.
4. Optional: on the task's **Integrations** tab, add a webhook, Slack or email notification for "Run succeeded", or connect Make, Zapier or n8n, so the new rows go where you need them.

To fetch the newest results without looking up a run ID, use the task's last-run link: `https://api.apify.com/v2/actor-tasks/<USERNAME>~personio-new-roles-daily/runs/last/dataset/items?status=SUCCEEDED&format=json`.

### Call it from the API (Python, JavaScript or curl)

Start the Actor from your own code with the official Apify clients. Put your API token (Console > Settings > API & Integrations) in an environment variable called `APIFY_TOKEN` rather than in the code.

**Python** (`pip install "apify-client>=3"`):

```python
import os
from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])
run_input = {
    "companies": [
        "tado",
        "https://1komma5grad.jobs.personio.de",
        "ottonova",
        "chrono24"
    ],
    "maxJobsPerCompany": 10,
    "newestFirst": true
}

run = client.actor("mrslm_data/personio-jobs").call(run_input=run_input)

## apify-client 3.x returns a Run object, so use run.default_dataset_id
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item["company"], item["title"], item["location"], item["postedAt"])
```

In apify-client 3.x, `call()` returns a typed object. Older examples that use `run["defaultDatasetId"]` were written for 1.x and 2.x and fail in 3.x with `TypeError: 'Run' object is not subscriptable`.

**JavaScript / Node.js** (`npm install apify-client`):

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

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const input = {
    "companies": [
        "tado",
        "https://1komma5grad.jobs.personio.de",
        "ottonova",
        "chrono24"
    ],
    "maxJobsPerCompany": 10,
    "newestFirst": true
};

const run = await client.actor('mrslm_data/personio-jobs').call(input);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.length, items[0].title);
```

**curl**, one request that runs the Actor and returns the rows as CSV (it waits up to 5 minutes, so keep the input small):

```bash
curl -X POST "https://api.apify.com/v2/acts/mrslm_data~personio-jobs/run-sync-get-dataset-items?format=csv" \
  -H "Authorization: Bearer $APIFY_TOKEN" -H "Content-Type: application/json" \
  -d '{"companies": ["tado", "https://1komma5grad.jobs.personio.de", "ottonova", "chrono24"], "maxJobsPerCompany": 10, "newestFirst": true}' -o personio-jobs.csv
```

### Use it from AI agents and chat assistants (MCP)

Apify runs a hosted Model Context Protocol (MCP) server, so assistants such as Claude Desktop, Cursor, VS Code or any other MCP client can call this Actor as a tool. Add this server URL to your MCP client and sign in with your Apify account (or send your API token as a Bearer header):

```
https://mcp.apify.com/?tools=mrslm_data/personio-jobs
```

The assistant then sees the Actor's input fields, can start a run with the input it builds from your request, and reads the dataset rows back. A prompt like this works well:

> Using Personio, list the open engineering roles at tado, chrono24 and 1komma5grad posted in the last 14 days, with location and seniority.

Runs started through MCP are billed exactly like any other run, per row saved, so it is worth asking the assistant to set `maxResults`.

### What it costs

The Actor uses pay-per-event pricing: one `job` event per role saved, and nothing for compute or for roles that your filters or caps skip.

#### Price per event

| Event (per 1,000) | Free / Bronze | Silver | Gold / Platinum / Diamond |
|---|---|---|---|
| Job saved (`job`) | $1.00 | $0.90 | $0.80 |
| Actor start (once per run at the default memory, not per 1,000) | $0.00005 | $0.00005 | $0.00005 |

You are charged only for the events in this table: nothing for compute, and nothing for searches that return no rows beyond the start fee.

| Scenario | Free / Bronze | Silver | Gold |
|---|---|---|---|
| The run in this guide (31 jobs) | $0.0311 | $0.0280 | $0.0249 |
| Daily new-roles alert for 50 companies, ~20 new roles a day (22 runs a month) | $0.44 | $0.40 | $0.35 |
| Weekly full snapshot of 50 companies, ~1,000 roles (4 runs a month) | $4.00 | $3.60 | $3.20 |
| One-off: all 311 open roles at 1KOMMA5° | $0.3111 | $0.2800 | $0.2489 |

The daily and weekly rows are estimates; your counts depend on the companies you track. To cap spending, set **Maximum cost per run** in the run options (or `maxTotalChargeUsd` in the API), or use `maxResults`.

### Tips

- **Filter before you pay.** Title, location, department and date filters run before rows are saved, so roles they drop are never charged.
- **Exclude students in German too.** `excludeTitleKeywords: ["Werkstudent", "Praktikum", "intern"]` removes most student roles; or filter on `seniority` = student in your sheet.
- **Use `maxJobsPerCompany` with `newestFirst`.** It keeps the newest roles from each company, so big employers do not crowd out small ones.
- **Turn descriptions on only when you need them**, for example to search for a technology or to feed an AI summariser.
- **One run, many companies.** Put all your boards in one task; the run reads each feed once.

### Limits

- Only companies that use Personio and publish a public careers page. Other applicant tracking systems need their own Actor (see the FAQ).
- What you get is what the company publishes: salary bands, experience and keywords are optional in Personio, so many rows have them empty.
- Personio feeds have no country, remote flag or last-updated date, so those fields are null.
- Each run is a snapshot of open roles. Closed roles are not kept; save runs if you need history.
- Descriptions are cut at 5,000 characters.

### FAQ

**Where does the data come from?**
From each company's public Personio job feed, the same list of open roles the company shows on its careers page.

**Do I need a Personio account?**
No. The feeds are public and need no login.

**Does it include recruiter or applicant data?**
No. Only the public job ad. E-mail addresses and phone numbers in descriptions are removed.

**How do I find a company's board name?**
Open the company's careers page. If its address is `<name>.jobs.personio.de` or `.com`, use `<name>` or the full URL.

**What happens if a board does not exist?**
The log says that board was not found, the other companies still run, and nothing is charged for the missing one.

**Can I get jobs in English?**
The Actor asks the feed for English. Companies that write their ads only in German still return German titles and text.

**Do you have other ATS platforms?**
Yes: [Greenhouse Jobs](https://apify.com/mrslm_data/greenhouse-jobs), [Lever Jobs](https://apify.com/mrslm_data/lever-jobs) and the multi-platform [ATS Jobs](https://apify.com/mrslm_data/ats-jobs) scraper.

**Does it work with Make, Zapier or n8n?**
Yes. All three have an Apify app. Use "Run Actor" (or run a saved task), then "Get dataset items" to pass the rows on to Google Sheets, Airtable, a CRM or Slack.

**Can AI agents use it?**
Yes, through Apify's MCP server at `https://mcp.apify.com/?tools=mrslm_data/personio-jobs`, as shown above.

### Is it legal?

This Actor only reads pages and feeds that the website itself publishes to every visitor without a login. It obeys each site's robots.txt, keeps a polite request rate, and never tries to get past logins, paywalls, captchas or bot checks. It does not collect personal data such as emails, phone numbers or names of private individuals. You are responsible for how you use the data: follow the source site's terms, copyright and the privacy laws that apply to you (GDPR, CCPA, etc.). This tool is independent and not affiliated with or endorsed by the platforms it reads.

# Actor input Schema

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

Personio board URLs or names. One per line.

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

Keep jobs whose title contains any of these words (e.g. engineer, sales). Empty = all jobs.

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

Drop jobs whose title contains any of these words (e.g. intern).

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

Keep jobs whose location or country contains any of these (e.g. London, Germany, remote).

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

Keep jobs whose department or team contains any of these (e.g. engineering).

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

Keep only jobs marked remote.

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

Keep only jobs posted in the last N days. 0 = any date.

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

Add the full job description as plain text (up to 5,000 characters). Makes rows larger.

## `newestFirst` (type: `boolean`):

Sort each company's jobs by posting date, newest first (so per-company limits keep the newest).

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

0 = all open jobs.

## `maxResults` (type: `integer`):

Safety cap on saved jobs across the whole run. 0 = no cap. Your max cost per run is always respected.

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

Usually not needed.

## Actor input object example

```json
{
  "companies": [
    "tado",
    "https://1komma5grad.jobs.personio.de"
  ],
  "remoteOnly": false,
  "postedWithinDays": 0,
  "includeDescription": false,
  "newestFirst": true,
  "maxJobsPerCompany": 20,
  "maxResults": 0,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

All jobs as a dataset (JSON, CSV, Excel and more via the API or Console).

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

Jobs per board, boards that failed, and the stop reason.

# 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": [
        "tado",
        "https://1komma5grad.jobs.personio.de"
    ],
    "maxJobsPerCompany": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("mrslm_data/personio-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": [
        "tado",
        "https://1komma5grad.jobs.personio.de",
    ],
    "maxJobsPerCompany": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("mrslm_data/personio-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": [
    "tado",
    "https://1komma5grad.jobs.personio.de"
  ],
  "maxJobsPerCompany": 20
}' |
apify call mrslm_data/personio-jobs --silent --output-dataset

```

## MCP server setup

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