{
  "openapi": "3.0.1",
  "info": {
    "title": "Personio Jobs by Company: Career Site Feed API",
    "description": "Open positions of any company career site on Personio, read live from its keyless job-board feed: title, department, offices, employment type, schedule, seniority, occupation category, skill keywords, disclosed minimum salary, creation date and job page. A summary mode counts open jobs per group.",
    "version": "0.1",
    "x-build-id": "TERPXdNokUDGgGSqD"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/yadroo~personio-jobs/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-yadroo-personio-jobs",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/acts/yadroo~personio-jobs/runs": {
      "post": {
        "operationId": "runs-sync-yadroo-personio-jobs",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor and returns information about the initiated run in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/runsResponseSchema"
                }
              }
            }
          }
        }
      }
    },
    "/acts/yadroo~personio-jobs/run-sync": {
      "post": {
        "operationId": "run-sync-yadroo-personio-jobs",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "inputSchema": {
        "type": "object",
        "required": [
          "companies"
        ],
        "properties": {
          "companies": {
            "title": "Career sites",
            "type": "array",
            "description": "One or more career-site slugs, e.g. `carbmee`, `stark`, `deskbird`: the label in front of `.jobs.personio.com`. A whole career-site or job URL works too; the slug is read out of it. Each entry costs one request that returns the whole board. A slug without a career site, and an entry that is no slug at all, each become one `found: false` row; the other entries still run.",
            "items": {
              "type": "string"
            }
          },
          "mode": {
            "title": "What to return",
            "enum": [
              "jobs",
              "summary"
            ],
            "type": "string",
            "description": "`jobs` writes a row per open position of every career site. `summary` groups the same positions by the field chosen below and writes a count per value - the cheap way to see how an employer is organised, where it hires and which codes its own postings carry before you filter on them. Both modes make the same single request per career site, so a summary costs no extra calls.",
            "default": "jobs"
          },
          "groupBy": {
            "title": "Group the summary by",
            "enum": [
              "department",
              "office",
              "recruitingCategory",
              "employmentType",
              "schedule",
              "seniority",
              "occupationCategory",
              "occupation",
              "keyword"
            ],
            "type": "string",
            "description": "Only used when *What to return* is `summary`. A position open in three offices counts once per office, a position with five keywords once per keyword, so the office and keyword counts can exceed the board size; the field `boardJobCount` on every row carries the number of positions the group was counted from. Positions where the field is empty are collected under the value `(none)`, so nothing disappears from the totals.",
            "default": "department"
          },
          "titleKeywords": {
            "title": "Job title contains any of",
            "type": "array",
            "description": "Keep a position when its title contains at least one of these words, case-insensitive, e.g. [\"engineer\", \"scientist\"]. Empty = every open position of the career site.",
            "items": {
              "type": "string"
            }
          },
          "excludeTitleKeywords": {
            "title": "Job title must not contain",
            "type": "array",
            "description": "Drop a position when its title contains any of these words, e.g. [\"initiativbewerbung\", \"speculative\", \"talent pool\"]. Applied after *Job title contains any of*. Employers on this platform often keep a permanent open-application posting online - this is how you leave it out of a board count.",
            "items": {
              "type": "string"
            }
          },
          "departments": {
            "title": "Department contains any of",
            "type": "array",
            "description": "Keep positions whose department contains one of these, case-insensitive, e.g. [\"Engineering\", \"Revenue\"]. Departments are free text (seen live: `R&D / Software`, `Founder's Office`, `Delivery`, `Growth`), so run `summary` grouped by department once to read the exact names an employer uses. Positions without a department are dropped by this filter.",
            "items": {
              "type": "string"
            }
          },
          "offices": {
            "title": "Office contains any of",
            "type": "array",
            "description": "Keep positions with an office that contains one of these, e.g. [\"Munich\", \"Berlin\", \"Thessaloniki\"]. Matched against the main office and every further office, so a posting open in Berlin and Munich is kept by either value. Office is the label the employer typed (`Munich HQ (Karlsfeld)`, `Plant (Gendorf)`), not a normalised city - a substring like `munich` is the safe way to ask.",
            "items": {
              "type": "string"
            }
          },
          "recruitingCategories": {
            "title": "Recruiting category contains any of",
            "type": "array",
            "description": "Keep positions whose recruiting category contains one of these. This is the employer's own bucket for a posting and often stays in its native language (seen live: `Engineering`, `Festangestellte`, `Werkstudenten`, `FTE Engineering/SE/Product`). Use `summary` grouped by recruiting category to read the list of one company before filtering.",
            "items": {
              "type": "string"
            }
          },
          "employmentTypes": {
            "title": "Employment type is any of",
            "type": "array",
            "description": "Keep positions whose employment-type code matches one of these, case-insensitive and with `-`/`_`/space treated as the same character, e.g. [\"permanent\"] or [\"intern\", \"working_student\"]. Codes seen on live career sites: `permanent`, `fixed_term`, `intern`, `working_student`, `trainee`, `freelance`. The platform publishes no closed list, so run `summary` grouped by employment type when a filter returns fewer rows than you expect.",
            "items": {
              "type": "string"
            }
          },
          "seniorities": {
            "title": "Seniority is any of",
            "type": "array",
            "description": "Keep positions whose seniority code matches one of these, e.g. [\"student\"], [\"entry-level\"] or [\"experienced\"]. Hyphen and underscore are interchangeable here, so `entry_level` and `entry-level` are the same value. Part of the postings leave the field empty - those are dropped by this filter and visible as the `(none)` group of a seniority summary.",
            "items": {
              "type": "string"
            }
          },
          "schedule": {
            "title": "Working time",
            "enum": [
              "any",
              "full-time",
              "part-time",
              "full-or-part-time"
            ],
            "type": "string",
            "description": "The source stores a third value next to full-time and part-time for postings an employer opened for either, which is why `full-or-part-time` is a value of its own here instead of a guess. Pick `part-time` and you get the genuinely part-time postings only; pick `full-or-part-time` to find the flexible ones a plain full/part split hides.",
            "default": "any"
          },
          "occupationCategories": {
            "title": "Occupation category contains any of",
            "type": "array",
            "description": "Keep positions whose platform job family contains one of these, e.g. [\"it_software\"], [\"sales_and_business_development\"], [\"marketing_and_product\"], [\"engineering\"], [\"finance\"]. Unlike department this code comes from the platform's own taxonomy, so it is comparable across employers - the right filter when you scan many career sites for the same kind of role. Postings the employer did not classify carry `other`.",
            "items": {
              "type": "string"
            }
          },
          "keywordsAny": {
            "title": "Skill keyword is any of",
            "type": "array",
            "description": "Keep positions carrying one of these skill keywords, matched as whole words, case-insensitive, e.g. [\"python\", \"kubernetes\"]: `SQL` keeps a posting tagged `SQL` or `SQL Server`, not one tagged only `PostgreSQL`; `software` still finds `Software Development`. Employers fill keywords themselves and many leave them empty, so this is a sharp filter, not a complete skills index. Group a summary by skill keyword to see what is tagged.",
            "items": {
              "type": "string"
            }
          },
          "onlySalaryDisclosed": {
            "title": "Only positions with a published salary",
            "type": "boolean",
            "description": "Keep only positions where the employer published a salary figure. The source carries a minimum amount, a currency and a period (`hourly`, `monthly`, `yearly`), and on a small share of postings an upper bound as well - so these rows answer \"who discloses pay and from how much\", the pay-transparency question. `salaryIsMinimum` tells the two apart: true when the employer named a floor and no ceiling, false when `salaryMax` carries one.",
            "default": false
          },
          "postedWithinDays": {
            "title": "Created within the last N days",
            "minimum": 1,
            "maximum": 3650,
            "type": "integer",
            "description": "Keep positions the employer created in the last N days - the \"who started hiring recently\" filter behind hiring-signal and lead-generation pipelines. The source gives the creation moment with a clock time, converted to UTC here, so windows of a day or two work as well. Empty = no age limit. Note that the date is when the posting was created, not when it was last edited or re-advertised."
          },
          "onlyNew": {
            "title": "Only positions not seen before",
            "type": "boolean",
            "description": "Write only positions no earlier run returned. Career site and position id are remembered in a named key-value store in your account: `personio-jobs-state`, or `personio-jobs-state-<task id>` for a task. The first run writes all it finds; positions cut by a limit come back next time. A `found: false` row is also written once, not on every run. Schedule runs one after another, not in parallel. Job rows mode only.",
            "default": false
          },
          "language": {
            "title": "Posting language",
            "enum": [
              "source",
              "en",
              "de",
              "fr",
              "es",
              "it",
              "nl",
              "pt"
            ],
            "type": "string",
            "description": "Ask the feed for one language version of the board. Titles and sections come back translated where the employer maintains a translation. A language can also shrink the board: a posting the employer did not publish in it is left out (13 positions in English, 11 in German on one board on 29.09.2026). `source` (the default) asks for nothing and returns the complete board.",
            "default": "source"
          },
          "includeDescription": {
            "title": "Include the description sections",
            "type": "boolean",
            "description": "Add `descriptionSections` (the employer's own section names with their text, e.g. *Your mission*, *What you need to be successful*), plus `descriptionHtml` and `descriptionText` of the whole posting, to every job row. The texts arrive in the same request, so this costs no extra call - it only makes rows much larger, which is why it is off by default. Some postings, and some language versions of a posting, carry no sections at all; those rows get empty values instead of a guess.",
            "default": false
          },
          "sortBy": {
            "title": "Sort rows by",
            "enum": [
              "createdDesc",
              "createdAsc",
              "titleAsc",
              "departmentAsc",
              "officeAsc",
              "feedOrder"
            ],
            "type": "string",
            "description": "Order of the rows before *Max rows* cuts the list. With several career sites the rows are sorted across all of them, after *Max rows per career site* was applied. In `summary` mode rows are ordered by job count, largest group first.",
            "default": "createdDesc"
          },
          "maxItems": {
            "title": "Max rows",
            "minimum": 1,
            "maximum": 5000,
            "type": "integer",
            "description": "Stop after this many rows in total. Boards on this platform are employer-sized: 1-15 positions for a small company, 20-80 for a mid-size one, a few hundred for a large group. Raise it when you watch a long list of career sites.",
            "default": 50
          },
          "maxItemsPerCompany": {
            "title": "Max rows per career site",
            "minimum": 1,
            "maximum": 2000,
            "type": "integer",
            "description": "Cap the rows taken from each career site before the global *Max rows*, so one large employer cannot fill the whole dataset when you watch a list of companies. Empty = no per-company cap."
          },
          "fields": {
            "title": "Output fields",
            "type": "array",
            "description": "Keep only these columns, in this order, e.g. [\"title\", \"office\", \"createdAt\", \"url\"]. `companySlug` and `found` are always kept. A name in another letter case or with a small typo is corrected and reported; a name that is no column of the chosen mode is left out and reported; if none is a column, the run stops before any request with the list of valid names. Empty = every column.",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "runsResponseSchema": {
        "type": "object",
        "properties": {
          "data": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "actId": {
                "type": "string"
              },
              "userId": {
                "type": "string"
              },
              "startedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "finishedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "status": {
                "type": "string",
                "example": "READY"
              },
              "meta": {
                "type": "object",
                "properties": {
                  "origin": {
                    "type": "string",
                    "example": "API"
                  },
                  "userAgent": {
                    "type": "string"
                  }
                }
              },
              "stats": {
                "type": "object",
                "properties": {
                  "inputBodyLen": {
                    "type": "integer",
                    "example": 2000
                  },
                  "rebootCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "restartCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "resurrectCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "computeUnits": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "options": {
                "type": "object",
                "properties": {
                  "build": {
                    "type": "string",
                    "example": "latest"
                  },
                  "timeoutSecs": {
                    "type": "integer",
                    "example": 300
                  },
                  "memoryMbytes": {
                    "type": "integer",
                    "example": 1024
                  },
                  "diskMbytes": {
                    "type": "integer",
                    "example": 2048
                  }
                }
              },
              "buildId": {
                "type": "string"
              },
              "defaultKeyValueStoreId": {
                "type": "string"
              },
              "defaultDatasetId": {
                "type": "string"
              },
              "defaultRequestQueueId": {
                "type": "string"
              },
              "buildNumber": {
                "type": "string",
                "example": "1.0.0"
              },
              "containerUrl": {
                "type": "string"
              },
              "usage": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "integer",
                    "example": 1
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_UNBLOCKER_UNITS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "usageTotalUsd": {
                "type": "number",
                "example": 0.00005
              },
              "usageUsd": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "number",
                    "example": 0.00005
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_UNBLOCKER_UNITS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}