Personio Vacancy Watch | Jobs Scraper & Change Monitor avatar

Personio Vacancy Watch | Jobs Scraper & Change Monitor

Pricing

from $18.00 / 1,000 complete board checks

Go to Apify Store
Personio Vacancy Watch | Jobs Scraper & Change Monitor

Personio Vacancy Watch | Jobs Scraper & Change Monitor

Follow new, changed and removed jobs on public Personio XML boards. Save job rows, keep state between runs, and export JSON, CSV or Excel.

Pricing

from $18.00 / 1,000 complete board checks

Rating

0.0

(0)

Developer

Cliqto Media

Cliqto Media

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

8 days ago

Last modified

Categories

Share

Watch known Personio boards for new, changed and removed jobs. This Personio jobs scraper reads public XML feeds and saves job rows or change events. It keeps the last complete board state when a check fails, so a failed check does not report every old job as removed.

Start with one board and snapshot mode. No Personio login is needed. Apify access is needed to run the Actor. A valid empty board or a delta with no changes can return zero rows. Platform usage can still have a cost; see Pricing and cost examples.

Job cards with new, changed and removed symbols in an observation loop

Table of contents

What this Actor does

Add known board names or exact HTTPS URLs on *.jobs.personio.de or *.jobs.personio.com. The Actor reads /xml?language=.... It does not search for companies, read applicant data, or open each job detail page.

Mode or eventWhat you receive
snapshotCurrent accepted jobs, each marked baseline. Every complete snapshot also updates saved state.
First delta for a board and languageCurrent jobs marked baseline. There is no earlier complete state to compare.
Later delta: newA job ID that was absent from the last complete snapshot.
Later delta: changedA saved job whose normalized core content changed. changedFields lists the field names.
Later delta: removedA saved job ID absent from a new complete feed. A removal is an observation, not proof that a vacancy was filled.
Later delta: unchangedSame content; emitted only with includeUnchanged=true.

The monitor compares tenant|language inside the selected stateKey. Use different keys for independent monitors. The same tenant on .de and .com has the same state partition; use a separate stateKey if you need to compare these hosts independently.

What data you can get

Get job IDs, titles, office lists, department, employment type, seniority, schedule, creation date, descriptions, source salary values and public job URLs. Missing source values stay null or empty arrays. A language request can return no description if the employer has not supplied one in that language.

Existing consumers can read company, url, location, allLocations, postedAt, commitment and _scrapedAt. Their source meaning is listed below. Check these types and meanings before connecting an existing application. Full compatibility with every application is not promised.

team, country, workplaceType and updatedAt are null. The Actor does not infer a country or remote status from a title or office. Salary is returned only from published source salary content; it is not estimated from a role name.

Quick start

  1. Open the Actor you have access to in Apify Console, then open Input.
  2. Switch to JSON input and paste the minimal example below. Use a tenant from the company public Personio careers board, such as teamative. A job title or search word such as mediabuyer is not a verified tenant.
  3. Keep language as en and choose snapshot for this first check.
  4. Click Start. Wait for the run to finish.
  5. Open Output or the run's default Dataset. Check tenant, jobId, title and eventType.
  6. Open the default Key-value store and read RUN_SUMMARY. Check each board's status and complete.

If every board has a technical error, the run is Failed. Read RUN_SUMMARY in Storage; no new complete snapshot or removal events are saved. A real HTTP 404 is not_found and can finish without an exception. A redirect to the Personio home page is an error, not proof of an empty board.

In the captured example, teamative returned 18 jobs. Live jobs can change; 18 is an example count, not a required future result. For a repeated monitor, use delta and keep a stable stateKey. Start runs on the same key one at a time.

Full Input reference

The JSON object has 9 supported fields. Only boards is required. The fresh Input form offers teamative in snapshot mode, a 100-job cap and a separate PERSONIO_UI_DEMO_TEAMATIVE_EN state key. Repeated sample runs return current jobs without moving your normal monitor baseline. API calls still require boards; omitted optional fields use the runtime defaults below, including delta mode.

FieldInput labelTypeRequiredRuntime defaultUI prefillAllowed valuesEffect and recommendation
boardsPersonio board names or URLsarrayYesNo default; required. Start with one known board.["teamative"]1–50 non-empty stringsReads each board in order. Use a slug such as teamative, or its exact HTTPS Personio board URL. Slugs use .de.
languageJob text languagestringNoenNonede, en, es, fr, it, nl, pl, ptRequests this language from the XML feed. Start with en; try the source language if text is empty. It does not translate text.
modeSnapshot or changesstringNodeltasnapshotsnapshot, deltaSnapshot returns current jobs as baseline rows. Delta compares saved state. Use snapshot for one-off exports and delta for repeated checks.
maxJobsPerBoardJobs allowed per boardintegerNo5000100integer 1–5000If the board has more jobs, returns only this many with truncated status. It keeps old state and emits no removals. Choose a limit above your board size.
maxBodyBytesXML size limit (bytes)integerNo10485760Noneinteger 16384–10485760Rejects an XML response above this size. Keeps old state. A 1 MiB limit (1048576) worked for the example boards; raise it for larger descriptions.
boardTimeoutMsTime limit per board (ms)integerNo30000Noneinteger 1000–120000Limits the whole board fetch, including retries and waits. Keep 30000 first; a lower value can stop a slow board before all attempts.
maxAttemptsRequest attempts per boardintegerNo3Noneinteger 1–5Includes the first request. Retries selected HTTP or transport failures within the board time limit. Use 1 to avoid retries or keep 3 for normal checks.
stateKeyMonitor state keystringNoPERSONIO_MONITOR_STATEPERSONIO_UI_DEMO_TEAMATIVE_EN1–128 characters; pattern belowSelects a record in the named state store. Keep it stable for one monitor. Use a new key for an independent baseline.
includeUnchangedShow unchanged jobsbooleanNofalseNoneboolean true or falseAdds unchanged rows in delta mode. Keep false for a change feed. It has no effect in snapshot mode.

stateKey accepts ^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$. INPUT, OUTPUT, RUN_SUMMARY and STATE_SNAPSHOT are reserved. Null is not a default. An empty boards array, blank strings, duplicate canonical boards or unknown fields cause an input error.

Board URLs accept only an empty path, / or /xml. A single language query is allowed and must match the input language. Job detail URLs, ports, fragments, credentials, custom domains and redirects are not accepted. A slug must be one plain ASCII tenant label. Language codes are normalized to lowercase.

Settings that work together

SettingsResult
language + URL queryA conflicting URL language is rejected. Language is part of saved board identity.
mode + includeUnchangedincludeUnchanged only adds rows in delta mode. Snapshot rows are baseline.
maxJobsPerBoard + stateA truncated feed can emit capped current events, but cannot replace saved state or emit removals.
maxAttempts + boardTimeoutMsThe board deadline includes retries and waits. More attempts cannot extend the deadline.
stateKey + board listBoards not requested keep their old snapshots. Removing a board from input does not emit its jobs as removed.
snapshot + stateKeyA complete snapshot replaces that board's saved state. Use a separate key for one-off exports if you also run a delta monitor.

Filters and reset

The Actor filters board hosts, XML shape and safe source URLs for valid input. It has no title, department, location, remote, date or salary search filters. Use the exported fields for your own filtering after the run. The job cap is a safety limit, not a search filter.

To start a new baseline, stop overlapping runs and choose a new stateKey, such as WEEKLY_PERSONIO_EN_V2. The old record stays intact. There is no reset flag and no state deletion in this workflow. To return to a monitor, restore its original key; it compares with that key's last complete snapshot.

Input examples

Minimal snapshot

{
"boards": [
"teamative"
],
"language": "en",
"mode": "snapshot"
}

This requests one current English board. In the captured source scope, it yields baseline job rows. The example uses a safe state key; the captured input and full samples are retained in the repository.

Repeated three-board monitor

{
"boards": [
"teamative",
"personio",
"sipgate"
],
"language": "en",
"mode": "delta",
"maxJobsPerBoard": 100,
"maxBodyBytes": 1048576,
"boardTimeoutMs": 30000,
"maxAttempts": 2,
"stateKey": "WEEKLY_PERSONIO_EN",
"includeUnchanged": false
}

Use this input unchanged for later checks. The first complete check on this key is a baseline. The next unchanged check can return zero rows. Three real boards returned 22 jobs in the checked snapshot and delta baseline, followed by zero repeat events. A future board may have more jobs than 100; check truncated status and raise the cap when needed.

Output example

This full job JSON is from a real teamative snapshot. The timestamp is the capture time, not a promise of current freshness. English description text was absent; the salary and offices came from the source.

{
"_type": "job",
"_schemaVersion": "1.0",
"_source": "personio",
"tenant": "teamative",
"jobId": "2681315",
"title": "Junior SAP Funkčný Konsultant – SAP CE / SAP IS-U",
"publicUrl": "https://teamative.jobs.personio.de/job/2681315",
"applyUrl": null,
"subcompany": "teamative Slovakia s.r.o.",
"office": "SK - Bratislava",
"additionalOffices": [
"SK - Košice"
],
"department": "Delivery",
"recruitingCategory": "SK - application process IT/SAP",
"employmentType": "permanent",
"seniority": "entry-level",
"schedule": "full-time",
"yearsOfExperience": "1-2",
"keywords": [
"SAP IS-U,SAP CE,ISU,CE,Junior"
],
"occupation": "enterprise_software_implementation_consulting",
"occupationCategory": "it_software",
"createdAt": "2026-06-22T11:07:27+00:00",
"descriptionSections": [],
"descriptionText": null,
"salaryInformation": {
"minimum": 2000,
"maximum": null,
"currency": "EUR",
"period": "monthly"
},
"company": "teamative",
"url": "https://teamative.jobs.personio.de/job/2681315",
"location": "SK - Bratislava",
"allLocations": [
"SK - Bratislava",
"SK - Košice"
],
"postedAt": "2026-06-22T11:07:27.000Z",
"commitment": "Full-time",
"team": null,
"country": null,
"workplaceType": null,
"updatedAt": null,
"observedAt": "2026-10-01T11:43:33.910Z",
"contentHash": "6b6d47abef37ae0392319d40644adbbefd20f280ac210875376111463339dbfe",
"_scrapedAt": "2026-10-01T11:43:33.910Z",
"eventType": "baseline",
"changedFields": []
}

JSON output keeps arrays and objects. For export:

  1. Open the finished run's default Dataset.
  2. Choose Export and JSON, CSV or Excel (XLSX).
  3. Keep the identity, title and event fields when selecting columns.
  4. Check Unicode titles and office names in the downloaded file.

JSON, CSV and XLSX exports were checked on 18 real rows. Use JSON for full nested sections, salary and arrays. CSV/Excel flatten nested values; select columns for your consumer. A table view can hide columns without removing them from raw JSON. See Dataset export documentation.

Full Output field reference

There are 41 Dataset fields: 39 are always present, with nullable values where shown; previousObservedAt and removedAt are event-specific. Source fields and aliases describe the same job. Monitor fields describe this check.

Source job fields

FieldTypeEmpty or absent?ExampleMeaning and provenance
_typestringNo"job"Fixed value job. Removed records are still job records.
_schemaVersionstringNo"1.0"Record contract version 1.0; not the upstream XML version.
_sourcestringNo"personio"Fixed source marker personio.
tenantstringNo"teamative"Validated board slug. Use tenant + jobId as the job identity.
jobIdstringNo"2681315"Numeric XML position ID, stored as a string. Unique within a board.
titlestringNoJunior SAP Funkčný Konsultant – SAP CE / SAP IS-UXML job name, trimmed. XML entities are decoded once; CDATA text stays literal.
publicUrlstringNo"https://teamative.jobs.personio.de/job/2681315"Built from the validated source host and jobId.
applyUrlstring / nullYes: nullnullSource value only when it is a safe same-host HTTPS /job/{jobId} URL, with no query or fragment. No invented apply link.
subcompanystring / nullYes: null"teamative Slovakia s.r.o."XML subcompany value. This may differ from the tenant slug.
officestring / nullYes: null"SK - Bratislava"First office value from XML.
additionalOfficesarrayYes: []["SK - Košice"]Remaining office values and explicit additional offices. Empty array when absent.
departmentstring / nullYes: null"Delivery"XML department. No department filter is applied.
recruitingCategorystring / nullYes: null"SK - application process IT/SAP"XML recruiting category, kept separate from team.
employmentTypestring / nullYes: null"permanent"XML employment type; no fixed job-type vocabulary is added.
senioritystring / nullYes: null"entry-level"XML seniority value; no seniority guess from the title.
schedulestring / nullYes: null"full-time"XML schedule value, such as full-time.
yearsOfExperiencestring / nullYes: null"1-2"Source text, not a calculated number.
keywordsarrayYes: []["SAP IS-U,SAP CE,ISU,CE,Junior"]Source keyword values; empty array when absent.
occupationstring / nullYes: null"enterprise_software_implementation_consulting"XML occupation value.
occupationCategorystring / nullYes: null"it_software"XML occupation category value.
createdAtstring / nullYes: null"2026-06-22T11:07:27+00:00"Original source creation date string. It is not a last-update time.
descriptionSectionsarrayYes: [][]Source sections with title and content. Content can contain HTML; treat it as untrusted text.
descriptionTextstring / nullYes: nullnullText built from section headings and content with HTML tags removed. HTML character references are decoded once. Original HTML stays in descriptionSections. Null when no useful text is available.
salaryInformationobject / nullYes: null{"minimum": 2000, "maximum": null, "currency": "EUR", "period": "monthly"}Published salary values, including supported currencyCode/type names. Numbers may be read from source salary text. No salary prediction or currency conversion.

Existing consumer aliases

FieldTypeEmpty or absent?ExampleMeaning and provenance
companystringNo"teamative"tenant alias. A board identifier, not a verified legal company name.
urlstringNo"https://teamative.jobs.personio.de/job/2681315"publicUrl alias.
locationstring / nullYes: null"SK - Bratislava"office alias.
allLocationsarray / nullYes: null["SK - Bratislava", "SK - Košice"]Unique office values, main office first. Null if no offices are supplied.
postedAtstring / nullYes: null"2026-06-22T11:07:27.000Z"An ISO date-time createdAt converted to UTC only when its calendar date, time and explicit timezone are valid. Null for a missing, invalid or timezone-free value. The original createdAt stays unchanged.
commitmentstring / nullYes: null"Full-time"schedule with the first letter in uppercase. Null when absent.
_scrapedAtstringNo"2026-10-01T11:43:33.910Z"Time of this check; on removed rows, this differs from the last-seen observedAt.
teamnullYes: nullnullAlways null: there is no dedicated mapped source field.
countrynullYes: nullnullAlways null: no country is inferred from office names.
workplaceTypenullYes: nullnullAlways null: no remote or hybrid status is inferred.
updatedAtnullYes: nullnullAlways null: no reliable source update timestamp is mapped.

Monitor extensions

FieldTypeEmpty or absent?ExampleMeaning and provenance
eventTypestringNobaseline / new / changed / removed / unchangedbaseline, new, changed, removed or unchanged; see mode rules.
observedAtstringNo"2026-10-01T11:43:33.910Z"Run-start UTC time for a current job. For removed rows, keeps the last time that job was seen.
changedFieldsarrayYes: [][]Names of changed core fields, only for changed events. Empty array for other events. Alias names are not repeated.
contentHashstringNo6b6d47abef37… (64 hex characters)SHA-256 of 24 normalized core fields. Stable for unchanged content. Observation times and aliases are excluded.
previousObservedAtstringAbsent on other eventsISO UTC; changed or removed onlyPrevious saved observation for changed or removed events. Absent for other event types.
removedAtstringAbsent on other eventsISO UTC; removed onlyUTC time when a complete feed no longer contains the job. Present only for removed events. It does not prove a role was filled.

Nested job values

Nested fieldTypeMeaning / empty value / example
descriptionSections[].titlestring / nullSection name, or null. Example: Your profile.
descriptionSections[].contentstringSource section body; may contain HTML. Example:

Public job text

. Never execute it.
salaryInformation.minimumnumber / nullSource minimum, e.g. 2000.
salaryInformation.maximumnumber / nullSource maximum; null in the selected live row.
salaryInformation.currencystring / nullSource currency/currencyCode or recognizable source currency text, e.g. EUR.
salaryInformation.periodstring / nullSource period/type or recognizable source period text, e.g. monthly.

XML & becomes & and & becomes &. CDATA is literal at the XML layer. Description text is then cleaned as HTML text, so its HTML entities follow text-cleaning rules. DOCTYPE/ENTITY declarations and malformed feeds are refused. These cases do not become valid empty boards.

The content hash includes 24 original core fields, with normalized whitespace and stable object key order. Arrays keep their order. Observation timestamps and consumer aliases do not trigger changed events. A section or office change can change the hash. changedFields lists original core names, such as office, not alias names such as location.

Where each result is saved

SurfaceWhat it contains
Default DatasetAll emitted job events from this run. Error details are in summaries, not extra job/error rows.
Default KVS INPUTThe run input.
Default KVS OUTPUTCompact result, counts, board results and first up to 10 events.
Default KVS RUN_SUMMARYFull board outcomes, attempts, resource counters and billing status.
Default KVS STATE_SNAPSHOTCaptured monitor state for this run, including saved snapshots from earlier checks.
Named KVS personio-jobs-monitor-stateDurable state under the chosen stateKey. Separate runs use this store for comparisons.
Output link jobsDefault Dataset API items.
Output link runOutput / runSummary / stateOUTPUT / RUN_SUMMARY / STATE_SNAPSHOT in the run's default KVS.

An input error writes OUTPUT and RUN_SUMMARY and fails the process. A PPE persistence or billing failure writes BILLING_ERROR and fails the process; saved records stay available. If data writing stopped, OUTPUT, RUN_SUMMARY or STATE_SNAPSHOT may be absent. Check the Dataset, receipt and platform status too. Other runtime errors write error surfaces before the process fails. STATE_SNAPSHOT may be absent. An input error summary uses fallback mode/language/key values, not validated input. A partial board-check loop can finish with process success; inspect each board before using its rows. When every board has a technical error, or the run is cancelled, the process fails after saving diagnostics. An all-404 result can finish successfully with explicit not_found statuses, but it is not a complete empty XML snapshot. Always inspect the summary.

The sanitized KVS examples below use a replaced run ID and state key. The reduced state sample contains one job from the 18-job snapshot; it is not the full snapshot. Its job values and shape are unchanged.

OUTPUT

FieldType / presenceMeaning / example
schemaVersionstringFixed personio-monitor-output/v1.
runIdstringApify run ID; EXAMPLE_RUN_ID in the sanitized sample.
terminalOutcomestringsuccess, partial, error or cancelled. Read this before using rows.
modestringsnapshot or delta for this run.
languagestringRequested language, e.g. en.
observedAtstringRun-start UTC timestamp.
datasetItemCountintegerNumber of emitted Dataset rows, e.g. 18.
eventCountsobjectEmitted counts by event type; see EventCounts below.
boardsarrayOne BoardSummary for each requested board.
datasetobjectDefault Dataset selector and emitted item count.
dataset.keystringFixed default; not an Apify Dataset ID.
dataset.itemCountintegerSame emitted row count as datasetItemCount.
samplearrayFirst up to 10 compact event rows; empty when no rows are emitted.
sample[].eventTypestringEvent classification.
sample[].tenantstringBoard slug.
sample[].jobIdstringSource job ID.
sample[].titlestringSource job title.
sample[].contentHashstringSHA-256 core hash.

RUN_SUMMARY

FieldType / presenceMeaning / example
schemaVersionstringFixed personio-monitor-run-summary/v1.
runIdstringApify run ID.
modestringRun mode.
languagestringRequested language.
stateKeystringSelected record in the named state store.
startedAtstringRun-start UTC timestamp.
finishedAtstringMonitor-finish UTC timestamp.
durationMsnumberMonitor elapsed time, e.g. 734; not the full platform run duration.
terminalOutcomestringsuccess, partial, error or cancelled.
boardCountintegerNumber of board summaries.
successfulBoardCountintegerBoards with status success, including valid empty feeds.
failedBoardCountintegerBoards with other errors; truncated boards are counted separately.
truncatedBoardCountintegerBoards with too many positions for maxJobsPerBoard.
datasetItemCountintegerNumber of emitted events.
eventCountsobjectCounts of emitted events, not all unchanged jobs.
boardsarrayBoardSummary objects below.
accountingobjectResource counters and billing status below.
redactionobjectLogging flags below; not a promise that source job text has no contacts.
errorClassstring; optionalTop-level runtime/input/storage error code; absent on normal monitor completion.
errorMessagestring; optionalSafe top-level diagnostic; absent on normal completion.

EventCounts

FieldType / presenceMeaning / example
baselineintegerNumber of emitted baseline rows; zero when none are emitted.
newintegerNumber of emitted new rows; zero when none are emitted.
changedintegerNumber of emitted changed rows; zero when none are emitted.
removedintegerNumber of emitted removed rows; zero when none are emitted.
unchangedintegerNumber of emitted unchanged rows; zero when none are emitted.

BoardSummary (OUTPUT.boards[] and RUN_SUMMARY.boards[])

FieldType / presenceMeaning / example
keystringtenant|language, e.g. teamative|en. Host suffix is not a separate state partition.
tenantstringCanonical tenant slug.
languagestringRequested language.
sourceUrlstringCanonical HTTPS XML request URL.
statusstringsuccess, truncated, not_found, access_block, transport_error, schema_drift, timeout, cancelled, security_error or body_cap.
domainStatusstring / nullempty or non_empty after XML parsing; null on a failed fetch.
completebooleanWhether the entire feed was accepted. Only true feeds replace saved board state.
truncatedbooleanTrue when job count exceeds the cap.
capintegerInput maxJobsPerBoard.
jobCountintegerAccepted positions, capped on a truncated feed; not the discarded total.
emittedItemCountintegerRows emitted from this board; may include removals in delta.
attemptsintegerAttempt-log length, including the first request.
durationMsnumberElapsed time for this board.
errorClassstring / nullFailure code, or null on parsed success/truncation.
errorMessagestring / nullSafe diagnostic, or null on parsed success/truncation.
attemptLogarrayAttemptRecord objects below; [] if cancelled before fetching.

AttemptRecord (boards[].attemptLog[])

FieldType / presenceMeaning / example
attemptinteger1-based request attempt number.
statusinteger / nullHTTP status, e.g. 200; null if no status was captured.
durationMsnumberDuration of this attempt.
resultstringresponse, retry, failure or cancelled.
errorClassstring; optionalDiagnostic code on an error/retry; absent on a normal response.
retryAfterMsnumber / null; optionalServer Retry-After parsed and bounded to 10000 ms; absent on other paths, null if unparseable.

Accounting (RUN_SUMMARY.accounting)

FieldType / presenceMeaning / example
currencystringFixed USD.
cloudChargeUsdnumber / nullNull in Cloud until platform read-back; local runs set 0 because no Cloud run is billed.
billingStatusstringpending_platform_readback in Cloud; not_applicable locally. Read final run usage in Console for a platform cost.
externalSpendUsdnumberFixed 0: the Actor does not call paid external services. This is not total platform billing.
localResourceAccountingobjectCounters below; not dollar amounts.
localResourceAccounting.networkRequestsintegerNumber of recorded board attempts; retries included.
localResourceAccounting.responseBytesintegerBytes counted from returned responses. A response interrupted inside the transport may not be counted here.
localResourceAccounting.datasetItemsintegerNumber of emitted Dataset rows.

Redaction (RUN_SUMMARY.redaction)

FieldType / presenceMeaning / example
rawResponsesLoggedbooleanFixed false: raw XML is not written to runtime logs.
credentialsLoggedbooleanFixed false: source credentials are not accepted or logged.
contactDetailsLoggedbooleanFixed false for routine log messages. Public job descriptions in Dataset/state can still contain contacts.

MonitorState (named stateKey and default STATE_SNAPSHOT)

FieldType / presenceMeaning / example
schemaVersionstringFixed personio-monitor-state/v1.
updatedAtstringLast monitor-finish UTC timestamp; may advance while failed board snapshots stay unchanged.
snapshotsobjectMap by tenant|language; includes earlier boards not requested in this run.
snapshots.<tenant|language>objectStoredBoardSnapshot below. Missing if this board has never completed successfully.

StoredBoardSnapshot

FieldType / presenceMeaning / example
tenantstringBoard slug.
languagestringRequested language.
sourceUrlstringAccepted XML feed URL.
observedAtstringTime this complete board was last seen.
jobsobjectMap by jobId. Empty object for a complete empty feed.
jobs.<jobId>objectAll JobCore source fields and aliases above, plus observedAt and contentHash. No eventType, changedFields, _scrapedAt, previousObservedAt or removedAt.

OUTPUT sample

{
"schemaVersion": "personio-monitor-output/v1",
"runId": "EXAMPLE_RUN_ID",
"terminalOutcome": "success",
"mode": "snapshot",
"language": "en",
"observedAt": "2026-10-01T11:43:33.910Z",
"datasetItemCount": 18,
"eventCounts": {
"baseline": 18,
"new": 0,
"changed": 0,
"removed": 0,
"unchanged": 0
},
"boards": [
{
"key": "teamative|en",
"tenant": "teamative",
"language": "en",
"sourceUrl": "https://teamative.jobs.personio.de/xml?language=en",
"status": "success",
"domainStatus": "non_empty",
"complete": true,
"truncated": false,
"cap": 100,
"jobCount": 18,
"emittedItemCount": 18,
"attempts": 1,
"durationMs": 586,
"errorClass": null,
"errorMessage": null,
"attemptLog": [
{
"attempt": 1,
"status": 200,
"durationMs": 566,
"result": "response"
}
]
}
],
"dataset": {
"key": "default",
"itemCount": 18
},
"sample": [
{
"eventType": "baseline",
"tenant": "teamative",
"jobId": "2819528",
"title": "Consultant Informationssicherheit / Notfallmanagement (BCM) (m/w/d)",
"contentHash": "98e456861e61a43c4900488870986d11809756fdf66c3ad1254f691a98aaa53e"
},
{
"eventType": "baseline",
"tenant": "teamative",
"jobId": "2623782",
"title": "Initiativbewerbung",
"contentHash": "8e7d003519b5838ca1f61b6c1b2dde278fb7870415f5321248372431dcb3b069"
},
{
"eventType": "baseline",
"tenant": "teamative",
"jobId": "2681315",
"title": "Junior SAP Funkčný Konsultant – SAP CE / SAP IS-U",
"contentHash": "6b6d47abef37ae0392319d40644adbbefd20f280ac210875376111463339dbfe"
},
{
"eventType": "baseline",
"tenant": "teamative",
"jobId": "2805114",
"title": "Lead Consultant SAP Architecture",
"contentHash": "80861f66a6e54c531a6479c639d12f2242209d8e12905aefb59617caf2067aab"
},
{
"eventType": "baseline",
"tenant": "teamative",
"jobId": "2617009",
"title": "Sales Manager – ServiceNow",
"contentHash": "68a21a83ee5658480905f028382ad56acf5cfcc31ce0e10f01bc46689ccaeec8"
},
{
"eventType": "baseline",
"tenant": "teamative",
"jobId": "2231198",
"title": "SAP CS Consultant with German",
"contentHash": "c0ed5e0aa47157018b4b41e45a448afe6bc3836762fec57496821da2ef0520be"
},
{
"eventType": "baseline",
"tenant": "teamative",
"jobId": "2232665",
"title": "SAP EWM Consultant",
"contentHash": "457f43f34971f3386bef18eb82188351dd0cf942ef78d781876142b96476addc"
},
{
"eventType": "baseline",
"tenant": "teamative",
"jobId": "2670413",
"title": "SAP IS-U Consultant with German (m/f/d)",
"contentHash": "f3ae88c10b769487618d9a34566376bee59a840f162d7d29595439017022eef7"
},
{
"eventType": "baseline",
"tenant": "teamative",
"jobId": "1896335",
"title": "SAP IS-U Konzultant pro Českou republiku (EU-remote)",
"contentHash": "6288fabc6ff238335bafca8ee8ddc6271450e501484b28ced77e1e12031dae28"
},
{
"eventType": "baseline",
"tenant": "teamative",
"jobId": "2553655",
"title": "Senior Consultant ServiceNow ITOM / CMDB",
"contentHash": "8254686597814b510ffe3b6fb0b255306c2cf6df9cda39d37f6b0b6eec315989"
}
]
}

RUN_SUMMARY sample

{
"schemaVersion": "personio-monitor-run-summary/v1",
"runId": "EXAMPLE_RUN_ID",
"mode": "snapshot",
"language": "en",
"stateKey": "PERSONIO_EXAMPLE_SNAPSHOT",
"startedAt": "2026-10-01T11:43:33.910Z",
"finishedAt": "2026-10-01T11:43:34.644Z",
"durationMs": 734,
"terminalOutcome": "success",
"boardCount": 1,
"successfulBoardCount": 1,
"failedBoardCount": 0,
"truncatedBoardCount": 0,
"datasetItemCount": 18,
"eventCounts": {
"baseline": 18,
"new": 0,
"changed": 0,
"removed": 0,
"unchanged": 0
},
"boards": [
{
"key": "teamative|en",
"tenant": "teamative",
"language": "en",
"sourceUrl": "https://teamative.jobs.personio.de/xml?language=en",
"status": "success",
"domainStatus": "non_empty",
"complete": true,
"truncated": false,
"cap": 100,
"jobCount": 18,
"emittedItemCount": 18,
"attempts": 1,
"durationMs": 586,
"errorClass": null,
"errorMessage": null,
"attemptLog": [
{
"attempt": 1,
"status": 200,
"durationMs": 566,
"result": "response"
}
]
}
],
"accounting": {
"currency": "USD",
"cloudChargeUsd": null,
"billingStatus": "pending_platform_readback",
"externalSpendUsd": 0,
"localResourceAccounting": {
"networkRequests": 1,
"responseBytes": 44712,
"datasetItems": 18
}
},
"redaction": {
"rawResponsesLogged": false,
"credentialsLogged": false,
"contactDetailsLogged": false
}
}

Reduced STATE_SNAPSHOT sample

{
"schemaVersion": "personio-monitor-state/v1",
"updatedAt": "2026-10-01T11:43:34.644Z",
"snapshots": {
"teamative|en": {
"tenant": "teamative",
"language": "en",
"sourceUrl": "https://teamative.jobs.personio.de/xml?language=en",
"observedAt": "2026-10-01T11:43:33.910Z",
"jobs": {
"2681315": {
"_type": "job",
"_schemaVersion": "1.0",
"_source": "personio",
"tenant": "teamative",
"jobId": "2681315",
"title": "Junior SAP Funkčný Konsultant – SAP CE / SAP IS-U",
"publicUrl": "https://teamative.jobs.personio.de/job/2681315",
"applyUrl": null,
"subcompany": "teamative Slovakia s.r.o.",
"office": "SK - Bratislava",
"additionalOffices": [
"SK - Košice"
],
"department": "Delivery",
"recruitingCategory": "SK - application process IT/SAP",
"employmentType": "permanent",
"seniority": "entry-level",
"schedule": "full-time",
"yearsOfExperience": "1-2",
"keywords": [
"SAP IS-U,SAP CE,ISU,CE,Junior"
],
"occupation": "enterprise_software_implementation_consulting",
"occupationCategory": "it_software",
"createdAt": "2026-06-22T11:07:27+00:00",
"descriptionSections": [],
"descriptionText": null,
"salaryInformation": {
"minimum": 2000,
"maximum": null,
"currency": "EUR",
"period": "monthly"
},
"company": "teamative",
"url": "https://teamative.jobs.personio.de/job/2681315",
"location": "SK - Bratislava",
"allLocations": [
"SK - Bratislava",
"SK - Košice"
],
"postedAt": "2026-06-22T11:07:27.000Z",
"commitment": "Full-time",
"team": null,
"country": null,
"workplaceType": null,
"updatedAt": null,
"observedAt": "2026-10-01T11:43:33.910Z",
"contentHash": "6b6d47abef37ae0392319d40644adbbefd20f280ac210875376111463339dbfe"
}
}
}
}
}

Optional PPE records

These records appear only when this run uses the supported pay-per-event model. They are in the default KVS, separate from your named monitor state. A receipt confirms platform event counts; it is not a customer invoice or proof of developer profit.

Record / fieldType / exampleMeaning
BILLING_RECEIPT.schemaVersionstring / personio-monitor-billing/v1Receipt format.
BILLING_RECEIPT.runIdstring / run IDExact run; a different run cannot reuse it.
BILLING_RECEIPT.digeststring / 64 hex charactersSHA-256 of the result and planned counts.
BILLING_RECEIPT.phasesaving / persisted / confirmedData write started, data saved, or all event counts confirmed.
BILLING_RECEIPT.counts.complete-board-checkinteger / 1Complete successful boards, including valid empty feeds.
BILLING_RECEIPT.counts.saved-current-jobinteger / 18Unique current-job events from complete boards. Removed events are free.
BILLING_RECEIPT.calls.complete-board-checkready / in_flight / confirmedBoard charge call not sent, uncertain, or confirmed by platform counts.
BILLING_RECEIPT.calls.saved-current-jobready / in_flight / confirmedCurrent-job charge call state.
BILLING_ERROR.runIdstring / run IDRun that failed during persistence or billing.
BILLING_ERROR.errorClassstring / storage_errorSafe failure class.
BILLING_ERROR.errorMessagestring / safe messageReason to inspect results and platform counts.

If a charge response is lost, the Actor reads platform counts. It does not send an uncertain charge again. If data saving was interrupted, it stops recovery to avoid duplicate rows. Preserve the run and contact support. Do not keep restarting it. A later independent run uses your saved monitor state and has its own billable checks.

Pricing and cost examples

Check the Pricing tab and your run limit before Start. This Actor uses pay per event. The Pricing tab shows whether platform usage is included or charged separately.

EventPrice in USDWhat you pay for
Complete board check0.018One complete successful board, including a valid empty feed.
Saved current job0.00045One unique baseline, new, changed or requested unchanged job from a complete board, saved to the Dataset.

Removed jobs, failed/truncated boards and their job rows have no event fee. There is no start fee or extra default Dataset event fee. A check with no changes still has the board fee. For one complete board and18 current jobs, events costUSD0.0261. For three complete boards and22 current jobs, events costUSD0.0639. For three complete boards with no emitted current jobs, events costUSD0.054. These examples describe event charges; your actual counts may differ.

When the Pricing tab shows pay per event + usage, Apify charges platform usage separately, including for failed or partial runs. When usage is included, it is covered by the event price. A zero event fee does not promise zero platform resource use. Check both event fees and usage before scheduling.

The Actor saves results before charging. If the full result exceeds your run charge limit, the run fails without saving or charging that result. Source requests may already have used resources. If a billing call fails after saving, inspect BILLING_RECEIPT and BILLING_ERROR; do not repeatedly restart an uncertain charge.

The observations below are historical platform usage from private technical runs; they are not customer debit evidence for this PPE version.

Observed scopeEmitted rowsPlatform usage USD
One teamative snapshot, first checked run180.00045294
Three boards, snapshot220.00044319
Three boards, delta baseline220.00044465
Same three boards, unchanged delta00.00031457

These runs used 256 MB and final platform usage read-back. They exclude build costs and future extra reads/exports. Billing meters can update after a run ends; RUN_SUMMARY.cloudChargeUsd stays null in Cloud. A zero-row delta still requests boards and reads/writes state. Fewer output rows do not mean a free check.

Use a platform run charge limit, a sensible timeout, and one test board before scheduling. maxJobsPerBoard limits accepted positions per board; it is not a dollar budget or a cap on removed events. No Cloud cost is claimed for 1000 or 5000 jobs from these small runs.

Use cases

Buyer taskInputUseful fieldsNext step
Keep a current vacancy listsnapshot on known boardstenant, jobId, title, publicUrlExport JSON or CSV; replace your current list only from complete boards.
Track added or edited jobsdelta with a stable stateKeyeventType, changedFields, contentHashUpsert by tenant + jobId in your database.
Mark a job as no longer listeddelta with complete sourceremovedAt, previousObservedAt, jobIdArchive the saved role; keep its last-known content.
Check locations and published payeither modeallLocations, salaryInformationFilter these values in your own worksheet or application.

Scheduling and monitoring

These are setup steps for your account, not a schedule created by this Actor.

  1. Run and check the intended delta input once.
  2. In Apify Console, open Schedules and Create new.
  3. Set the timezone and frequency, for example once per day.
  4. Select this Actor or a saved task with the same boards, language and stateKey.
  5. Select a tested build and set timeout, memory and charge limits.
  6. Leave enough time between runs for the full board list to finish. Do not overlap runs on the same key.
  7. After each run, read RUN_SUMMARY. Use its board complete/status fields before treating missing rows as domain events.

See Apify scheduling. Scheduled events can be delayed. This Actor has no state lock or automatic alert delivery; send notifications from your own workflow after checking eventType and terminalOutcome.

API, MCP and integrations

API

The API example uses this Actor's exact ID. Set PERSONIO_BUILD to the exact build number you have checked in Console. It does not use a moving build tag. Your account must have access to the Actor. Start creates a run and can incur a charge. Keep your token in APIFY_TOKEN in the environment. Do not put it in a URL, source file, screenshot or shared input.

# APIFY_TOKEN must already be set securely in your environment.
# Set PERSONIO_BUILD to your checked build number (for example, 0.1.4 for the earlier runtime).
: "${PERSONIO_BUILD:?Set the exact checked build number first}"
curl --fail-with-body --request POST \
"https://api.apify.com/v2/acts/vJ9HvIdGAVEGWQbxF/runs?build=${PERSONIO_BUILD}&memory=256&timeout=120&maxTotalChargeUsd=0.03" \
--header "Authorization: Bearer ${APIFY_TOKEN:?Set APIFY_TOKEN securely first}" \
--header 'Content-Type: application/json' \
--data '{"boards":["teamative"],"language":"en","mode":"snapshot","stateKey":"API_PERSONIO_EN"}'

Read data.id from the response. Poll GET /v2/actor-runs/{RUN_ID} until it is terminal. Read data.defaultDatasetId and data.defaultKeyValueStoreId. Download GET /v2/datasets/{DATASET_ID}/items?format=json and GET /v2/key-value-stores/{KVS_ID}/records/RUN_SUMMARY, with the same Authorization header. For tabular exports use format=csv or format=xlsx. Check both platform status and the summary. See Run Actor API.

MCP and downstream tools

Apify provides an MCP server for supported Actors and storage tools. This Actor has no custom MCP server, and its execution through MCP has not been verified. Use the API above for the documented path. An existing authorized MCP storage tool may read a Dataset that your account can access; check that tool's permissions first.

Your own Node.js/Python app can read the Dataset API. You can also import CSV/XLSX into a spreadsheet. For an upsert, use tenant + jobId; use eventType for the action and contentHash for content checks. This Actor does not create a database, CRM record, webhook or notification service.

Limits and expected partial results

Input safety caps are 50 boards, 5000 positions per board and 10 MiB per response. Real Cloud checks covered up to 22 current jobs across 3 boards at 256 MB. A local synthetic test accepted 5000 positions on one board. The local test is not a Cloud capacity or throughput guarantee. Start small and inspect the results for your source.

SituationDatasetSaved board stateHow to read it
Complete feed with no jobsNo current jobs; delta may emit removalsReplaced with an empty job mapstatus=success, domainStatus=empty, complete=true.
Unchanged deltaEmpty unless includeUnchanged=trueUpdated complete snapshotsuccess and zero emitted events are normal.
More jobs than the capCapped current events; no removalsOld snapshot keptstatus=truncated, complete=false, terminalOutcome=partial.
404, block, bad XML, body cap or timeoutNo events from that failed boardOld snapshot keptError status, complete=false; not an empty domain result.
Some boards failSuccessful/capped board events stay availableOnly complete boards replace their stateterminalOutcome=partial; inspect boards individually.
All boards failNo job eventsOld board snapshots keptterminalOutcome=error, even if platform process completed.

Snapshots measure the source only at check time. Language availability, published fields, source changes and rate limits can affect output. Retry delays are bounded; only selected failures retry. Redirects and private/reserved network addresses are refused. No proxy or Personio credential input is supported.

The Actor reads public job feeds. It does not read applications, candidate profiles, private HR records or login-only data. Public descriptions may contain names, email addresses or phone numbers. They are not automatically redacted from Dataset/state. Review them before sharing. Follow source terms and your data-use duties. This is an independent product, not affiliated with or endorsed by Personio.

Troubleshooting

SymptomLikely reasonCheckSafe next action
Input error before requestsBad board shape, duplicate board, reserved state key or unknown fieldInput and top-level errorClassUse the exact fields and valid board root URL.
Failed run with redirect_blockedThe tenant redirects away from its XML feedRUN_SUMMARY and the company careers boardUse the actual public company tenant. Redirects stay blocked. Previous state is kept.
Empty delta DatasetNo changes, a valid empty board, or failed checksterminalOutcome, status, domainStatus, completeFor no changes do nothing; for failure fix the input/source and retry.
descriptionText is nullEmployer has no text in the requested languagedescriptionSections and public sourceTry the employer's language with a separate monitor key if desired.
Posted time is null but createdAt is filledNo explicit timezone or invalid source dateOriginal createdAt stringKeep the source value; do not invent a timezone.
Truncated statusFeed exceeds maxJobsPerBoardcap and jobCountRaise the cap within its range; previous complete state stays intact.
body_cap / timeoutResponse is too large or slowmaxBodyBytes, boardTimeoutMs and attemptLogRaise the relevant bound within the input range, then test one board.
not_found / access_block / schema_driftWrong board, denied access or changed XMLsourceUrl and safe errorMessageCheck the public board; report a reproducible source change. Do not treat it as all jobs removed.
Repeated new/changed eventsTruncated snapshots, a changed key, or overlapping runscomplete, stateKey and run timesKeep one key, complete feeds and sequential runs.
State cannot be readUnsupported or damaged saved stateTop-level error and named KVS recordPreserve the old record. Use a new key for a fresh baseline, then report the issue.

FAQ

Do I need a Personio account?

No. Only known public XML boards are read. You need Apify access to run the Actor. Private job data is outside this workflow.

Can I use a custom careers domain or a job URL?

No. Use a plain board slug or the supported Personio board root/XML URL. A slug points to .de; use a full URL for .com.

Does removed mean the company filled the job?

No. It means the ID disappeared from a complete accepted feed. The employer may have hidden, moved or removed the listing for another reason.

Why is the first delta a baseline?

There is no prior complete board snapshot under this key and language. Later delta runs compare with the saved state. Changing the key starts a new comparison.

Does snapshot mode affect my monitor?

Yes. A complete snapshot updates the selected state record. Use a separate key for an export that should not move your delta baseline.

After an update that corrects derived fields, use snapshot mode to refresh all exported rows. Delta reports source content changes; it does not refresh every unchanged row. Use a separate key if you want to keep your existing monitor baseline.

Is createdAt the job's last change date?

No. It is the source creation value. updatedAt remains null. changed events compare content; they do not supply an employer update timestamp.

Are all fields always filled?

No. Null means the mapped value is absent or not safe to use. Empty arrays mean no values were supplied. Optional event timestamps can be absent. A job row with null source fields is different from a failed board fetch.

Are repeated runs free if no jobs change?

No free-run promise is made. Source checks and state work still use platform resources. Check the Pricing tab and run usage.

These are separate source tools. Check each tool's input and output before joining their data.

Support

Open the Actor's Issues tab for a question or reproducible bug. This is the support route listed by Cliqto Media. Include the run ID, board URL, language, mode and safe error text. Share only the fields needed to reproduce the issue. Never post tokens, cookies, private payloads or account billing details.