{
  "openapi": "3.0.1",
  "info": {
    "title": "Platsbanken Scraper — Sweden Job Ads & Employer Leads",
    "description": "Scrape Sweden's national job board Platsbanken (Arbetsformedlingen open data): 40,000+ live ads with employer, organisation number, municipality, occupation, deadline, the named contact person's e-mail and phone, and the ATS vendor behind the apply link. No login, no API key.",
    "version": "0.1",
    "x-build-id": "IL1Xx87NGSIZwQKGr"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/scrapersdelight~platsbanken-jobs-scraper/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-scrapersdelight-platsbanken-jobs-scraper",
        "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/scrapersdelight~platsbanken-jobs-scraper/runs": {
      "post": {
        "operationId": "runs-sync-scrapersdelight-platsbanken-jobs-scraper",
        "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/scrapersdelight~platsbanken-jobs-scraper/run-sync": {
      "post": {
        "operationId": "run-sync-scrapersdelight-platsbanken-jobs-scraper",
        "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",
        "properties": {
          "searchQueries": {
            "title": "Search keywords",
            "type": "array",
            "description": "Free-text searches, one per line. Each is run as its own query and the results are merged and de-duplicated on the ad id. **Swedish freetext is DIACRITIC-SENSITIVE** — measured live 2026-09-05: `sjukskoterska` returns **0** ads and `sjuksköterska` returns **1,915**. Never transliterate å ä ö. Leave empty to sweep every ad matching your other filters.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "qfields": {
            "title": "Extra freetext fields",
            "type": "array",
            "description": "Search these structured fields IN ADDITION to the default headline / description / employer match. **Honest note, measured 2026-09-05:** on the live corpus this made no difference — `volvo` returned 154 ads both with and without `qfields=employer`, because the default freetext already matches the employer name. It is passed straight through in case Arbetsförmedlingen starts honouring it. The API spec also advertises `trait`, but the live API rejects it with HTTP 400, so it is deliberately not offered here.",
            "items": {
              "type": "string",
              "enum": [
                "occupation",
                "skill",
                "location",
                "employer"
              ],
              "enumTitles": [
                "Occupation",
                "Skill",
                "Location",
                "Employer"
              ]
            },
            "default": []
          },
          "freetextBoolMethod": {
            "title": "Multi-word freetext: AND or OR",
            "enum": [
              "",
              "or",
              "and"
            ],
            "type": "string",
            "description": "How to combine the words inside one search keyword. This ONLY changes the result set when \"Turn off smart freetext matching\" is also on — measured 2026-09-05 on `sjuksköterska stockholm`: smart matching ON gives 236 either way; smart matching OFF gives OR 12,106 and AND 313.",
            "default": ""
          },
          "disableSmartFreetext": {
            "title": "Turn off smart freetext matching",
            "type": "boolean",
            "description": "Arbetsförmedlingen enriches a freetext query with machine-learned synonyms and related occupations. Turn this OFF for literal, exact-word matching — measured 2026-09-05: `sjuksköterska` returns 1,915 with enrichment on and 2,447 with it off, because the two searches match different things, not more or fewer of the same thing.",
            "default": false
          },
          "enableFalseNegativeSearch": {
            "title": "Extra pass to avoid false negatives",
            "type": "boolean",
            "description": "Runs an additional search for the literal term alongside the enriched one. Widens recall on rare or newly coined job titles, at some cost in precision.",
            "default": false
          },
          "relevanceThreshold": {
            "title": "Relevance threshold (0–1)",
            "minimum": 0,
            "maximum": 1,
            "type": "integer",
            "description": "Drop weakly matching ads. Only meaningful with a search keyword and the `Relevance` sort. Measured 2026-09-05 on `sjuksköterska`: no threshold 1,915 ads, threshold 0.9 → 1,038."
          },
          "region": {
            "title": "Regions (län)",
            "type": "array",
            "description": "Swedish counties, OR-ed server-side (measured 2026-09-05: Stockholm 10,681 + Västra Götaland 6,523 → 17,204 combined). **The code is a ZERO-PADDED string** — the API answers `region=1` with HTTP 200 and ZERO ads rather than an error, so an unpadded code reads as \"no jobs here\". This actor validates and pads for you, and also accepts the plain Swedish name.",
            "items": {
              "type": "string",
              "enum": [
                "01",
                "03",
                "04",
                "05",
                "06",
                "07",
                "08",
                "09",
                "10",
                "12",
                "13",
                "14",
                "17",
                "18",
                "19",
                "20",
                "21",
                "22",
                "23",
                "24",
                "25"
              ],
              "enumTitles": [
                "Stockholms län (01)",
                "Uppsala län (03)",
                "Södermanlands län (04)",
                "Östergötlands län (05)",
                "Jönköpings län (06)",
                "Kronobergs län (07)",
                "Kalmar län (08)",
                "Gotlands län (09)",
                "Blekinge län (10)",
                "Skåne län (12)",
                "Hallands län (13)",
                "Västra Götalands län (14)",
                "Värmlands län (17)",
                "Örebro län (18)",
                "Västmanlands län (19)",
                "Dalarnas län (20)",
                "Gävleborgs län (21)",
                "Västernorrlands län (22)",
                "Jämtlands län (23)",
                "Västerbottens län (24)",
                "Norrbottens län (25)"
              ]
            },
            "default": []
          },
          "municipality": {
            "title": "Municipalities (kommun)",
            "type": "array",
            "description": "All 290 Swedish municipalities, OR-ed server-side. Live sizes measured 2026-09-05: Stockholm (0180) 6,531 · Göteborg (1480) 3,242. Codes are 4 characters and carry the same zero-pad trap as regions; this actor pads them and also accepts the Swedish name.",
            "items": {
              "type": "string",
              "enum": [
                "0114",
                "0115",
                "0117",
                "0120",
                "0123",
                "0125",
                "0126",
                "0127",
                "0128",
                "0136",
                "0138",
                "0139",
                "0140",
                "0160",
                "0162",
                "0163",
                "0180",
                "0181",
                "0182",
                "0183",
                "0184",
                "0186",
                "0187",
                "0188",
                "0191",
                "0192",
                "0305",
                "0319",
                "0330",
                "0331",
                "0360",
                "0380",
                "0381",
                "0382",
                "0428",
                "0461",
                "0480",
                "0481",
                "0482",
                "0483",
                "0484",
                "0486",
                "0488",
                "0509",
                "0512",
                "0513",
                "0560",
                "0561",
                "0562",
                "0563",
                "0580",
                "0581",
                "0582",
                "0583",
                "0584",
                "0586",
                "0604",
                "0617",
                "0642",
                "0643",
                "0662",
                "0665",
                "0680",
                "0682",
                "0683",
                "0684",
                "0685",
                "0686",
                "0687",
                "0760",
                "0761",
                "0763",
                "0764",
                "0765",
                "0767",
                "0780",
                "0781",
                "0821",
                "0834",
                "0840",
                "0860",
                "0861",
                "0862",
                "0880",
                "0881",
                "0882",
                "0883",
                "0884",
                "0885",
                "0980",
                "1060",
                "1080",
                "1081",
                "1082",
                "1083",
                "1214",
                "1230",
                "1231",
                "1233",
                "1256",
                "1257",
                "1260",
                "1261",
                "1262",
                "1263",
                "1264",
                "1265",
                "1266",
                "1267",
                "1270",
                "1272",
                "1273",
                "1275",
                "1276",
                "1277",
                "1278",
                "1280",
                "1281",
                "1282",
                "1283",
                "1284",
                "1285",
                "1286",
                "1287",
                "1290",
                "1291",
                "1292",
                "1293",
                "1315",
                "1380",
                "1381",
                "1382",
                "1383",
                "1384",
                "1401",
                "1402",
                "1407",
                "1415",
                "1419",
                "1421",
                "1427",
                "1430",
                "1435",
                "1438",
                "1439",
                "1440",
                "1441",
                "1442",
                "1443",
                "1444",
                "1445",
                "1446",
                "1447",
                "1452",
                "1460",
                "1461",
                "1462",
                "1463",
                "1465",
                "1466",
                "1470",
                "1471",
                "1472",
                "1473",
                "1480",
                "1481",
                "1482",
                "1484",
                "1485",
                "1486",
                "1487",
                "1488",
                "1489",
                "1490",
                "1491",
                "1492",
                "1493",
                "1494",
                "1495",
                "1496",
                "1497",
                "1498",
                "1499",
                "1715",
                "1730",
                "1737",
                "1760",
                "1761",
                "1762",
                "1763",
                "1764",
                "1765",
                "1766",
                "1780",
                "1781",
                "1782",
                "1783",
                "1784",
                "1785",
                "1814",
                "1860",
                "1861",
                "1862",
                "1863",
                "1864",
                "1880",
                "1881",
                "1882",
                "1883",
                "1884",
                "1885",
                "1904",
                "1907",
                "1960",
                "1961",
                "1962",
                "1980",
                "1981",
                "1982",
                "1983",
                "1984",
                "2021",
                "2023",
                "2026",
                "2029",
                "2031",
                "2034",
                "2039",
                "2061",
                "2062",
                "2080",
                "2081",
                "2082",
                "2083",
                "2084",
                "2085",
                "2101",
                "2104",
                "2121",
                "2132",
                "2161",
                "2180",
                "2181",
                "2182",
                "2183",
                "2184",
                "2260",
                "2262",
                "2280",
                "2281",
                "2282",
                "2283",
                "2284",
                "2303",
                "2305",
                "2309",
                "2313",
                "2321",
                "2326",
                "2361",
                "2380",
                "2401",
                "2403",
                "2404",
                "2409",
                "2417",
                "2418",
                "2421",
                "2422",
                "2425",
                "2460",
                "2462",
                "2463",
                "2480",
                "2481",
                "2482",
                "2505",
                "2506",
                "2510",
                "2513",
                "2514",
                "2518",
                "2521",
                "2523",
                "2560",
                "2580",
                "2581",
                "2582",
                "2583",
                "2584"
              ],
              "enumTitles": [
                "Upplands Väsby (0114)",
                "Vallentuna (0115)",
                "Österåker (0117)",
                "Värmdö (0120)",
                "Järfälla (0123)",
                "Ekerö (0125)",
                "Huddinge (0126)",
                "Botkyrka (0127)",
                "Salem (0128)",
                "Haninge (0136)",
                "Tyresö (0138)",
                "Upplands-Bro (0139)",
                "Nykvarn (0140)",
                "Täby (0160)",
                "Danderyd (0162)",
                "Sollentuna (0163)",
                "Stockholm (0180)",
                "Södertälje (0181)",
                "Nacka (0182)",
                "Sundbyberg (0183)",
                "Solna (0184)",
                "Lidingö (0186)",
                "Vaxholm (0187)",
                "Norrtälje (0188)",
                "Sigtuna (0191)",
                "Nynäshamn (0192)",
                "Håbo (0305)",
                "Älvkarleby (0319)",
                "Knivsta (0330)",
                "Heby (0331)",
                "Tierp (0360)",
                "Uppsala (0380)",
                "Enköping (0381)",
                "Östhammar (0382)",
                "Vingåker (0428)",
                "Gnesta (0461)",
                "Nyköping (0480)",
                "Oxelösund (0481)",
                "Flen (0482)",
                "Katrineholm (0483)",
                "Eskilstuna (0484)",
                "Strängnäs (0486)",
                "Trosa (0488)",
                "Ödeshög (0509)",
                "Ydre (0512)",
                "Kinda (0513)",
                "Boxholm (0560)",
                "Åtvidaberg (0561)",
                "Finspång (0562)",
                "Valdemarsvik (0563)",
                "Linköping (0580)",
                "Norrköping (0581)",
                "Söderköping (0582)",
                "Motala (0583)",
                "Vadstena (0584)",
                "Mjölby (0586)",
                "Aneby (0604)",
                "Gnosjö (0617)",
                "Mullsjö (0642)",
                "Habo (0643)",
                "Gislaved (0662)",
                "Vaggeryd (0665)",
                "Jönköping (0680)",
                "Nässjö (0682)",
                "Värnamo (0683)",
                "Sävsjö (0684)",
                "Vetlanda (0685)",
                "Eksjö (0686)",
                "Tranås (0687)",
                "Uppvidinge (0760)",
                "Lessebo (0761)",
                "Tingsryd (0763)",
                "Alvesta (0764)",
                "Älmhult (0765)",
                "Markaryd (0767)",
                "Växjö (0780)",
                "Ljungby (0781)",
                "Högsby (0821)",
                "Torsås (0834)",
                "Mörbylånga (0840)",
                "Hultsfred (0860)",
                "Mönsterås (0861)",
                "Emmaboda (0862)",
                "Kalmar (0880)",
                "Nybro (0881)",
                "Oskarshamn (0882)",
                "Västervik (0883)",
                "Vimmerby (0884)",
                "Borgholm (0885)",
                "Gotland (0980)",
                "Olofström (1060)",
                "Karlskrona (1080)",
                "Ronneby (1081)",
                "Karlshamn (1082)",
                "Sölvesborg (1083)",
                "Svalöv (1214)",
                "Staffanstorp (1230)",
                "Burlöv (1231)",
                "Vellinge (1233)",
                "Östra Göinge (1256)",
                "Örkelljunga (1257)",
                "Bjuv (1260)",
                "Kävlinge (1261)",
                "Lomma (1262)",
                "Svedala (1263)",
                "Skurup (1264)",
                "Sjöbo (1265)",
                "Hörby (1266)",
                "Höör (1267)",
                "Tomelilla (1270)",
                "Bromölla (1272)",
                "Osby (1273)",
                "Perstorp (1275)",
                "Klippan (1276)",
                "Åstorp (1277)",
                "Båstad (1278)",
                "Malmö (1280)",
                "Lund (1281)",
                "Landskrona (1282)",
                "Helsingborg (1283)",
                "Höganäs (1284)",
                "Eslöv (1285)",
                "Ystad (1286)",
                "Trelleborg (1287)",
                "Kristianstad (1290)",
                "Simrishamn (1291)",
                "Ängelholm (1292)",
                "Hässleholm (1293)",
                "Hylte (1315)",
                "Halmstad (1380)",
                "Laholm (1381)",
                "Falkenberg (1382)",
                "Varberg (1383)",
                "Kungsbacka (1384)",
                "Härryda (1401)",
                "Partille (1402)",
                "Öckerö (1407)",
                "Stenungsund (1415)",
                "Tjörn (1419)",
                "Orust (1421)",
                "Sotenäs (1427)",
                "Munkedal (1430)",
                "Tanum (1435)",
                "Dals-Ed (1438)",
                "Färgelanda (1439)",
                "Ale (1440)",
                "Lerum (1441)",
                "Vårgårda (1442)",
                "Bollebygd (1443)",
                "Grästorp (1444)",
                "Essunga (1445)",
                "Karlsborg (1446)",
                "Gullspång (1447)",
                "Tranemo (1452)",
                "Bengtsfors (1460)",
                "Mellerud (1461)",
                "Lilla Edet (1462)",
                "Mark (1463)",
                "Svenljunga (1465)",
                "Herrljunga (1466)",
                "Vara (1470)",
                "Götene (1471)",
                "Tibro (1472)",
                "Töreboda (1473)",
                "Göteborg (1480)",
                "Mölndal (1481)",
                "Kungälv (1482)",
                "Lysekil (1484)",
                "Uddevalla (1485)",
                "Strömstad (1486)",
                "Vänersborg (1487)",
                "Trollhättan (1488)",
                "Alingsås (1489)",
                "Borås (1490)",
                "Ulricehamn (1491)",
                "Åmål (1492)",
                "Mariestad (1493)",
                "Lidköping (1494)",
                "Skara (1495)",
                "Skövde (1496)",
                "Hjo (1497)",
                "Tidaholm (1498)",
                "Falköping (1499)",
                "Kil (1715)",
                "Eda (1730)",
                "Torsby (1737)",
                "Storfors (1760)",
                "Hammarö (1761)",
                "Munkfors (1762)",
                "Forshaga (1763)",
                "Grums (1764)",
                "Årjäng (1765)",
                "Sunne (1766)",
                "Karlstad (1780)",
                "Kristinehamn (1781)",
                "Filipstad (1782)",
                "Hagfors (1783)",
                "Arvika (1784)",
                "Säffle (1785)",
                "Lekeberg (1814)",
                "Laxå (1860)",
                "Hallsberg (1861)",
                "Degerfors (1862)",
                "Hällefors (1863)",
                "Ljusnarsberg (1864)",
                "Örebro (1880)",
                "Kumla (1881)",
                "Askersund (1882)",
                "Karlskoga (1883)",
                "Nora (1884)",
                "Lindesberg (1885)",
                "Skinnskatteberg (1904)",
                "Surahammar (1907)",
                "Kungsör (1960)",
                "Hallstahammar (1961)",
                "Norberg (1962)",
                "Västerås (1980)",
                "Sala (1981)",
                "Fagersta (1982)",
                "Köping (1983)",
                "Arboga (1984)",
                "Vansbro (2021)",
                "Malung-Sälen (2023)",
                "Gagnef (2026)",
                "Leksand (2029)",
                "Rättvik (2031)",
                "Orsa (2034)",
                "Älvdalen (2039)",
                "Smedjebacken (2061)",
                "Mora (2062)",
                "Falun (2080)",
                "Borlänge (2081)",
                "Säter (2082)",
                "Hedemora (2083)",
                "Avesta (2084)",
                "Ludvika (2085)",
                "Ockelbo (2101)",
                "Hofors (2104)",
                "Ovanåker (2121)",
                "Nordanstig (2132)",
                "Ljusdal (2161)",
                "Gävle (2180)",
                "Sandviken (2181)",
                "Söderhamn (2182)",
                "Bollnäs (2183)",
                "Hudiksvall (2184)",
                "Ånge (2260)",
                "Timrå (2262)",
                "Härnösand (2280)",
                "Sundsvall (2281)",
                "Kramfors (2282)",
                "Sollefteå (2283)",
                "Örnsköldsvik (2284)",
                "Ragunda (2303)",
                "Bräcke (2305)",
                "Krokom (2309)",
                "Strömsund (2313)",
                "Åre (2321)",
                "Berg (2326)",
                "Härjedalen (2361)",
                "Östersund (2380)",
                "Nordmaling (2401)",
                "Bjurholm (2403)",
                "Vindeln (2404)",
                "Robertsfors (2409)",
                "Norsjö (2417)",
                "Malå (2418)",
                "Storuman (2421)",
                "Sorsele (2422)",
                "Dorotea (2425)",
                "Vännäs (2460)",
                "Vilhelmina (2462)",
                "Åsele (2463)",
                "Umeå (2480)",
                "Lycksele (2481)",
                "Skellefteå (2482)",
                "Arvidsjaur (2505)",
                "Arjeplog (2506)",
                "Jokkmokk (2510)",
                "Överkalix (2513)",
                "Kalix (2514)",
                "Övertorneå (2518)",
                "Pajala (2521)",
                "Gällivare (2523)",
                "Älvsbyn (2560)",
                "Luleå (2580)",
                "Piteå (2581)",
                "Boden (2582)",
                "Haparanda (2583)",
                "Kiruna (2584)"
              ]
            },
            "default": []
          },
          "country": {
            "title": "Countries",
            "type": "array",
            "description": "Numeric JobTech country codes or Swedish country names, for ads placed outside Sweden. Live sizes measured 2026-09-05: Sverige (199) 41,003 · Norge (155) 96 · Spanien (193) 42 · Danmark (44) 13.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "unspecifiedSwedenWorkplace": {
            "title": "Only ads with an unspecified Swedish workplace",
            "type": "boolean",
            "description": "Ads that say \"Sweden\" but name no municipality — 610 live, measured 2026-09-05. Remote-first and travelling roles cluster here. Setting this to false does nothing (the API ignores a false), so it is only ever sent when true.",
            "default": false
          },
          "abroad": {
            "title": "Include jobs outside Sweden",
            "type": "boolean",
            "description": "Also return the ads for work abroad — 243 live, measured 2026-09-05 — even while a Swedish municipality or region filter is set. Setting it to false does nothing (the API ignores a false).",
            "default": false
          },
          "position": {
            "title": "Centre point for a radius search",
            "type": "string",
            "description": "Latitude,longitude — e.g. `59.3293,18.0686` for central Stockholm. Pair it with a radius below. Territory-shaped lead lists (a franchise patch, a sales district) are what this is for.",
            "default": ""
          },
          "positionRadiusKm": {
            "title": "Radius from that point (km)",
            "type": "array",
            "description": "One or more radii in kilometres. Measured 2026-09-05 from central Stockholm (`59.3293,18.0686`): 10 km → 7,016 ads, 100 km → 12,652.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "occupationField": {
            "title": "Occupation fields",
            "type": "array",
            "description": "The 21 top-level occupation areas, OR-ed server-side. Live sizes measured 2026-09-05: Hälso- och sjukvård 5,961 · Försäljning/inköp/marknadsföring 4,326 · Administration/ekonomi/juridik 3,517 · Data/IT 2,642.",
            "items": {
              "type": "string",
              "enum": [
                "NYW6_mP6_vwf",
                "X82t_awd_Qyc",
                "RPTn_bxG_ExZ",
                "GazW_2TU_kJw",
                "6Hq3_tKo_V57",
                "MVqp_eS8_kDZ",
                "wTEr_CBC_bqh",
                "bh3H_Y3h_5eD",
                "apaJ_2ja_LuF",
                "j7Cq_ZJe_GkT",
                "ASGV_zcE_bWf",
                "yhCP_AqT_tns",
                "ScKy_FHB_7wT",
                "whao_Q6A_ScE",
                "9puE_nYg_crq",
                "E7hm_BLq_fqZ",
                "VuuL_7CH_adj",
                "Uuf1_GMh_Uvw",
                "kJeN_wmw_9wX",
                "PaxQ_o1G_wWH",
                "bH5L_uXD_ZAX"
              ],
              "enumTitles": [
                "Hälso- och sjukvård — Healthcare",
                "Administration, ekonomi, juridik — Administration, finance, law",
                "Försäljning, inköp, marknadsföring — Sales, purchasing, marketing",
                "Yrken med social inriktning — Social work",
                "Yrken med teknisk inriktning — Engineering and technology",
                "Pedagogik — Education and teaching",
                "Industriell tillverkning — Industrial manufacturing",
                "Chefer och verksamhetsledare — Managers and executives",
                "Data/IT — Data / IT",
                "Bygg och anläggning — Construction and civil engineering",
                "Transport, distribution, lager — Transport, distribution, warehousing",
                "Installation, drift, underhåll — Installation, operation, maintenance",
                "Hotell, restaurang, storhushåll — Hotel, restaurant, catering",
                "Sanering och renhållning — Cleaning and sanitation",
                "Kultur, media, design — Culture, media, design",
                "Säkerhet och bevakning — Security and surveillance",
                "Naturbruk — Agriculture, forestry, fishing",
                "Kropps- och skönhetsvård — Body and beauty care",
                "Naturvetenskap — Natural sciences",
                "Hantverk — Crafts",
                "Militära yrken — Military"
              ]
            },
            "default": []
          },
          "occupationGroup": {
            "title": "Occupation groups (SSYK codes or concept ids)",
            "type": "array",
            "description": "Narrower than a field. Accepts either the taxonomy concept id or the legacy SSYK code — both work (measured 2026-09-05: `Z6TY_xDf_Yup` and `9111` each return 962 ads for \"Städare\"). Codes come from taxonomy.jobtechdev.se. This value is sent to the API exactly as typed: unlike region, municipality or occupation field there is no table to resolve it against, so an unknown code comes back as HTTP 200 with ZERO rows rather than an error. The run logs a notice when this field is used, and names it again if the result is empty. Nothing is billed either way.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "occupationName": {
            "title": "Specific occupations (concept ids)",
            "type": "array",
            "description": "The narrowest occupation level — one named job title. Measured 2026-09-05: `KF64_AvU_S4N` (\"Städare\") returns 952 ads. Concept ids come from taxonomy.jobtechdev.se. Sent to the API exactly as typed — there is no table to resolve an unknown concept id against, so a bad value returns a clean, empty, successful search rather than an error. The run logs a notice when this field is used, and names it again if the result is empty.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "occupationCollection": {
            "title": "Occupation collections",
            "type": "array",
            "description": "Curated cross-cutting bundles maintained by Arbetsförmedlingen. Measured 2026-09-05: Bygg → 2,450 ads.",
            "items": {
              "type": "string",
              "enum": [
                "XouD_cCj_HZN",
                "8too_bEs_7NU",
                "sqvh_gTy_6Sj",
                "ja7J_P8X_YC9",
                "epLc_SkD_cbT",
                "UdVa_jRr_9DE"
              ],
              "enumTitles": [
                "Sjöfart — Maritime",
                "Kultur — Culture",
                "Legitimationsyrken inom hälso- och sjukvård — Licensed healthcare professions",
                "Bygg — Construction",
                "Yrkessamling, chefsyrken — Management occupations",
                "Yrkessamling, yrken utan krav på utbildning — Occupations with no education requirement"
              ]
            },
            "default": []
          },
          "skill": {
            "title": "Required competencies (concept ids)",
            "type": "array",
            "description": "Taxonomy competency codes. These tags are SPARSE — only 1.9% of ads carry `must_have.skills` (38 of the 2,000 newest ads, run BEjkBLIO8FBrVejcC, 2026-09-05) — so a skill filter returns a small, high-precision slice, never the whole occupation. Sent to the API exactly as typed, and it must be a taxonomy CONCEPT ID, not a word: `skill: \"Python\"` returned API total 0 on 2026-09-06 (run `Ldks48nqu3M7SXWN0`). There is no table to resolve a bad value against, so it comes back as a clean empty search — the run logs a notice when this field is used and names it again if the result is empty. Nothing is billed.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "language": {
            "title": "Required languages (concept ids)",
            "type": "array",
            "description": "Taxonomy language codes. Measured 2026-09-05: `NVxJ_hLg_TYS` (Engelska) → 2,758 ads. 12.6% of ads carry a language requirement (251 of the 2,000 newest ads, run BEjkBLIO8FBrVejcC, 2026-09-05). Sent to the API exactly as typed — a bad concept id returns a clean, empty, successful search rather than an error, so the run logs a notice when this field is used and names it again if the result is empty.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "employmentType": {
            "title": "Employment type",
            "type": "array",
            "description": "OR-ed server-side. Live sizes measured 2026-09-05: Vanlig anställning 33,987 · Behovsanställning · Tidsbegränsad · Vikariat · Sommarjobb 367. Note the taxonomy API lists only five of these seven; the two missing ones (including the largest) were recovered from the live corpus.",
            "items": {
              "type": "string",
              "enum": [
                "PFZr_Syz_cUq",
                "kpPX_CNN_gDU",
                "1paU_aCR_nGn",
                "sTu5_NBQ_udq",
                "gro4_cWF_6D7",
                "Jh8f_q9J_pbJ",
                "EBhX_Qm2_8eX"
              ],
              "enumTitles": [
                "Vanlig anställning — Regular employment",
                "Tillsvidareanställning (inkl. eventuell provanställning) — Permanent (incl. probation)",
                "Behovsanställning — On-demand / as-needed",
                "Tidsbegränsad anställning — Fixed-term",
                "Vikariat — Temporary cover (vikariat)",
                "Sommarjobb / feriejobb — Summer / holiday job",
                "Säsongsanställning — Seasonal"
              ]
            },
            "default": []
          },
          "worktimeExtent": {
            "title": "Full-time / part-time",
            "type": "array",
            "description": "Live sizes measured 2026-09-05: Heltid 30,494 · Deltid 6,313.",
            "items": {
              "type": "string",
              "enum": [
                "6YE1_gAC_R2G",
                "947z_JGS_Uk2"
              ],
              "enumTitles": [
                "Heltid — Full-time",
                "Deltid — Part-time"
              ]
            },
            "default": []
          },
          "parttimeMin": {
            "title": "Minimum scope of work (%)",
            "minimum": 0,
            "maximum": 100,
            "type": "integer",
            "description": "Filters on the ad's stated employment-percentage band. Measured 2026-09-05: minimum 50 → 30,499 ads."
          },
          "parttimeMax": {
            "title": "Maximum scope of work (%)",
            "minimum": 0,
            "maximum": 100,
            "type": "integer",
            "description": "Pair with the minimum to isolate genuine part-time roles. Measured 2026-09-05: 0–50 → 2 ads, 50–100 → 30,499."
          },
          "duration": {
            "title": "Employment duration",
            "type": "array",
            "description": "How long the job lasts. Live sizes measured 2026-09-05: Tills vidare 28,076 · 6 månader eller längre 5,042. As with employment type, the taxonomy API is missing a value the corpus actually uses; both lists are merged here.",
            "items": {
              "type": "string",
              "enum": [
                "a7uU_j21_mkL",
                "qQUd_4qe_NDT",
                "9RGe_UxD_FZw",
                "gJRb_akA_95y",
                "Xj7x_7yZ_jEn",
                "Sy9J_aRd_ALx",
                "cAQ8_TpB_Tdv"
              ],
              "enumTitles": [
                "Tills vidare — Until further notice (permanent)",
                "6 månader eller längre — 6 months or longer",
                "12 månader – upp till 2 år — 12 months – up to 2 years",
                "6 månader – upp till 12 månader — 6 months – up to 12 months",
                "3 månader – upp till 6 månader — 3 months – up to 6 months",
                "11 dagar – upp till 3 månader — 11 days – up to 3 months",
                "Upp till 10 dagar — Up to 10 days"
              ]
            },
            "default": []
          },
          "experience": {
            "title": "Experience required",
            "enum": [
              "",
              "true",
              "false"
            ],
            "type": "string",
            "description": "Live sizes measured 2026-09-05: requires experience 38,282 · does NOT require experience 2,964. Leave on \"Any\" to ignore.",
            "default": ""
          },
          "drivingLicenseRequired": {
            "title": "Driving licence required",
            "enum": [
              "",
              "true",
              "false"
            ],
            "type": "string",
            "description": "Live sizes measured 2026-09-05: licence required 13,551 · not required 27,695.",
            "default": ""
          },
          "drivingLicense": {
            "title": "Specific driving-licence classes",
            "type": "array",
            "description": "Live sizes measured 2026-09-05: B 6,384 (by far the most common) · C · CE · D. Only 16.2% of ads name a specific class (367 of the 2,268-ad 2026-09-04 day sweep, run `iDb78sXSeDHqPH5gN`, 2026-09-07).",
            "items": {
              "type": "string",
              "enum": [
                "4HpY_e2U_TUH",
                "bneY_V3g_Kog",
                "Eybh_j3s_SqF",
                "hK8X_cX9_5P4",
                "VTK8_WRx_GcM",
                "ftCQ_gFu_L4b",
                "bcFd_Vkt_KXL",
                "swP6_psb_FCB",
                "xRum_Wtt_T1e",
                "BKCx_hST_Pcm",
                "zZu8_iZ9_wMH",
                "5L6k_p4w_8Wu",
                "qZ2E_JSL_xAq",
                "hK1a_wsQ_4UG",
                "6se7_AdD_nGw"
              ],
              "enumTitles": [
                "AM (4HpY_e2U_TUH)",
                "A1 (bneY_V3g_Kog)",
                "A2 (Eybh_j3s_SqF)",
                "A (hK8X_cX9_5P4)",
                "B (VTK8_WRx_GcM)",
                "Utökad B (ftCQ_gFu_L4b)",
                "BE (bcFd_Vkt_KXL)",
                "C1 (swP6_psb_FCB)",
                "C1E (xRum_Wtt_T1e)",
                "C (BKCx_hST_Pcm)",
                "CE (zZu8_iZ9_wMH)",
                "D1 (5L6k_p4w_8Wu)",
                "D1E (qZ2E_JSL_xAq)",
                "D (hK1a_wsQ_4UG)",
                "DE (6se7_AdD_nGw)"
              ]
            },
            "default": []
          },
          "remote": {
            "title": "Remote-friendly ads",
            "enum": [
              "",
              "true",
              "false"
            ],
            "type": "string",
            "description": "Phrase-matched by Arbetsförmedlingen over the ad text. Live sizes measured 2026-09-05: likely remote-friendly 675 · not 40,571. This is the filter that actually works for remote work — see the honest note on \"Workplace model\" below.",
            "default": ""
          },
          "workplaceModel": {
            "title": "Workplace model (⚠ measured no-op)",
            "type": "array",
            "description": "**Honest warning, re-measured 2026-09-05:** this parameter does not currently filter anything. \"Arbete på plats\" (on-site) returns the ENTIRE corpus unfiltered — the on-site total equalled the unfiltered total exactly, 41,246 = 41,246, and \"Distansarbete\" and \"Hybridarbete\" each return 0 — every live ad carries the on-site concept. It is exposed because the API accepts it and Arbetsförmedlingen may start populating it, and it is passed through exactly as you set it, but for remote work use \"Remote-friendly ads\" above instead.",
            "items": {
              "type": "string",
              "enum": [
                "ZxwM_gtz_n61",
                "L4T8_Z9y_DkN",
                "Vbpe_rGe_vCW"
              ],
              "enumTitles": [
                "Arbete på plats — On-site",
                "Distansarbete — Remote",
                "Hybridarbete — Hybrid"
              ]
            },
            "default": []
          },
          "trainee": {
            "title": "Trainee positions",
            "enum": [
              "",
              "true",
              "false"
            ],
            "type": "string",
            "description": "Phrase-matched. 13 live ads — genuinely rare, so expect a very small result set.",
            "default": ""
          },
          "apprenticeship": {
            "title": "Apprenticeships (lärling)",
            "enum": [
              "",
              "true",
              "false"
            ],
            "type": "string",
            "description": "Phrase-matched. 19 live ads.",
            "default": ""
          },
          "openForAll": {
            "title": "\"Öppen för alla\" ads",
            "enum": [
              "",
              "true",
              "false"
            ],
            "type": "string",
            "description": "Ads that explicitly welcome every applicant regardless of background. 1,456 live, measured 2026-09-05.",
            "default": ""
          },
          "franchise": {
            "title": "Franchise opportunities",
            "enum": [
              "",
              "true",
              "false"
            ],
            "type": "string",
            "description": "Phrase-matched. 11 live ads.",
            "default": ""
          },
          "hireWorkPlace": {
            "title": "Chair / space rental ads",
            "enum": [
              "",
              "true",
              "false"
            ],
            "type": "string",
            "description": "Ads renting out a chair or a workspace rather than employing. 6 live ads — salons and clinics.",
            "default": ""
          },
          "label": {
            "title": "Ad labels",
            "type": "array",
            "description": "Swedish employment-subsidy programme labels. Live sizes measured 2026-09-05: `nystartsjobb` 1,011 · `etableringsjobb` 343. A subsidised hire is a distinct buying signal. Sent to the API exactly as typed — a bad value returns a clean, empty, successful search rather than an error, so the run logs a notice when this field is used and names it again if the result is empty.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "employer": {
            "title": "Employers (name or organisation number)",
            "type": "array",
            "description": "Account-based prospecting: watch named companies. Accepts a company name OR a Swedish organisation number (digits only — spaces and dashes are stripped for you).\n\nThe API's own `employer=` parameter is a FULL-TEXT match over the employer name, not an exact one, so on its own it is useless here: measured 2026-09-06, `Norrköpings kommun` matched 4,061 ads — every municipality in Sweden, because *kommun* alone matches 4,234 — while that employer itself had 43; `Spotify AB` matched 29,291 of 41,232 while `Spotify` matched 0; and a company that does not exist, `Kommun som inte finns AB`, matched 33,533.\n\nThis actor therefore sends it to narrow the crawl and then RE-CHECKS every row against the name or organisation number you typed, dropping the rest BEFORE billing — you are never charged for another company's ad. Proven 2026-09-07: `Norrköpings kommun` delivered and billed 22 rows, every one of them that employer (run `tnSYbgB5S1WhKyEaA`); the non-existent company delivered and billed 0 (run `qIiduKkmWRb4D8RcT`). Expect a large \"dropped by filters\" count in the log — that is the filter working. An organisation number is matched exactly and is the more reliable input; a name that shares a common word with thousands of others can hit the API's 2,100-row per-query ceiling before the scan finishes, which the run reports rather than passing off as complete.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "publishedWithinDays": {
            "title": "Published within the last N days",
            "minimum": 0,
            "maximum": 3650,
            "type": "integer",
            "description": "The \"new ads since yesterday\" lever, and the cheapest way to run this as a daily monitor. Sweden publishes roughly 2,100–2,500 new ads per WEEKDAY (measured 2026-09-05: 2,478 published on 09-01, 2,131 on 09-02, 2,115 on 09-03, 2,299 on 09-04), which is already over the API's per-query ceiling — so pair anything longer than a day with \"Sweep the whole corpus\". The weekend is far quieter: the trailing-24h window measured on Saturday 2026-09-05 held 165 ads. TIMEZONE: date filters are evaluated in UTC, while `publication_date` comes back in Stockholm local time (+01:00 winter, +02:00 summer), so about 1% of a one-day window's rows carry the neighbouring local date — measured 2026-09-06, a 2026-09-03..2026-09-04 request delivered 2,095 rows dated 09-03 and 20 dated 09-04 (run `Rcl2sclubWqOAgXcL`). The run logs this whenever a date filter is set."
          },
          "publishedAfter": {
            "title": "Published after (exact)",
            "type": "string",
            "description": "ISO date or datetime, e.g. `2026-09-01` or `2026-09-01T08:00:00`. Verified as a REAL server-side filter, not a label — measured 2026-09-05: `before 2026-01-01` returns 7 ads and a trailing six-hour window returned 46. TIMEZONE: date filters are evaluated in UTC, while `publication_date` comes back in Stockholm local time (+01:00 winter, +02:00 summer), so about 1% of a one-day window's rows carry the neighbouring local date — measured 2026-09-06, a 2026-09-03..2026-09-04 request delivered 2,095 rows dated 09-03 and 20 dated 09-04 (run `Rcl2sclubWqOAgXcL`). The run logs this whenever a date filter is set.",
            "default": ""
          },
          "publishedBefore": {
            "title": "Published before (exact)",
            "type": "string",
            "description": "Pairs with \"Published after\" for a backfill window. The live corpus runs from 2025-07-09 to today (oldest live ad confirmed 2026-09-05: 2025-07-09T15:34:40). TIMEZONE: date filters are evaluated in UTC, while `publication_date` comes back in Stockholm local time (+01:00 winter, +02:00 summer), so about 1% of a one-day window's rows carry the neighbouring local date — measured 2026-09-06, a 2026-09-03..2026-09-04 request delivered 2,095 rows dated 09-03 and 20 dated 09-04 (run `Rcl2sclubWqOAgXcL`). The run logs this whenever a date filter is set.",
            "default": ""
          },
          "applicationDeadlineAfter": {
            "title": "Application deadline after",
            "type": "string",
            "description": "Applied over the rows (the API has no deadline filter). Use it to keep only ads that are still genuinely open. Evaluated in UTC against an `application_deadline` the API returns in Stockholm local time, so a deadline within an hour of midnight can fall on either side of the boundary.",
            "default": ""
          },
          "applicationDeadlineBefore": {
            "title": "Application deadline before",
            "type": "string",
            "description": "Applied over the rows. Closing-soon ads are the urgent end of a recruitment-agency call list. Evaluated in UTC against an `application_deadline` the API returns in Stockholm local time, so a deadline within an hour of midnight can fall on either side of the boundary.",
            "default": ""
          },
          "adUrls": {
            "title": "Specific ad IDs or URLs",
            "type": "array",
            "description": "Re-check named ads instead of searching — paste `https://arbetsformedlingen.se/platsbanken/annonser/31441923` or just `31441923`. Each ad is one billed row, exactly as in a search. A removed ad returns nothing and is NOT billed; the log names it — \"Not delivered and NOT billed: <id>\" — so a mixed batch tells you which ids expired.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "onlyWithContact": {
            "title": "Only ads with any e-mail or phone (application inbox counts)",
            "type": "boolean",
            "description": "Keep only ads carrying a contact e-mail, a contact phone number, OR an application e-mail such as jobb@foretaget.se. Measured 2026-09-07 on two differently-shaped runs: 57.8% qualify over run `iDb78sXSeDHqPH5gN`, a 2,268-ad sweep of the full day 2026-09-04, 59.6% over run `jP1jGT0bR51D6UdWf`, the 2,000 most recently published ads — call it 58-60%. About a quarter of those qualify on the application inbox alone, which is a role address, not a named person. For a call list of actual humans use \"Only ads with a named contact's e-mail or phone\" below instead. Applied BEFORE billing — you never pay for a row this drops.",
            "default": false
          },
          "onlyWithNamedContact": {
            "title": "Only ads with a named contact's e-mail or phone",
            "type": "boolean",
            "description": "Strictest reachability filter: keep only ads where the ad itself lists a contact person's e-mail or phone (the `contact_email` / `contact_phone` columns) — a generic application inbox never qualifies. Measured 2026-09-07: 47.5% qualify over run `iDb78sXSeDHqPH5gN`, a 2,268-ad sweep of the full day 2026-09-04, 45.1% over run `jP1jGT0bR51D6UdWf`, the 2,000 most recently published ads; `contact_email` 41.1% / 39.8% and `contact_phone` 35.6% / 32.0%. Applied BEFORE billing — you never pay for a row this drops.",
            "default": false
          },
          "onlyWithOrgNumber": {
            "title": "Only ads with an organisation number",
            "type": "boolean",
            "description": "Keep only rows carrying the Swedish organisation number — the registry key that joins a lead to a real company record at Bolagsverket or SCB. Measured 2026-09-07: 99.4% fill over run `iDb78sXSeDHqPH5gN`, a 2,268-ad sweep of the full day 2026-09-04 and 99.2% over run `jP1jGT0bR51D6UdWf`, the 2,000 most recently published ads, so this drops very little and guarantees a joinable list.",
            "default": false
          },
          "onlyWithoutAts": {
            "title": "Only employers with NO apply link (ATS displacement list)",
            "type": "boolean",
            "description": "Keep only the ads with no application URL at all — employers who are hiring but have no applicant-tracking system in the loop. For an HR-tech or recruitment-software seller this is the highest-intent slice in the whole corpus. It is also the slice that moves most between samples: measured 2026-09-07, 11.9% over run `iDb78sXSeDHqPH5gN`, a 2,268-ad sweep of the full day 2026-09-04 but 17.0% over run `jP1jGT0bR51D6UdWf`, the 2,000 most recently published ads, because a rolling 2,000 reaches back across the weekend. Budget 12-17%, not a fixed figure.",
            "default": false
          },
          "atsVendorFilter": {
            "title": "Only employers on these ATS vendors",
            "type": "array",
            "description": "Competitive displacement targeting: keep only employers whose apply link resolves to a named ATS. Measured 2026-09-07 over run `iDb78sXSeDHqPH5gN`, a 2,268-ad sweep of the full day 2026-09-04 — Varbi 317, Visma Recruit 175, ReachMee 113, Recman 112, Teamtailor 74, Bullhorn 65, EasyApply 46, Ponty 40, Avaron 29, Talentech 29, PowerInit 27, Workday 23; 25 of the 49 recognised vendors appeared and 50.2% of ads resolved to a named vendor. The same parser over run `jP1jGT0bR51D6UdWf`, the 2,000 most recently published ads the same day resolved 40.5%, so budget 40-50%, not 50%. A rare vendor is a small slice of a big corpus: pair it with \"Sweep the whole corpus\" or the 2,100-row per-query ceiling will stop the scan long before it has examined every ad — the run warns you when that happens, and says so again if the result is zero rows.",
            "items": {
              "type": "string",
              "enum": [
                "Ashby",
                "Avaron",
                "BambooHR",
                "Bullhorn",
                "CareerHub",
                "Cinode",
                "EasyApply",
                "Emply",
                "Ena Portal",
                "Greenhouse",
                "Hailey HR",
                "Heltid.se",
                "IBM Kenexa",
                "Intelliplan",
                "Jobbet.se",
                "Jobbland",
                "Jobtip",
                "Jobvite",
                "Jobylon",
                "Join",
                "Ledigajobb.se",
                "Lever",
                "MyNetwork",
                "Offentligajobb",
                "Oracle Taleo",
                "Personio",
                "Pinpoint",
                "Ponty",
                "PowerInit",
                "ReachMee",
                "Recman",
                "Recruitee",
                "Recruto",
                "Refapp",
                "SAP SuccessFactors",
                "SmartRecruiters",
                "TalentAdore",
                "TalentLyft",
                "Talentech",
                "Teamtailor",
                "Varbi",
                "Verama",
                "Visma Recruit",
                "Webcruiter",
                "Wopify",
                "Workable",
                "Workbuster",
                "Workday",
                "iCIMS"
              ]
            },
            "default": []
          },
          "excludeStaffingAgencies": {
            "title": "Exclude staffing and recruitment agencies",
            "type": "boolean",
            "description": "Drops ads whose employer is itself a recruiter or staffing firm, so an agency buying this list gets END CLIENTS rather than its own competitors. Matched on the employer name against a 50-word Swedish agency vocabulary (bemanning, rekrytering, Manpower, Adecco, Randstad, StudentConsulting, …). Every row also carries `is_staffing_agency` so you can audit the call.",
            "default": false
          },
          "dedupeByEmployer": {
            "title": "One row per employer",
            "type": "boolean",
            "description": "Collapse to a single row per organisation number for a COMPANY-level list rather than a vacancy-level one. Measured 2026-09-07: run `iDb78sXSeDHqPH5gN`, a 2,268-ad sweep of the full day 2026-09-04 came from 951 distinct organisations (a 58% cut) and run `jP1jGT0bR51D6UdWf`, the 2,000 most recently published ads from 918 (a 54% cut) — and you are only billed for the rows you keep.",
            "default": false
          },
          "titleIncludeAny": {
            "title": "Ad title must contain any of",
            "type": "array",
            "description": "Applied over the rows, diacritic-insensitively, so `sokerska` matches `söterska`. Use it to tighten a broad occupation filter without changing the query.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "titleExcludeAny": {
            "title": "Ad title must NOT contain any of",
            "type": "array",
            "description": "Applied over the rows. Handy for stripping `praktik`, `sommarjobb` or `timvikarie` out of a permanent-hire list.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "descriptionIncludeAny": {
            "title": "Ad text must contain any of",
            "type": "array",
            "description": "Applied over the rows against the full ad text (filled on 2,268 of 2,268 rows — run `iDb78sXSeDHqPH5gN`, a 2,268-ad sweep of the full day 2026-09-04, 2026-09-07). This is where technographic signals live — a named tool, a certification, a system the employer says they run. A rare phrase over a large corpus needs \"Sweep the whole corpus\" on, or the 2,100-row per-query ceiling stops the scan before it has read every ad.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "outputShape": {
            "title": "Output shape",
            "enum": [
              "full",
              "lead",
              "minimal"
            ],
            "type": "string",
            "description": "`Full` = every mapped field (~60 columns). `Lead` = the CRM spine only: employer, organisation number, contact, apply link, ATS vendor, location, occupation and dates. `Minimal` = the 21-field identity and reachability core.",
            "default": "full"
          },
          "outputFieldLabels": {
            "title": "Label language",
            "enum": [
              "sv",
              "en",
              "both"
            ],
            "type": "string",
            "description": "Arbetsförmedlingen publishes every label in Swedish. `English` swaps the CLOSED vocabularies (occupation field, employment type, working hours, duration, workplace model, pay structure) for an English gloss; `Both` adds them as extra `_en` columns. Place names, specific occupation names and the ad text always stay Swedish — machine-translating 290 municipality names would be a fabrication, not data.",
            "default": "sv"
          },
          "includeDescriptionText": {
            "title": "Include the full ad text",
            "type": "boolean",
            "description": "The plain-text ad body, filled on 2,268 of 2,268 rows (run `iDb78sXSeDHqPH5gN`, a 2,268-ad sweep of the full day 2026-09-04, 2026-09-07). On by default — it is where contact e-mail addresses, named systems and hiring context live. Turn it off for a slim CRM import.",
            "default": true
          },
          "includeDescriptionHtml": {
            "title": "Include the HTML ad text",
            "type": "boolean",
            "description": "The formatted `text_formatted` body. Off by default — it roughly doubles the row size and most pipelines want the plain text.",
            "default": false
          },
          "includeRawTaxonomyIds": {
            "title": "Include taxonomy concept ids",
            "type": "boolean",
            "description": "Adds the `*_concept_id` columns beside every label, so you can join to taxonomy.jobtechdev.se or feed the ids straight back into this actor's own filters.",
            "default": false
          },
          "includeRawJson": {
            "title": "Attach the raw API object",
            "type": "boolean",
            "description": "Adds `raw`, the untouched JobTech hit, for pipelines that would rather map the fields themselves.",
            "default": false
          },
          "sort": {
            "title": "Sort",
            "enum": [
              "pubdate-desc",
              "pubdate-asc",
              "applydate-desc",
              "applydate-asc",
              "relevance"
            ],
            "type": "string",
            "description": "`Newest first` is the right choice for lead work and for monitoring, and it is what makes contiguous paging safe here — measured 2026-09-07 (run `jP1jGT0bR51D6UdWf`): 2,000 rows across 20 pages, 2,000 unique ids, ZERO duplicates. `Relevance` only makes sense with a search keyword.",
            "default": "pubdate-desc"
          },
          "maxItems": {
            "title": "Max ads to return (0 = no cap)",
            "minimum": 0,
            "type": "integer",
            "description": "Hard cap on BILLED rows across the whole run — your budget guard. Counts rows DELIVERED and is enforced before delivery, so you are never charged past it, and paging continues past filtered rows until the cap is filled. If the run ends short of the cap the log says whether the source was exhausted or the API's 2,100-row per-query ceiling truncated the scan.",
            "default": 100
          },
          "maxResultsPerQuery": {
            "title": "Max ads per search keyword (0 = no cap)",
            "minimum": 0,
            "type": "integer",
            "description": "A per-keyword cap when you run several searches at once, so one broad keyword cannot eat the whole budget. Counts rows DELIVERED, not rows read: paging continues past ads your client-side filters drop until the cap is filled or the keyword runs out of ads, which is the same rule \"Max ads to return\" follows and the same thing you are billed for. If a keyword ends short of its cap the log says which — the source was exhausted (a complete answer), or the API's 2,100-row per-query ceiling stopped it (turn on \"Sweep the whole corpus\").",
            "default": 0
          },
          "resultsPerPage": {
            "title": "Rows per API request",
            "minimum": 1,
            "maximum": 100,
            "type": "integer",
            "description": "The API accepts 1–100; 101 is HTTP 400. 100 is fastest and is the default — lower it only when you want a small run to stop precisely on a cap.",
            "default": 100
          },
          "startOffset": {
            "title": "Start at offset",
            "minimum": 0,
            "maximum": 2000,
            "type": "integer",
            "description": "Skip the first N rows of a query. The API accepts 0-2000; 2001 is HTTP 400. If N lands past the end of the result set the run returns zero rows and says exactly that, naming N and the number of matching ads — it does not report an empty search.",
            "default": 0
          },
          "fullCorpusMode": {
            "title": "Sweep the whole corpus (auto-partition)",
            "type": "boolean",
            "description": "**Turn this on for any run that should return more than 2,100 ads.** A single API query can reach at most 2,100 rows (limit ≤ 100, offset ≤ 2000), and a one-DAY partition is already too big — measured 2026-09-05: 2,478 ads published on 09-01, 2,131 on 09-02, 2,115 on 09-03 and 2,299 on 09-04, so a day-window crawler silently drops ads on a green run. This mode probes each publication-date window with a free `limit=0` call, reads the true total, and bisects the window until every slice fits under the ceiling. A whole-corpus sweep takes roughly 40–80 requests.",
            "default": false
          },
          "deduplicate": {
            "title": "Deduplicate on the ad id",
            "type": "boolean",
            "description": "On (default): each ad is delivered and BILLED at most once, even when several of your search keywords or date windows return it. Partitioned sweeps overlap by design, so leaving this on is what stops you paying twice for one ad.",
            "default": true
          },
          "stateKey": {
            "title": "State key (incremental monitoring)",
            "type": "string",
            "description": "Name this watch (e.g. `sthlm-nurses`) and the actor remembers every ad id it has delivered under that name in a NAMED key-value store. On the next run with the same key, ads already seen are dropped BEFORE delivery — so a daily monitor only ever bills you for genuinely new vacancies. Leave empty for a one-off scrape.",
            "default": ""
          },
          "proxyConfiguration": {
            "title": "Proxy",
            "type": "object",
            "description": "Apify DATACENTER proxies are enough — the full escalation ladder was measured and all four tiers returned identical bytes: direct 1,449 ms, datacenter 1,644 ms, residential SE 1,666 ms, residential US 2,849 ms. Residential is 1.7x slower for no benefit; do not pay for it. The API is open Swedish government data and is not geo-blocked.",
            "default": {
              "useApifyProxy": true
            }
          },
          "proxyCountry": {
            "title": "Pin the proxy to a country",
            "type": "string",
            "description": "Optional two-letter code. Measured: SE and US return byte-identical responses, so leave this empty unless your own network policy requires a pin.",
            "default": ""
          },
          "maxConcurrency": {
            "title": "Concurrent requests",
            "minimum": 1,
            "maximum": 10,
            "type": "integer",
            "description": "Only used when fetching specific ad IDs. Search paging is sequential on purpose: this is a public government API, and one pinned session paging politely measured 100% success over 20 pages (2,000 rows, run `jP1jGT0bR51D6UdWf`, 2026-09-07).",
            "default": 3
          },
          "maxRequestRetries": {
            "title": "Retries per request",
            "minimum": 0,
            "maximum": 10,
            "type": "integer",
            "description": "Keep this at 3 or more. The API itself never rate-limited across ~200 probe calls; the residual failure is the Apify proxy tunnel dropping a large response (ECONNRESET), and a retry clears it.",
            "default": 3
          },
          "requestTimeoutSecs": {
            "title": "Request timeout (seconds)",
            "minimum": 5,
            "maximum": 300,
            "type": "integer",
            "description": "A 100-row page measured 2–9 s through a datacenter proxy depending on load. 60 s leaves room for a slow tunnel.",
            "default": 60
          },
          "requestDelayMs": {
            "title": "Delay between requests (ms)",
            "minimum": 0,
            "maximum": 10000,
            "type": "integer",
            "description": "Politeness pause between paged requests against a public government API.",
            "default": 250
          }
        }
      },
      "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
                  }
                }
              },
              "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
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}