Personio Job Board Scraper — DACH SME Hiring Signals avatar

Personio Job Board Scraper — DACH SME Hiring Signals

Pricing

$2.00 / 1,000 job postings

Go to Apify Store
Personio Job Board Scraper — DACH SME Hiring Signals

Personio Job Board Scraper — DACH SME Hiring Signals

Search open jobs across 2,500+ German-speaking SMEs hiring on Personio by job title, city, department and date — no slugs needed. Or pass companies. Live data: full description, employer name and address, department, seniority, schedule, apply URL.

Pricing

$2.00 / 1,000 job postings

Rating

0.0

(0)

Developer

Berkan Kaplan

Berkan Kaplan

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

9 hours ago

Last modified

Share

Personio Job Board Scraper — DACH SME Hiring Signals 🚀

foXLabs hiring series: Greenhouse · Lever · Ashby · Workday · SmartRecruiters · Teamtailor · Workable · BambooHR · All platforms in one search →

🎉 Search open jobs across 2,500+ German-speaking SMEs that hire on Personio — by job title, city, department and posting date — without knowing a single company slug. Or point it at the companies you track. No login, no API key, one row per open role with Personio's structured classification. Built for sales & competitive-intelligence teams prospecting the DACH Mittelstand, recruiters, and job-data pipelines.

🔍 What is the Personio Job Board Scraper — and when should you use it?

Personio is the HR system of the German-speaking Mittelstand, but it has no job search across companies: every company publishes its jobs on its own *.jobs.personio.de/.com site. This actor does the searching for you. It carries an index of 2,558 Personio job sites (discovered from the public Common Crawl web archive; 2,557 had a live job feed in our last full check, October 2026), reads their feeds live during your run, and keeps the jobs whose title matches your keywords and filters — with department, seniority, schedule, years of experience, occupation category, sub-company, office and posted date.

Already know your target companies? Add their slug (deskbird) or careers URL and get every open role.

Use it when you need: DACH SMEs hiring for a function right now (data that US-centric job feeds miss); a feed of new roles for a job title; every open role at a target company; or a read on a competitor's hiring plans — from the XML job feed every Personio careers site publishes at /xml, no key.

Use something else when: the companies you care about don't run on Personio. Every actor in the foXLabs hiring series emits the same core schema, so their outputs concatenate into one hiring-intent table: Greenhouse · Lever · Ashby · Workday · SmartRecruiters · Teamtailor · Workable · BambooHR.

Need one search across every platform? Job Postings Database keeps a daily-refreshed index of 440,000+ open roles from ~33,000 Greenhouse, Lever, Ashby, SmartRecruiters, BambooHR, Personio and Teamtailor boards and answers a single query across all of them in seconds — every delivered role is re-checked live on its own board before it reaches you.

🤖 Use with AI agents

Already on the Apify MCP server? Ask for this Actor by name: foxlabs/personio-job-board-scraper.

Your agent can pay for its own runs. This Actor is pay-per-event with agentic payments, so an agent can discover it, run it and settle the bill over x402 (USDC on Base) or Skyfire — no Apify account or API token of its own. Billing is per delivered role, never for errors.

Otherwise paste this into Claude, ChatGPT, Cursor or any MCP-enabled assistant:

I want to find open jobs at German-speaking SMEs that hire on Personio using the Apify Actor `foxlabs/personio-job-board-scraper`.
Input: `searchQueries` is a list of job-title keywords (every word must appear in the title; "*" = every job;
German titles work too, e.g. "Vertrieb", "Entwickler"). Optional filters: `location` (Personio names the office city,
so use a city: "Berlin", "München", "Wien"), `department` (e.g. "Sales"), `remoteOnly`, `employmentType`,
`postedWithinDays` ("1" | "7" | "30" | "90"). `queries` optionally adds companies (slugs like "deskbird").
`maxResultsPerQuery` caps jobs per keyword or company (default 200).
Start with: {"searchQueries":["sales"],"location":"Berlin","maxResultsPerQuery":50}
Ask me which roles and cities I care about, run the Actor, then summarise the hiring companies as a table.

The machine-readable API, MCP config and OpenAPI definition live at apify.com/foxlabs/personio-job-board-scraper.md.

📋 Overview

Everything you need to turn Personio's scattered job sites into one searchable DACH hiring dataset — role details, classification and hiring signals — in one actor, with no login, cookies or API key.

Why teams pick this actor:

  • 🔎 No slugs needed — search 2,500+ live Personio job sites by job title; the companies come to you.
  • 🇩🇪 DACH SME coverage — the mid-market segment US-centric ATS feeds miss entirely.
  • ⚡ Live, not a stale dump — every job feed is read during your run.
  • 🧩 Structured classification — department, seniority, schedule, years of experience, occupation category, sub-company.
  • 🔗 Same schema across the series — join with the sibling ATS actors on companyName / companyBoard.
  • 💰 Pay only for results — per-role pricing; a job found twice is charged once; empty searches and failed lookups are never billed.
  • 🤖 Agent-ready — MCP + x402 agentic payments.
  • 📝 Full job description + employer details — every delivered job also reads its own page: the full advert text, the employer's registered name and logo, and the street address of the office.

🎬 Quick Start

curl -X POST "https://api.apify.com/v2/acts/foxlabs~personio-job-board-scraper/runs?token=YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"searchQueries":["sales"],"location":"Berlin","maxResultsPerQuery":50}'

🚀 Getting Started (3 steps)

  1. Type your job titles — one per line (sales, Entwickler, controller), or * for every job.
  2. Narrow it down (optional) — city, department, remote only, employment type, posting date; or add specific companies in queries.
  3. Run and export — a clean dataset as JSON, CSV or Excel.

📥 Input

{
"searchQueries": ["sales", "vertrieb"],
"location": "Berlin",
"postedWithinDays": "30",
"queries": ["deskbird"],
"maxResultsPerQuery": 200
}
FieldTypeDescription
searchQueriesarrayJob-title keywords, one per line. Every word must appear in the title (engineer also matches engineering). * = every job matching the filters.
locationstringText the office must contain. Personio usually names only the city (Berlin, Wien, Zürich), so filter by city rather than country. German and English spellings both work (München / Munich, Wien / Vienna).
departmentstringText the department must contain, e.g. Sales, Engineering.
remoteOnlybooleanKeep only remote jobs.
employmentTypestringText the employment type must contain, e.g. permanent, intern, working student.
postedWithinDaysstringany, 1, 7, 30 or 90.
queriesarrayOptional Personio slugs (deskbird) or careers URLs. Search filters do not apply to them.
maxResultsPerQueryintegerCaps jobs per keyword or company (default 200).
maxConcurrencyintegerHow many companies from queries to fetch at once.
scanConcurrencyintegerHow many job feeds a keyword search reads at once (default 8).
includeRawbooleanAttach the source's untouched record under raw (companies in queries).
requestDelayMsintegerPoliteness delay between requests.
proxyConfigurationobjectOptional.

📤 Output

One row per open role, saved to the dataset.

FieldDescription
companyNameEmployer name as published on the job page (e.g. Memodo GmbH; falls back to the slug, title-cased)
companyBoardPersonio slug — join key
jobIdPosition ID
jobTitleRole title
department / recruitingCategoryDepartment / Personio recruiting category
locationOffice
isRemoteRemote flag
employmentType / scheduleEmployment type / full-time or part-time
seniority / yearsOfExperienceSeniority level / experience band where published
occupationCategoryOccupation category
subcompanyLegal entity where set
postedAtPosted date (ISO, from the feed)
applyUrl / sourceUrlApply link / public posting URL
descriptionFull job advert as plain text (from the job page)
companyLogoUrlEmployer logo
streetAddress / postalCode / city / region / countryNameAddress of the office the job is attached to, where the job page publishes it. For remote roles Personio usually gives the company's own office here — use location and isRemote for where the work happens.
allLocationsEvery office listed for the job, City, Country

Every row also carries query, scrapedAt, and — when a search finds nothing or a lookup fails — an error.

{
"query": "vertrieb",
"companyName": "ELAFLEX HIBY GmbH & Co. KG",
"companyBoard": "elaflex-group",
"jobTitle": "Vertriebsmitarbeiter (m/w/d) International",
"department": "Vertrieb SAT/ERV Export",
"location": "ELAFLEX HIBY GmbH & Co. KG Hamburg",
"employmentType": "permanent",
"schedule": "full-time",
"seniority": "experienced",
"yearsOfExperience": "2-5",
"occupationCategory": "sales_and_business_development",
"postedAt": "2026-09-10",
"applyUrl": "https://elaflex-group.jobs.personio.com/job/2792144?display=en",
"description": "Unsere Stelle: Gestalten Sie mit uns die Zukunft unseres internationalen Vertriebs! Sie haben Freude am internationalen Kundenkontakt, beraten gerne z…",
"companyLogoUrl": "https://assets.cdn.personio.de/logos/281382/social/c1d4f1b7e40f72d5e423bd7f24064e88.jpg",
"streetAddress": "Schnackenburgallee 121",
"postalCode": "22525",
"city": "Hamburg",
"countryName": "Germany"
}

💼 Use cases

1. DACH SME prospecting Input: searchQueries: ["sales", "vertrieb"], a city, weekly. Output: Mittelstand companies hiring sales. Use: find mid-market hiring data unavailable elsewhere.

2. Hiring-intent scoring Input: searchQueries: ["data"], postedWithinDays: "30". Output: companies opening data roles, with seniority. Use: a company opening its first data roles is starting a data project.

3. Competitor headcount tracking Input: the competitor's slug in queries, weekly. Output: the full role list over time. Use: spot expansion and hiring freezes early.

4. Recruiter sourcing Input: a title and a city. Output: roles + apply links across DACH employers. Use: source in minutes.

🔗 Integration

JavaScript / Node.js

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('foxlabs/personio-job-board-scraper').call({ searchQueries: ['sales'], location: 'Berlin', maxResultsPerQuery: 50 });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items[0]);

Python

from apify_client import ApifyClient
client = ApifyClient('YOUR_TOKEN')
run = client.actor('foxlabs/personio-job-board-scraper').call(run_input={'searchQueries': ['sales'], 'location': 'Berlin', 'maxResultsPerQuery': 50})
for item in client.dataset(run.default_dataset_id).iterate_items():
print(item)

Written for apify-client 3 for Python, where call() returns a run object; with version 1 or 2 it returns a dict — write run['defaultDatasetId'] there.

Automation (n8n / Zapier / Make): schedule or webhook → HTTP request to the actor API → handle the JSON dataset → push to a sheet, CRM or dashboard.

📊 Pricing

Pay-per-event:

EventPriceWhen
Job posting$0.002per delivered role
Keyword search$0.02from October 29, 2026: once per search line that delivers at least one job (a search reads every feed in the index)

A job found twice is charged once; a search line that finds nothing, jobs that don't match your filters, and failed lookups are never billed. Companies you list in queries pay only per role. Example: one keyword returning 50 jobs costs $0.02 + 50 × $0.002 = $0.12. View current pricing.

❓ FAQ

Do I need an account, login or API key? No. This reads the XML job feed every Personio careers site publishes at /xml — no key.

Do I need to know company slugs? No. Search by job title; every row tells you the company and its slug.

Which companies does the search cover? Personio job sites found in the public Common Crawl web archive — 2,558 sites, of which 2,557 had a live feed in our last full check (October 2026). A company that isn't in the index can still be read by adding its slug to queries.

How long does a search take? A broad search stops as soon as it has your cap; a search for a rare title reads every feed (about 8 minutes).

Can I filter by country? Personio usually names only the office city, so filter by city (Berlin, Hamburg, Wien, Zürich). German and English spellings of the same city both match.

How current is the data? Every run reads the feeds live, so results are as fresh as the careers sites themselves.

Can I export to CSV / Excel / JSON? Yes — directly from the Apify dataset.

What happens on a bad input or a search with no matches? You get a row with your query and an error — nothing is silently dropped, and you're not charged.

Is scraping this legal? It reads a public job feed the company publishes for syndication — job data, not personal data. See below.

🐛 Troubleshooting

  • Fewer rows than expected — title matching is strict (every word must appear in the title); try German and English titles, fewer keywords, or loosen the filters.
  • "No Personio job feed found …" — pass a bare slug (the actor tries both .de and .com) or the exact careers URL.
  • Personio rate-limited this IP — lower maxConcurrency / scanConcurrency, raise the delay, or turn the proxy on.
  • A few rows without description — the job's own page could not be read (usually throttling); the row is still delivered with the feed fields, and the log counts how many pages were missed. Turn the proxy on or raise requestDelayMs.

⚠️ Trademark

Independent, third-party tool. Not affiliated with, endorsed by or connected to Personio SE. "Personio" and related marks belong to their owners and are used here for identification only.

This actor reads only publicly published job-posting data — no private or personal user data. Your results can still contain personal data (e.g. a name in a description); personal data is protected by the GDPR and similar laws, so only process it with a legitimate basis. See Apify's blog post on the legality of web scraping.

🤝 Support & contact

Changelog

0.5.10 — 2026-10-14 — full job description, employer name and office address

  • Every delivered job now reads its own Personio page (the public /xml feed has an empty <jobDescriptions/>) and adds description (full advert, plain text), companyLogoUrl, and the office address: streetAddress, postalCode, city, region, countryName, plus allLocations (every office listed). Measured before building: 5 of 5 job pages on two companies carried the advert (2,440–4,403 characters).
  • companyName is now the employer name the job page publishes (Memodo GmbH instead of the slug-derived Memodo-Gmbh); it falls back to the title-cased slug when the page cannot be read. companyBoard (the join key) is unchanged.
  • Keyword search reads the page only for jobs that passed the filters; companies named in queries read it for each job delivered. A page that cannot be read (throttling, timeout) is retried twice and the job is still delivered without those fields — the new columns are then null, and the log counts the misses.
  • Careers sites that leave the JobPosting block out of the page (44 of 200 jobs in beta search run vIz3dGgBbOr0sDu27) are read from the page's description sections instead: description and companyLogoUrl are filled, the address and the employer name stay as the feed gives them.
  • For remote roles Personio usually puts the company's own office in the address; location and isRemote remain the place of work.

Index refresh — 2026-10-14

  • Company index refreshed from the latest public web-archive snapshots: no new careers sites found, 1,260 that no longer exist removed (checked live on 2026-10-14); the index now holds 2,558 careers sites, 2,557 of them answering and 1 temporarily unreachable (kept).

0.5.0 — 2026-10-14 — keyword search across Personio (no slug needed)

  • New: searchQueries — search open jobs by title across 3,818 indexed Personio job sites (2,557 live), with location, department, remoteOnly, employmentType and postedWithinDays filters. Feeds are read live.
  • A job is delivered and charged once per run, whether a search or a company in queries finds it.
  • Restart-safe: a run that Apify moves to another server resumes without writing or charging a row twice.
  • queries (slugs / careers URLs) works exactly as before; it is now optional.

0.4.6 — 2026-10-08 — README: the Python example works with the current Apify client

  • The README's Python example read the results with run['defaultDatasetId']. With the current Apify client for Python (version 3) call() returns a run object, not a dict, and that line raised TypeError: 'Run' object is not subscriptable — after the run itself had finished. The example now reads run.default_dataset_id; with client version 1 or 2, keep run['defaultDatasetId']. Checked with apify-client 3.2.1, 2.5.1 and 1.12.2.
  • No code, output field or pricing change.

0.4.2 — 2026-09-06

  • Added recruitingCategory. Personio's human-readable role category (100% of roles), distinct from the coded occupationCategory slug.

0.4.1 — 2026-09-06

  • Fixed includeRaw. Turning on includeRaw previously failed the whole run with "Converting circular structure to JSON" — the actor attached the raw XML DOM node (which holds circular parent/child references). Each position is now parsed into a plain field object, so includeRaw returns a clean, serialisable record and the default (includeRaw: false) run is unchanged.

0.4 — 2026-09-06

  • Enabled AI-agent payments (x402). Standby mode was removed because Apify's x402 agentic-payment eligibility requires it off; the actor now runs as a standard pay-per-event run that agents can discover, run and pay for over x402 (USDC on Base) or Skyfire without an Apify account. Batch and MCP usage are unchanged.

0.3 — 2026-09-05

  • Output schema linked in actor.json (output field) so the Store shows the Output tab.
  • Standby mode (real-time API). The actor now also runs as an always-on HTTP API: GET /search?query=<query>&maxResults=N returns open roles as JSON, billed per role (pay-per-event). Batch runs are unchanged. Endpoint and OpenAPI schema appear in the Standby tab.

0.2 — 2026-09-05

  • Added posted date. postedAt is now populated from the feed's createdAt.
  • Removed columns the feed never provides: team, city, countryName, salary, updatedAt. Every emitted column now carries real data.
  • Descriptions: Personio's public /xml feed ships an empty <jobDescriptions/> (the free-text advert is only on each job's HTML page). Rather than risk Personio's aggressive IP rate-limiting with a request per job, this actor returns the rich structured classification (department, seniority, schedule, yearsOfExperience, occupationCategory, subcompany, employmentType, location) instead of a description.

0.1

  • Initial release: positions from any Personio XML feed.