{
  "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": "eV4BUkAzcvOthEygk"
  },
  "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 slug is the label in front of `.jobs.personio.com` on the employer's career site — paste a whole career-site or job URL (`https://carbmee.jobs.personio.com/job/2717004`) and the slug is read out of it. Each entry costs exactly one request that returns the employer's whole board, so there is no paging to pay for. A slug nobody hosts a career site under is answered by the platform with a redirect to its marketing site; that becomes one row with `found: false` instead of a failed 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, case-insensitive and matched as whole words inside a keyword, e.g. [\"python\", \"kubernetes\"]: `SQL` keeps a posting tagged `SQL` or `SQL Server` but not one tagged only `PostgreSQL`, while `software` still finds `Software Development`. Employers fill the keyword list of a posting themselves (seen live: `ROS,python,c++,robotics,perception,UAV,Jetson Nano NX,Linux,NVIDIA`) and many leave it empty, so treat it as a sharp filter on the boards that use it, not as a complete skills index. `summary` grouped by skill keyword lists what a set of companies really tags.",
            "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": "Remember career site and position id in a named key-value store in your account (`personio-jobs-state`, or `personio-jobs-state-<task id>` when a task starts the run, so two schedules do not blind each other) and write only positions that were not there on an earlier run. The first run writes everything it finds, later runs write what appeared since; positions cut off by the row limit come back next time. Runs started in the same second share one memory snapshot, so schedule a watchlist sequentially. Only for the job rows mode.",
            "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 description sections come back translated where the employer maintains that translation and in the original language where it does not. Asking for a language can also change the set of positions: a board answered with 13 positions in English and 11 in German on 29.09.2026, because a posting the employer did not publish for a language is left out. `source` (the default) asks for nothing and gives you the complete board in its published languages.",
            "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 fields, in this order, e.g. [\"companySlug\", \"title\", \"office\", \"createdAt\", \"url\"]. Empty = every field the mode produces.",
            "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
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}