All platforms

Personio Jobs API

Query jobs from Personio boards through one API — and how the Personio XML feed, its structured keywords and its empty descriptions behave.

?source=personiosource reference

Personio is an HR platform built for European small and mid-sized companies, and it's dominant in the DACH region. Boards live at <slug>.jobs.personio.com, and the useful endpoint is an XML feed rather than a JSON API. It's an unusually rich feed — Personio publishes seniority, schedule, years of experience and a genuine keyword list as structured tags — and it has one failure mode that looks exactly like success.

Try it#

BASH
curl -G https://api.hyperjobs.io/v1/jobs \
  -H "Authorization: Bearer $HYPERJOBS_KEY" \
  --data-urlencode "source=personio" \
  --data-urlencode "country=Germany" \
  --data-urlencode "limit=2"
Illustrative — the shape, not a live response
{
  "data": [
    {
      "id": "personio:1284517",
      "title": "Machine Learning Engineer (m/w/d)",
      "company": {
        "slug": "nordstern-tech",
        "name": "Nordstern Tech",
        "domain": "nordstern.de",
        "website": "https://nordstern.de",
        "logo": "https://media.licdn.com/dms/image/…",
        "industry": "Software Development",
        "employee_count": 180
      },
      "location": {
        "remote": true,
        "countries": ["Germany"],
        "cities": ["Hamburg"],
        "regions": null,
        "raw": ["Hamburg, remote"]
      },
      "employment_type": ["FULL_TIME"],
      "work_arrangement": "Remote",
      "seniority": "Mid-Senior level",
      "experience_level": "2-5",
      "job_language": "de",
      "salary": {
        "min": 65000,
        "max": 85000,
        "currency": "EUR",
        "unit": "YEAR",
        "annual_min": 65000,
        "annual_max": 85000,
        "summary": null
      },
      "skills": ["Machine Learning", "LLM", "Python"],
      "taxonomies": ["Software"],
      "apply_url": "https://nordstern.jobs.personio.com/job/1284517",
      "url": "https://nordstern.jobs.personio.com/job/1284517",
      "source": "personio",
      "posted_at": "2026-07-08T10:22:00.000Z",
      "expires_at": null,
      "updated_at": "2026-07-13T19:44:02.000Z"
    }
  ],
  "total": 2,
  "limit": 2,
  "offset": 0
}

How Personio boards actually work#

One GET, no auth, no pagination, the whole board as XML:

BASH
curl "https://<slug>.jobs.personio.com/xml"

<slug> is the subdomain. The .de host serves byte-identical content, so <slug>.jobs.personio.de/xml is the same feed and not a separate board.

There's also a /search.json, and it's worse: the XML feed carries createdAt, seniority, schedule, employmentType and yearsOfExperience that search.json simply doesn't have — including any posting date at all. The <id> is identical between the two, so switching costs nothing.

The document is a list of <position> blocks. Per position: <id>, <name>, <office> (repeatable), <department>, <recruitingCategory>, <employmentType>, <schedule>, <seniority>, <yearsOfExperience>, <keywords>, <salaryInformation> and a set of <jobDescription> sections.

The description is titled sections#

<jobDescription> repeats, each with a <name> and a <value>. The name is a heading — Your tasks, Ihr Profil, Benefits — and the value is HTML. You reassemble them in order, and the section names are how you find perks without an AI pass: the section named some variant of Benefits / Vorteile / Wir bieten / We offer / Avantages is the benefits list.

<keywords> is real structured skills#

A comma-separated tag list — AI Engineer,Machine Learning,LLM,Python — verified at 66% fill live. That's a dedicated skills field, which almost no ATS in this pool has. Recruitee and Pinpoint make you mine <li> bullets and hope; Personio just tells you.

<salaryInformation> is plain units#

min, max, currencyCode and type, in whole units — no cents divide like Greenhouse. Fill is about 13% overall, but up to 88% on a single company's board. That spread is worth internalising: pay transparency here is a per-employer decision, not a platform average, so a sample from one board tells you nothing about the source.

What bites you#

The feed is not the complete source it looks like

Some boards emit <jobDescriptions></jobDescriptions> empty for part of their positions — one live board had 14 of 15 blank — while the posting page carries the full body in its JobPosting JSON-LD. The feed returns 200 with a well-formed document and a hole in it.

Most boards fill every one and this never fires. That's what makes it dangerous: you'll test on a healthy board, ship, and lose most of the descriptions on a board you never looked at. We detect the blanks and recover only those rows from the posting page, so a healthy board pays nothing.

Personio has no workplace-type field. No remote flag, no arrangement enum. What it has is <office> values that are frequently the arrangement itselfRemote, Hybrid, Remote Germany, Hamburg, remote. The office field is doing double duty. Reading an arrangement out of it recovers about 24% of postings including hybrid, and the city still parses out of the same string. It's a workaround for a missing field, and it only works because employers keep typing the arrangement where the office goes.

<keywords> leaks raw HTML entities. Sales &amp; acquisition, &quot;Field Service&quot;, R&#38;D. Store them undecoded and your skill tags contain literal &amp;.

<seniority> is a clean English enum even on German boards. experienced, entry-level, student — it doesn't get localised, which is convenient and surprising enough to be worth stating.

A position can have several <office> tags. Pass them as an array or a multi-site job collapses to one city.

Or use ours#

Personio's feed is one request and genuinely pleasant. The reasons to take ours are the hole and the reach.

The hole: descriptions that are silently blank on some boards, recovered from the posting page only where the feed left them empty. The reach: we track 2,463 Personio boards, and Personio publishes no index of its customers — assembling that list is the actual work, not the parsing.

Plus the usual: <keywords> decoded into clean skill tags, the arrangement recovered from <office>, sections reassembled into one body with benefits extracted, EUR bands normalised so they compare against USD ones, dedup, expiry by board diffing, and the company join.

Personio is in the fast refresh tier, attempted every cycle — see coverage.

Quickstart · Pricing

Every source above normalises onto the same schema, with the hiring company already joined onto each posting.

Get an API key