{
  "openapi": "3.0.1",
  "info": {
    "title": "APEC Jobs Scraper — French Cadre Jobs, Employer Type & NAF",
    "description": "Scrape apec.fr, France's executive job board (~88,000 live offers). Filter by keyword, place, salary, contract, sector, métier, remote and 20 more. Every row names the employer TYPE - direct employer, recruitment cabinet, agency, ESN or resold job-board feed - plus its NAF industry code.",
    "version": "0.1",
    "x-build-id": "kQiTuKDaXizFkCfak"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/scrapersdelight~apec-jobs-scraper/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-scrapersdelight-apec-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~apec-jobs-scraper/runs": {
      "post": {
        "operationId": "runs-sync-scrapersdelight-apec-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~apec-jobs-scraper/run-sync": {
      "post": {
        "operationId": "run-sync-scrapersdelight-apec-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": {
          "keywords": {
            "title": "Keywords",
            "type": "array",
            "description": "Free-text search terms, one crawl per term. Measured corpus sizes on 2026-09-04: \"developpeur\" 3,295 offers · \"commercial\" 23,324 · \"ingenieur\" 21,148. Leave empty to crawl the whole APEC board (~88,000 live offers) newest first.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "jobTitles": {
            "title": "Job titles (resolved to APEC métiers)",
            "type": "array",
            "description": "Fuzzy job titles. Each one is sent through APEC's own métier autocomplete and expanded into up to 5 canonical métier labels, which are then searched as keywords — so \"developpeur\" also finds \"Business developer\" and \"Développeur foncier\" the way the site's own search box does. Use \"Keywords\" instead if you want the raw term and nothing else.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "searchUrls": {
            "title": "APEC search URLs",
            "type": "array",
            "description": "Paste apec.fr search URLs — e.g. https://www.apec.fr/candidat/recherche-emploi.html/emploi?lieux=75&typesContrat=101888. Their query parameters map 1:1 onto the search API, so the URL is parsed, never fetched. Any filter the URL sets wins over the equivalent field below; the rest of your settings still apply.",
            "default": [],
            "items": {
              "type": "object",
              "required": [
                "url"
              ],
              "properties": {
                "url": {
                  "type": "string",
                  "title": "URL of a web page",
                  "format": "uri"
                }
              }
            }
          },
          "offerNumbers": {
            "title": "Specific offer numbers (re-check mode)",
            "type": "array",
            "description": "APEC offer numbers such as `179364344W` (or their bare numeric ids). Each one is looked up exactly — the search API answers with totalCount 1 — so you can feed yesterday's numbers back in to see which postings are still live. Offers that have gone are listed in the run log.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "excludeKeywords": {
            "title": "Exclude keywords",
            "type": "array",
            "description": "Drop offers whose title or description snippet contains any of these (case-insensitive). APEC has no server-side NOT, so this runs on our side — excluded rows are never billed.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "location": {
            "title": "Locations (plain names)",
            "type": "array",
            "description": "Departments (\"Paris\", \"Rhône\", \"75\", \"69\"), regions (\"Ile-de-France\", \"Occitanie\"), countries (\"Allemagne\", \"Belgique\"), \"International\", or communes (\"Bordeaux\", \"Lyon 01\"). Departments, regions and countries resolve instantly from the bundled APEC nomenclature; a commune is resolved through APEC's own place autocomplete. Verified live: 75 → 6,963 Paris offers, 69 → 5,649, Ile-de-France → 21,932, Bordeaux → 1,229, Allemagne → 18.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "lieuxIds": {
            "title": "Raw APEC lieu ids (power user)",
            "type": "array",
            "description": "Escape hatch for the same filter: department ids (75), region ids (711 = Ile-de-France), commune ids (590711 = Paris 1er) or country ids (99109 = Allemagne). Merged with the names above.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "radiusKm": {
            "title": "Radius (km) around a point",
            "minimum": 0,
            "maximum": 1000,
            "type": "integer",
            "description": "Keep only offers within this many km of the centre below. APEC's own geo parameter does NOT filter at any distance (measured: 87,736 offers at 10 km, 50 km and 300 km alike) — it only orders results by distance. So the crawl sets the point, sorts by distance ascending, and walks outward until it passes your radius. 0 = off.",
            "default": 0
          },
          "centerLat": {
            "title": "Centre latitude",
            "type": "string",
            "description": "Latitude of the radius centre, e.g. 45.75 for Lyon. Required for the radius filter.",
            "default": ""
          },
          "centerLng": {
            "title": "Centre longitude",
            "type": "string",
            "description": "Longitude of the radius centre, e.g. 4.85 for Lyon. Required for the radius filter.",
            "default": ""
          },
          "onlyGeolocated": {
            "title": "Only offers with coordinates",
            "type": "boolean",
            "description": "Keep only offers APEC can place on a map (measured: 84,804 of 87,843). Useful when you are going to plot or distance-sort the results downstream.",
            "default": false
          },
          "employerType": {
            "title": "Employer type",
            "type": "array",
            "description": "THE differentiating filter, and no rival APEC actor exposes it. The corpus partitions cleanly: Entreprise 18,308 · Cabinet de recrutement 17,108 · Agence d'emploi 9,670 · SSII/ESN 4,826 · Partenaire (job-board feed resold onto APEC) 37,858. Leave empty to use the \"exclude aggregator feeds\" switch below.",
            "items": {
              "type": "string",
              "enum": [
                "143684",
                "143685",
                "143686",
                "143687",
                "143706"
              ],
              "enumTitles": [
                "Entreprise — the hiring company itself",
                "Cabinet de recrutement — a search firm",
                "Agence d'emploi — a staffing agency",
                "SSII / ESN — an IT-services company",
                "Partenaire — a partner job-board feed resold onto APEC"
              ]
            },
            "default": []
          },
          "excludeAggregatorFeeds": {
            "title": "Exclude partner-aggregator feeds",
            "type": "boolean",
            "description": "43% of APEC is resale from partner job boards. In a blind 300-row sample the top \"companies\" were Hellowork (145) and Meteojob (58) — i.e. two thirds of rows named a job board, not the employer. On by default so \"which company is recruiting\" actually means something. Ignored when you pick employer types explicitly.",
            "default": true
          },
          "stampEmployerClass": {
            "title": "Stamp every row with its employer class",
            "type": "boolean",
            "description": "APEC never returns the employer class on the row, only as a filter. With this on, the crawl runs one query slice per class and stamps each row — costing no extra requests, because the classes partition the corpus (18,308 + 17,108 + 9,670 + 4,826 = 49,912, exactly what the four return together). Turn it off for a single combined query with a blank `employerClass` column.",
            "default": true
          },
          "contractTypes": {
            "title": "Contract types",
            "type": "array",
            "description": "Measured live: CDI 81,393 · CDD 5,113 · Intérim 820 · Alternance 517. The level-2 entries (apprentissage, professionnalisation, mission d'intérim, stage, CDI intérimaire) are narrower slices of the same facet.",
            "items": {
              "type": "string",
              "enum": [
                "101888",
                "101887",
                "20053",
                "597137",
                "597138",
                "597139",
                "597140",
                "101930",
                "597141",
                "101889",
                "597171"
              ],
              "enumTitles": [
                "CDI",
                "CDD",
                "Alternance",
                "CDD - Alternance - Contrat d'apprentissage",
                "CDD - Alternance - Contrat de professionnalisation",
                "CDI - Alternance - Contrat d'apprentissage",
                "CDI - Alternance - Contrat de professionnalisation",
                "Intérim",
                "CDI interimaire",
                "Mission d'intérim",
                "Stage"
              ]
            },
            "default": []
          },
          "jobFunctions": {
            "title": "Job functions (métier families)",
            "type": "array",
            "description": "APEC's own job-family tree: 10 families and their 46 sub-functions. Measured: Commercial/Marketing 19,089 · Administration des ventes et SAV 1,253. Several are OR-ed together.",
            "items": {
              "type": "string",
              "enum": [
                "101828",
                "101782",
                "101783",
                "101784",
                "101785",
                "101786",
                "101787",
                "101788",
                "101789",
                "101829",
                "101790",
                "101791",
                "101792",
                "101793",
                "101830",
                "101794",
                "101795",
                "101831",
                "101796",
                "101797",
                "101798",
                "101799",
                "101800",
                "101832",
                "101801",
                "101802",
                "101803",
                "101804",
                "101805",
                "101806",
                "101833",
                "101807",
                "101808",
                "101809",
                "101810",
                "101811",
                "101812",
                "101813",
                "101834",
                "101814",
                "101815",
                "101816",
                "101835",
                "101817",
                "101818",
                "101819",
                "101820",
                "101836",
                "101821",
                "101837",
                "101822",
                "101823",
                "101824",
                "101825",
                "101826",
                "101827"
              ],
              "enumTitles": [
                "Commercial, Marketing",
                "— Administration des ventes et SAV",
                "— Chargé d'affaires, technico-commercial",
                "— Commercial",
                "— Commerce international",
                "— Direction commerciale et marketing",
                "— Direction régionale et d'agence",
                "— Marketing",
                "— Ventes en magasin",
                "Communication, Création",
                "— Communication",
                "— Création",
                "— Documentation, rédaction technique",
                "— Journalisme, édition",
                "Direction d'entreprise",
                "— Adjoint, conseil de direction",
                "— Direction générale",
                "Etudes, Recherche et Développement",
                "— Conception, recherche",
                "— Direction recherche et développement",
                "— Etudes socio-économiques",
                "— Projets scientifiques et techniques",
                "— Test, essai, validation, expertise",
                "Gestion, Finance, Administration",
                "— Administration, gestion, organisation",
                "— Comptabilité",
                "— Contrôle de gestion, audit",
                "— Direction gestion, finance",
                "— Droit, fiscalité",
                "— Finance, trésorerie",
                "Informatique",
                "— Direction informatique",
                "— Exploitation, maintenance informatique",
                "— Informatique de gestion",
                "— Informatique industrielle",
                "— Informatique web, sites et portails Internet",
                "— Maîtrise d'ouvrage et fonctionnel",
                "— Système, réseaux, données",
                "Production Industrielle, Travaux, Chantiers",
                "— Cadres de chantier",
                "— Cadres de production industrielle",
                "— Direction d'unité industrielle",
                "Ressources Humaines",
                "— Administration des RH",
                "— Développement des RH",
                "— Direction des ressources humaines",
                "— Formation initiale et continue",
                "Sanitaire, Social, Culture",
                "— Activités sanitaires, sociales et culturelles",
                "Services Techniques",
                "— Achats",
                "— Direction des services techniques",
                "— Logistique",
                "— Maintenance, sécurité",
                "— Process, méthodes",
                "— Qualité"
              ]
            },
            "default": []
          },
          "jobFunctionIds": {
            "title": "Raw métier ids (power user)",
            "type": "array",
            "description": "The same filter accepts APEC's 502 individual métier ids as well as the families above — e.g. 600174 (Acheteur) returns 377 offers. Merged with the selection above.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "sectors": {
            "title": "Sectors of activity",
            "type": "array",
            "description": "The hiring company's broad sector. Measured: Conseil et gestion des entreprises 21,325 · Activités informatiques 7,787. Every row also carries the company's precise NAF/APE code and label, decoded from APEC's nomenclature.",
            "items": {
              "type": "string",
              "enum": [
                "101752",
                "101753",
                "101754",
                "101755",
                "101756",
                "101757",
                "101758",
                "101759",
                "101760",
                "101761",
                "101762",
                "101763",
                "101764",
                "101765",
                "101766",
                "101767",
                "101768",
                "101769",
                "101770",
                "101771",
                "101772",
                "101773",
                "101774",
                "101775",
                "101776",
                "101777",
                "101778",
                "101779",
                "101780"
              ],
              "enumTitles": [
                "Activités des organisations associatives et administration publique",
                "Activités informatiques",
                "Activités juridiques et comptables",
                "Agroalimentaire",
                "Automobile, aéronautique et autres matériels de transport",
                "Banque et Assurances",
                "Bois - Papier - Imprimerie",
                "Chimie - Caoutchouc - Plastique",
                "Commerce interentreprises",
                "Communication et médias",
                "Conseil et gestion des entreprises",
                "Construction",
                "Distribution généraliste et spécialisée",
                "Energies - Eau",
                "Equipements électriques et électroniques",
                "Formation initiale et continue",
                "Gestion des déchets",
                "Hôtellerie - Restauration - Loisirs",
                "Immobilier",
                "Industrie pharmaceutique",
                "Ingénierie - R et D",
                "Intermédiaires du recrutement",
                "Mécanique - Métallurgie",
                "Meuble, Textile et autres industries manufacturières",
                "Santé - action sociale",
                "Services divers aux entreprises",
                "Télécommunications",
                "Transports et logistique",
                "Autres"
              ]
            },
            "default": []
          },
          "experienceLevels": {
            "title": "Experience levels",
            "type": "array",
            "description": "Measured: Débutant 58,417 · 3-5 ans 58,195 · 6-9 ans 38,493 · 10 ans et + 36,985. These overlap — an offer open to 3-5 and 6-9 years counts in both.",
            "items": {
              "type": "string",
              "enum": [
                "101881",
                "20043",
                "20044",
                "20045"
              ],
              "enumTitles": [
                "Débutant",
                "3 - 5 ans",
                "6 - 9 ans",
                "10 ans et +"
              ]
            },
            "default": []
          },
          "positionStatus": {
            "title": "Position status",
            "type": "array",
            "description": "Measured: Cadre du secteur privé 76,742 · Agent de maîtrise / Technicien 9,274 · Cadre du secteur public / Armées 1,827.",
            "items": {
              "type": "string",
              "enum": [
                "143688",
                "143689",
                "143690"
              ],
              "enumTitles": [
                "Cadre du secteur privé",
                "Cadre du secteur public / Armées",
                "Agent de maîtrise / Technicien"
              ]
            },
            "default": []
          },
          "remoteWork": {
            "title": "Remote-work policy",
            "type": "array",
            "description": "Measured: télétravail partiel possible 11,861 · ponctuel autorisé 5,855 · total possible 972. Only 27.7% of offers declare a policy at all, so this filter is strict by nature.",
            "items": {
              "type": "string",
              "enum": [
                "20766",
                "20765",
                "20767",
                "20949"
              ],
              "enumTitles": [
                "Télétravail ponctuel autorisé",
                "Télétravail partiel possible",
                "Télétravail total possible",
                "Pas de télétravail autorisé"
              ]
            },
            "default": []
          },
          "contractDurationBands": {
            "title": "Contract duration",
            "type": "array",
            "description": "For fixed-term work. Measured: 1-6 mois 3,764 · 7-12 mois 1,862 · 13 mois et + 835.",
            "items": {
              "type": "string",
              "enum": [
                "143691",
                "143692",
                "143693"
              ],
              "enumTitles": [
                "1 à 6 mois",
                "7 à 12 mois",
                "13 mois et +"
              ]
            },
            "default": []
          },
          "internshipDuration": {
            "title": "Internship duration",
            "type": "array",
            "description": "For internships (stage). Measured: 3 mois 672 · 1 mois 677 · 4 mois 455.",
            "items": {
              "type": "string",
              "enum": [
                "599764",
                "599765",
                "599766",
                "599767",
                "599768",
                "599769",
                "599770"
              ],
              "enumTitles": [
                "< 1 mois",
                "1 mois",
                "2 mois",
                "3 mois",
                "4 mois",
                "5 mois",
                "6 mois"
              ]
            },
            "default": []
          },
          "educationLevel": {
            "title": "Education level",
            "enum": [
              "",
              "20017",
              "20018",
              "20019",
              "20020"
            ],
            "type": "string",
            "description": "A thin facet — APEC only records it on a few thousand offers (Bac+5 returned 53). Expect small result sets, and expect most offers to be excluded rather than matched.",
            "default": ""
          },
          "travelZones": {
            "title": "Travel expected",
            "type": "array",
            "description": "How far the role travels. Measured: Régionale 6,834. Not on the public search UI, but the search API accepts it.",
            "items": {
              "type": "string",
              "enum": [
                "596720",
                "596722",
                "596723",
                "596724",
                "596725",
                "596726"
              ],
              "enumTitles": [
                "Pas de déplacement",
                "Départementale",
                "Régionale",
                "Nationale",
                "Union Européenne",
                "Internationale"
              ]
            },
            "default": []
          },
          "startDateWithin": {
            "title": "Start date",
            "enum": [
              "",
              "599777",
              "599778",
              "599779"
            ],
            "type": "string",
            "description": "When the post opens. Measured: dès que possible 86,715 · < 2 mois 966 · > 2 mois 175.",
            "default": ""
          },
          "salaryMinK": {
            "title": "Minimum salary (k€)",
            "minimum": 0,
            "maximum": 1000,
            "type": "integer",
            "description": "In thousands of euros, gross annual. APEC ignores a minimum sent on its own — sending only a floor leaves totalCount at the unfiltered 87,775 with byte-identical rows — so if you set just one bound the other is completed for you (0 or 1000). Measured: 50-70 → 38,254 offers, 90-1000 → 6,262. Note this is an INTERSECTION filter — an offer whose own range overlaps the band matches, open-ended \"A partir de N k€\" offers count as unbounded above, and \"A négocier\" offers pass every band. Use the strict euro bounds below for a hard floor. 0 = off.",
            "default": 0
          },
          "salaryMaxK": {
            "title": "Maximum salary (k€)",
            "minimum": 0,
            "maximum": 1000,
            "type": "integer",
            "description": "In thousands of euros, gross annual. The site's own bands are 0-25, 25-35, 35-50, 50-70, 70-91 and 90-1000. 0 = off.",
            "default": 0
          },
          "minSalaryEur": {
            "title": "Strict minimum salary (€ per year)",
            "minimum": 0,
            "type": "integer",
            "description": "A hard floor applied on our side to the salary APEC actually printed, normalised to gross annual euros (a monthly figure is multiplied by 12). Use this when the band above is not strict enough: the APEC band is an INTERSECTION filter, so \"A partir de 70 k€\" — which has no upper bound — matches the 90-1000 k€ band, and \"A négocier\" offers match every band. Setting this also drops offers with no published figure. 0 = off.",
            "default": 0
          },
          "maxSalaryEur": {
            "title": "Strict maximum salary (€ per year)",
            "minimum": 0,
            "type": "integer",
            "description": "A hard ceiling on the printed salary, in gross annual euros, applied on our side. Also drops offers with no published figure. 0 = off.",
            "default": 0
          },
          "onlyWithNumericSalary": {
            "title": "Only offers with a real salary figure",
            "type": "boolean",
            "description": "Drops \"A négocier\", \"Minimum légal\" and \"Non renseignée\". Measured on 1,418 rows: 53% carry a numeric figure (41% a range, 12% a floor), 33% say \"A négocier\", 9% \"Minimum légal\", 3% \"Non renseignée\", 2% a monthly figure. Runs on our side; dropped rows are never billed.",
            "default": false
          },
          "postedWithin": {
            "title": "Posted within",
            "enum": [
              "",
              "101850",
              "101851",
              "101852",
              "101853"
            ],
            "type": "string",
            "description": "APEC's own date facet. Read live on 2026-09-04 at 20:20 UTC: last 24h 18,680 · last 7 days 44,092 · last 30 days = the whole board (APEC expires offers at 30 days). Both counts move through the day.",
            "default": ""
          },
          "postedAfter": {
            "title": "Posted on or after (YYYY-MM-DD)",
            "type": "string",
            "description": "A tighter window than the four buckets above, applied on our side to the publication date. Rows outside it are never billed.",
            "default": ""
          },
          "postedBefore": {
            "title": "Posted on or before (YYYY-MM-DD)",
            "type": "string",
            "description": "Upper bound on the publication date, applied on our side.",
            "default": ""
          },
          "companyNameContains": {
            "title": "Company name contains",
            "type": "array",
            "description": "Keep only offers whose advertised company name contains one of these (case-insensitive).",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "excludeCompanies": {
            "title": "Exclude companies",
            "type": "array",
            "description": "Drop offers from these company names. Handy for muting a bulk poster the aggregator filter does not catch — e.g. a single ESN posting hundreds of near-identical roles.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "includeConfidential": {
            "title": "Include confidential offers",
            "type": "boolean",
            "description": "APEC hides the employer on 497 offers corpus-wide; they post as \"ZZ_Confidentiel\" and every row carries `isConfidential`. Switch off to drop them (measured: 87,843 → 87,239).",
            "default": true
          },
          "onlyLowApplicantCount": {
            "title": "Only offers with few applicants",
            "type": "boolean",
            "description": "APEC flags postings that have attracted few applications. Rare — 2 of 300 rows in a blind sample — but it is a genuine recruiter signal, and no other APEC actor surfaces it at all.",
            "default": false
          },
          "postingOrigins": {
            "title": "Posting origin",
            "type": "array",
            "description": "How the offer reached APEC. \"Agrégation\" (20382) is the aggregated-feed marker and is orthogonal to the employer type, so it catches resale the type filter misses. Applied on our side; leave empty for all.",
            "items": {
              "type": "string",
              "enum": [
                "101859",
                "101860",
                "101861",
                "101862",
                "101863",
                "101864",
                "101865",
                "101866",
                "101973",
                "20182",
                "20382"
              ],
              "enumTitles": [
                "Site Web APEC",
                "FAX",
                "Visite Client",
                "Visite APEC",
                "Téléphone",
                "Courrier",
                "E-mail",
                "ADEP",
                "Autre",
                "Import XML",
                "Agrégation"
              ]
            },
            "default": []
          },
          "sortBy": {
            "title": "Sort by",
            "enum": [
              "DATE",
              "ID",
              "SCORE",
              "COMPANY",
              "GEOLOC"
            ],
            "type": "string",
            "description": "All five sorts are live. A note worth knowing before a deep crawl: paging by DATE while new offers arrive at the head shifts the window — measured 4.88% duplicate rows over 800 rows, versus 0.88% paging by ID. Duplicates are removed before billing either way, but ID is the steadier deep crawl. GEOLOC is chosen automatically when you set a radius.",
            "default": "DATE"
          },
          "sortOrder": {
            "title": "Sort order",
            "enum": [
              "DESCENDING",
              "ASCENDING"
            ],
            "type": "string",
            "description": "DESCENDING with \"Publication date\" gives newest first.",
            "default": "DESCENDING"
          },
          "maxItems": {
            "title": "Max offers (total)",
            "minimum": 0,
            "type": "integer",
            "description": "Hard stop across every search, applied BEFORE rows are pushed and billed. 0 = no limit — the whole board is ~88,000 offers, so set this deliberately.",
            "default": 200
          },
          "maxItemsPerSearch": {
            "title": "Max offers per search",
            "minimum": 0,
            "type": "integer",
            "description": "Per keyword / per pasted URL, so one broad term cannot eat the whole budget. 0 = no per-search limit.",
            "default": 0
          },
          "pageSize": {
            "title": "Results per request",
            "minimum": 1,
            "maximum": 100,
            "type": "integer",
            "description": "APEC caps this at 100 and does it silently: ask for 101 or 150 and it returns HTTP 200 with only 20 rows, quietly costing 5x the requests. Values above 100 are clamped here.",
            "default": 100
          },
          "startIndex": {
            "title": "Start offset",
            "minimum": 0,
            "type": "integer",
            "description": "Resume a crawl part-way in. APEC has no offset ceiling — verified to the tail of the corpus (offset 87,700 returned the last 28 rows).",
            "default": 0
          },
          "dedupeAcrossSearches": {
            "title": "Deduplicate across searches",
            "type": "boolean",
            "description": "One offer, one row, however many of your keywords match it. Switch off to get one row per matching search instead (each still deduped within its own search). Duplicates are dropped before billing in both modes.",
            "default": true
          },
          "includeRawCodes": {
            "title": "Include raw APEC codes",
            "type": "boolean",
            "description": "Adds the numeric nomenclature ids next to the decoded labels (contract, remote, employer class, NAF, sector, posting origin) so you can feed them straight back into a later run.",
            "default": true
          },
          "includeTitleHighlight": {
            "title": "Include the search-highlighted title",
            "type": "boolean",
            "description": "Adds `titleHighlighted`, APEC's title with `<span class=\"highlightResult\">` markup around your keyword. Off by default — most buyers want the clean title.",
            "default": false
          },
          "compact": {
            "title": "Compact output",
            "type": "boolean",
            "description": "Ship 13 core columns instead of ~40: offer number, title, company, employer class, city, department, contract, salary, remote, published, URL.",
            "default": false
          },
          "outputFields": {
            "title": "Output fields",
            "type": "array",
            "description": "Exact column selection, overriding \"Compact\". `numeroOffre` is always kept so rows stay joinable on your side.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "refreshLabels": {
            "title": "Refresh the label nomenclature live",
            "type": "boolean",
            "description": "The 3,502-entry label nomenclature (sectors, NAF codes, métiers, contract types, places) is bundled with the actor, so labels never depend on a live call. Switch this on to re-fetch it from APEC at the start of the run — one extra request — if you need same-day nomenclature.",
            "default": false
          },
          "proxyConfiguration": {
            "title": "Proxy configuration",
            "type": "object",
            "description": "The Apify datacenter pool is the measured-correct default: APEC's search API answered 200 from a direct IP, a datacenter IP and a residential FR IP alike, and 17 of 18 varied calls on one pinned session came back clean. No browser and no residential proxy are needed.",
            "default": {
              "useApifyProxy": true
            }
          },
          "maxConcurrency": {
            "title": "Concurrent requests",
            "minimum": 1,
            "maximum": 10,
            "type": "integer",
            "description": "Query slices run in parallel. Measured safe: 5 parallel 5/5, 10 parallel 10/10 on one session, and 25 rapid sequential calls with no delay 25/25 — no rate limiting observed.",
            "default": 5
          },
          "requestDelayMs": {
            "title": "Delay between requests (ms)",
            "minimum": 0,
            "maximum": 30000,
            "type": "integer",
            "description": "Optional politeness pause after every request. 0 in testing caused no throttling.",
            "default": 0
          },
          "maxRetries": {
            "title": "Max retries per request",
            "minimum": 0,
            "maximum": 8,
            "type": "integer",
            "description": "Retries rotate the proxy session. A HTTP 500 is never retried — it means the search body itself was rejected, and no amount of retrying fixes that.",
            "default": 3
          },
          "requestTimeoutSecs": {
            "title": "Request timeout (seconds)",
            "minimum": 5,
            "maximum": 120,
            "type": "integer",
            "description": "Per request. Median search latency measured at 2.4-4.9 s, p95 17.6 s under parallel load.",
            "default": 45
          }
        }
      },
      "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
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}