# Personio Jobs Scraper (`anyxsolutions/personio-jobs-scraper`) Actor

Scrape open jobs from any company's Personio careers site: titles, departments, offices, employment type, seniority, schedule, experience, keywords, pay details and full descriptions in your language. Paste a careers site URL, get clean JSON. No login, no API key, no proxy.

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

## Pricing

from $2.50 / 1,000 results

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

![banner](https://anyx.solutions/banners/personio-jobs-scraper.png)

## Personio Jobs Scraper

**Every open job on any company's Personio careers site — with full descriptions, offices, employment details and pay sections — as clean JSON.**

Personio is the HR platform behind the careers sites of tens of thousands of small and mid-sized
companies across Germany and Europe, from XITASO and Cloover to ottonova and 1KOMMA5°. This Actor
reads any of those careers sites and returns every open job: title, hiring company, department,
recruiting category, main office and every additional office, employment type, seniority, schedule,
years of experience, occupation, keywords, the creation date, and the full description as HTML,
plain text and named sections, plus the pay section whenever the company publishes one. Pick the
language you want the descriptions in; jobs the company has not translated come in the site's own
language. Paste a careers site URL; there is no login, no API key, and no proxy to configure. Built
for job aggregators, recruiting and talent-intelligence teams, salary benchmarking, and
hiring-signal tracking across the European SME market.

### ⚡ Quick start

```json
{
  "startUrls": [{ "url": "https://xitaso.jobs.personio.de/" }],
  "language": "en",
  "maxItems": 5
}
```

### 🧩 Input

| Field                | Type    | Required | Default                      | Description                                                                                                                                                  |
| -------------------- | ------- | -------- | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `startUrls`          | array   | yes      | —                            | Personio careers site URLs (`https://<company>.jobs.personio.de/` or `.com`) or single job URLs from one (`…/job/<id>`).                                     |
| `language`           | string  | no       | `en`                         | Description language: `en`, `de`, `fr`, `es`, `it`, `nl`, `pt`, `pl`, `cs`, `sv`, `da`, `fi` or `no`. Untranslated jobs come in the site's default language. |
| `maxItems`           | integer | no       | —                            | Maximum number of jobs per start URL. The input form starts at 5; leave it empty to get the whole careers site.                                              |
| `proxyConfiguration` | object  | no       | `{ "useApifyProxy": false }` | Proxy settings. Personio careers sites need no proxy.                                                                                                        |

**Finding a company's careers site.** Every Personio customer has an address of the form
`https://<company>.jobs.personio.de/` (the same site also answers on `.jobs.personio.com`). Many
companies link to it from the "Careers" or "Jobs" page of their own website; open any job there and
look for `jobs.personio` in the address bar or the apply link. Custom domains are not supported as
input, so use the `<company>.jobs.personio.de` address. A `?language=de` (or any other language) on
a start URL overrides the **Description language** for that URL.

### 📤 Output

Each open job is one dataset row.

#### Fields

| Field                | Type   | Description                                                                                                      |
| -------------------- | ------ | ---------------------------------------------------------------------------------------------------------------- |
| `id`                 | number | Personio job ID.                                                                                                 |
| `title`              | string | Job title, in the description's language.                                                                        |
| `company`            | string | Hiring company: the legal entity the job is posted for, or the careers site's name when none is given.           |
| `companySlug`        | string | The company's subdomain on jobs.personio.de.                                                                     |
| `careersSiteUrl`     | string | The company's careers site on Personio.                                                                          |
| `url`                | string | The job's public page.                                                                                           |
| `applyUrl`           | string | The job's application form.                                                                                      |
| `department`         | string | Department name.                                                                                                 |
| `recruitingCategory` | string | The company's own recruiting category for the job.                                                               |
| `office`             | string | Main office.                                                                                                     |
| `additionalOffices`  | array  | Other offices the job is open in.                                                                                |
| `employmentType`     | string | Employment type, such as `permanent`, `fixed_term`, `intern`, `working_student` or `trainee`.                    |
| `seniority`          | string | Seniority, such as `entry-level`, `experienced`, `executive` or `student`.                                       |
| `schedule`           | string | Schedule: `full-time`, `part-time` or `full-or-part-time`.                                                       |
| `yearsOfExperience`  | string | Years of experience asked for, such as `lt-1`, `1-2`, `2-5`, `5-7`, `7-10` or `10-15`.                           |
| `occupation`         | string | Occupation, such as `software_and_web_development`.                                                              |
| `occupationCategory` | string | Occupation category, such as `it_software`.                                                                      |
| `keywords`           | array  | Keywords the company added to the job.                                                                           |
| `createdAt`          | string | When the job was created (ISO 8601, UTC).                                                                        |
| `salaryText`         | string | Plain text of the description section about pay ("Salary information", "Gehalt", …), when the job has one.       |
| `descriptionHtml`    | string | Full job description as HTML, each section under an `<h2>` heading.                                              |
| `descriptionText`    | string | Full job description as plain text, each section under its heading.                                              |
| `sections`           | array  | The description as the company wrote it: one `{ heading, html, text }` object per section, such as "Your tasks". |

<details><summary>Example output</summary>

```json
{
  "id": 1139164,
  "title": "Senior Software Engineer (all genders) for our locations in Germany",
  "company": "XITASO GmbH IT & Software Solutions",
  "companySlug": "xitaso",
  "careersSiteUrl": "https://xitaso.jobs.personio.de/",
  "url": "https://xitaso.jobs.personio.de/job/1139164?language=en",
  "applyUrl": "https://xitaso.jobs.personio.de/job/1139164/apply?language=en",
  "department": "Delivery",
  "recruitingCategory": "1 - Delivery",
  "office": "Berlin",
  "additionalOffices": [
    "Krumbach",
    "Ingolstadt",
    "Augsburg",
    "Erlangen",
    "Leipzig",
    "Münster",
    "München",
    "Karlsruhe"
  ],
  "employmentType": "permanent",
  "seniority": "experienced",
  "schedule": "full-or-part-time",
  "yearsOfExperience": null,
  "occupation": "software_and_web_development",
  "occupationCategory": "it_software",
  "keywords": [],
  "createdAt": "2023-06-01T13:05:30Z",
  "salaryText": "Within our standardized and transparent salary framework, the gross annual salary of the position ranges from EUR 58,000 – 70,000 and is based on various factors such as qualifications and experience.",
  "descriptionHtml": "<h2>Become part of our mission</h2><span style=\"font-family: …",
  "descriptionText": "Become part of our mission\nWith high-end software engineerin …",
  "sections": [
    {
      "heading": "Become part of our mission",
      "html": "<span style=\"font-family:Arial, Helvetica, sans-serif;font-s …",
      "text": "With high-end software engineering in the age of AI, we insp …"
    },
    {
      "heading": "These tasks interest you",
      "html": "<ul><li style=\"font-family:Arial, Helvetica, sans-serif;font …",
      "text": "You will take responsibility within an interdisciplinary tea …"
    }
  ]
}
```

</details>

### 💡 Use cases

- **Job aggregation** — feed job boards, newsletters and niche listing sites with fresh postings from German and European SMEs.
- **Salary benchmarking** — collect the pay ranges companies publish under Germany's and the EU's pay-transparency push.
- **Hiring signals** — watch which departments and offices a company is growing, for sales prospecting or investment research.
- **Recruiting intelligence** — monitor competitors' open roles, seniority mix and how long they stay open.

### ❓ FAQ

- **Do I need an API key or a Personio account?** No. The Actor reads the same public careers sites any candidate sees.
- **Do I need a proxy?** No. It runs without one by default.
- **Why is a description in German when I chose English?** Companies translate jobs one by one. A job without an English version comes in the careers site's default language (or, failing that, in English or German), so you get a description whenever the company wrote one. The job's `url` opens it in the same language.
- **Why is `salaryText` empty?** Personio has no separate salary field, so pay only appears when the company writes it into a section of the description, such as "Salary information" or "Gehalt". `salaryText` is `null` when no such section exists; the full description is still in `descriptionText`.
- **The company's careers page is on its own domain.** Custom domains are not supported as input. Use the company's `https://<company>.jobs.personio.de/` address; the jobs are the same.
- **Can I scrape many companies at once?** Yes. Add one careers site URL per company to `startUrls`; `maxItems` applies to each one separately.
- **Why did a run return no jobs?** The company name in the URL may be wrong, the company may have no open jobs, or it may have switched its public job listing off; the log says when a careers site is not found. Open an issue if something looks off.

### 🔗 More scrapers by Anyx

- [Greenhouse Jobs Scraper](https://apify.com/anyxsolutions/greenhouse-jobs-scraper)
- [Indeed Scraper](https://apify.com/anyxsolutions/indeed-scraper)
- [Glassdoor Scraper](https://apify.com/anyxsolutions/glassdoor-scraper)
- [Jobs.ch Scraper](https://apify.com/anyxsolutions/jobs-ch-scraper)

### 🤝 Anyx Solutions

We build custom scrapers and data-extraction pipelines.

- Email: tantosthor@gmail.com

### 🖼 Image credit

Image credit: [personio.com](https://www.personio.com/)

# Actor input Schema

## `startUrls` (type: `array`):

Personio careers site URLs on a company subdomain, such as https://xitaso.jobs.personio.de/ or https://cloover.jobs.personio.com/. Single job URLs (…/job/<id>) work too. Custom domains are not supported; use the company's <company>.jobs.personio.de address.

## `language` (type: `string`):

Language to read job titles and descriptions in. Jobs the company has not translated into it come in the careers site's default language instead. A ?language= on a start URL overrides this for that URL.

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

Maximum number of jobs to return per start URL. Leave empty to get every open job on the careers site.

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

Proxy settings. Personio careers sites need no proxy.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://xitaso.jobs.personio.de/"
    }
  ],
  "language": "en",
  "maxItems": 5,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `overview` (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 = {
    "startUrls": [
        {
            "url": "https://xitaso.jobs.personio.de/"
        }
    ],
    "maxItems": 5
};

// Run the Actor and wait for it to finish
const run = await client.actor("anyxsolutions/personio-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 = {
    "startUrls": [{ "url": "https://xitaso.jobs.personio.de/" }],
    "maxItems": 5,
}

# Run the Actor and wait for it to finish
run = client.actor("anyxsolutions/personio-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 '{
  "startUrls": [
    {
      "url": "https://xitaso.jobs.personio.de/"
    }
  ],
  "maxItems": 5
}' |
apify call anyxsolutions/personio-jobs-scraper --silent --output-dataset

```

## MCP server setup

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