# APEC France Jobs Scraper (`solidcode/apec-fr-scraper`) Actor

\[💰 $0.9 / 1K] Extract executive and cadre-level job listings from APEC.fr — France's national jobs board. Search by keyword, location, or department; filter by contract type, experience, and salary. Returns title, company, salary, full description, contract type, and application link per job.

- **URL**: https://apify.com/solidcode/apec-fr-scraper.md
- **Developed by:** [SolidCode](https://apify.com/solidcode) (community)
- **Categories:** Jobs, Automation, Developer tools
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, NaN bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.90 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).


# README

## APEC France Jobs Scraper

Pull executive and cadre-level job listings from APEC.fr at scale — French job titles, hiring companies, salary ranges, contract codes, experience buckets, telework arrangements, full HTML and plain-text descriptions, application links, and APEC reference numbers for every open position. Built for recruiters, talent acquisition platforms, ATS and CRM tools, and market-research analysts who need fresh French executive job-market data without manually paging through APEC's search UI.

### Why This Scraper?

- **162-entry French location lookup** — paste `Île-de-France`, `Bouches-du-Rhône`, `13`, or `Marseille` and the actor resolves it to APEC's internal nomenclature. Covers all 13 metropolitan régions, all 96 départements, 13 DROM-COM territories, and 28 of France's largest metropolitan cities — accent-folded so `ile-de-france` and `Île-de-France` both work.
- **Four contract types wired to APEC's official codes** — CDI (permanent), CDD (fixed-term), Stage (internship), and Alternance (apprenticeship), each mapped to the exact `idNomenclature` APEC's search uses.
- **Three telework levels, separately filterable** — full remote (`Télétravail total`), hybrid (`Télétravail partiel`), and on-site only (`Sur site`). Surface remote-friendly cadre roles in one query.
- **Four experience buckets aligned to APEC's cadre nomenclature** — Junior (0–3 years), Mid-level (1–7 years), Senior (7+ years), and Expert/Director.
- **EUR salary floor with auto-conversion to APEC's k€ filter** — type `50000` and the actor talks to APEC in its native unit, paired with a no-upper-bound sentinel so `minSalary: 50000` returns every listing paying €50k+.
- **Paste apec.fr search URLs directly** — every facet you tweak in your browser (`motsCles`, `typesContrat`, `niveauxExperience`, `typesTeletravail`, `lieux`, `salaireMinimum`) is parsed automatically. Build the search visually, copy the URL, run the actor.
- **22 fields per job** — title, company, French location string + département + région, salary range, contract code, experience bucket, telework level, publication date, applyLink, full HTML and plain-text descriptions, company description, industry sector, named competences, scrape timestamp, and the APEC reference number.
- **Up to 50 000 results per run** — single `maxResults` cap with built-in pagination across APEC's full corpus, no dual-knob configuration to mis-set.

### Use Cases

**Recruitment Intelligence**
- Track competitor hiring volume by company, sector, and région
- Detect headcount-growth signals from new senior cadre openings
- Map which French firms are hiring abroad versus domestically
- Monitor specific employers for new postings on a daily cadence

**Salary Benchmarking**
- Build EUR salary distributions by région, contract type, and experience bucket
- Compare CDI vs. CDD pay ranges in target sectors
- Quantify the remote-work premium across `Télétravail total` versus `Sur site`
- Benchmark advertised salary ranges against your own offers

**French Market Research**
- Map executive job-market trends across the 13 régions
- Identify hot sectors by tracking `industry` distribution over time
- Surface emerging skills via the `competences` tag analysis
- Monitor cadre demand in specific départements

**Lead Generation & Sourcing**
- Build lists of French companies actively hiring in target sectors
- Feed an ATS or CRM with fresh APEC openings every morning
- Identify recruiting agencies posting on behalf of confidential clients
- Discover hiring contacts via the application links on each listing

**Aggregator & Dataset Building**
- Power French-language jobs aggregator sites with structured APEC data
- Build training datasets for NLP models on French executive job descriptions
- Cross-reference APEC postings with LinkedIn, Indeed, or your internal pipeline
- Maintain an always-fresh inventory of `cadre`-level vacancies in France

### Getting Started

#### Minimal Search

Just a keyword and a row cap:

```json
{
    "keyword": "ingénieur logiciel",
    "maxResults": 100
}
````

#### Multi-Filter Search

CDI permanent contracts in Paris with full remote and €50k+ salary:

```json
{
    "keyword": "data scientist",
    "location": "Paris",
    "contractTypes": ["CDI"],
    "teleworkLevels": ["full"],
    "minSalary": 50000,
    "maxResults": 200
}
```

#### Region-Wide Senior Search

Surface senior cadre roles across an entire région using the département code:

```json
{
    "keyword": "directeur",
    "department": "75",
    "experienceLevels": ["7plus", "senior"],
    "contractTypes": ["CDI"],
    "maxResults": 500
}
```

#### Paste APEC Search URLs

Build the search visually on apec.fr, then drop the URL in:

```json
{
    "searchUrls": [
        "https://www.apec.fr/candidat/recherche-emploi.html?motsCles=chef%20de%20projet&typesContrat=101888&typesTeletravail=20767&lieux=711"
    ],
    "maxResults": 250
}
```

### Input Reference

#### What to Scrape

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `searchUrls` | string\[] | `[]` | Paste one or more APEC search URLs directly from your browser. Use this when you've already built a complex filter set in the APEC UI and want to copy the URL in. When provided, the structured filters below are ignored. |
| `keyword` | string | `"ingénieur"` | Job title, role, or skill keywords (e.g. `"ingénieur logiciel"`, `"chef de projet"`, `"data scientist"`). French preferred — APEC is a French site. |

#### Filters

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `location` | string | `null` | City or region name (e.g. `"Paris"`, `"Lyon"`, `"Île-de-France"`, `"Toulouse"`). When set, takes priority over Department. |
| `department` | string | `null` | French department code, two digits (e.g. `"75"` Paris, `"69"` Rhône, `"13"` Bouches-du-Rhône, `"92"` Hauts-de-Seine). Ignored when Location is set. |
| `contractTypes` | string\[] | `[]` | Only include jobs matching these contract types. Empty = all types. Options: `CDI (permanent contract)`, `CDD (fixed-term contract)`, `Internship (Stage)`, `Apprenticeship (Alternance)`. |
| `experienceLevels` | string\[] | `[]` | Only include jobs matching these experience levels. Empty = all levels. Options: `Junior (0-3 years)`, `Mid-level (1-7 years)`, `Senior (7+ years)`, `Expert / Director`. |
| `teleworkLevels` | string\[] | `[]` | Only include jobs offering these telework arrangements. Empty = all. Options: `Full remote (Télétravail total)`, `Hybrid / Partial (Télétravail partiel)`, `On-site only (Sur site)`. |
| `minSalary` | integer | `null` | Only include jobs with an annual salary at or above this amount, in euros (e.g. `45000` for 45 k€). Listings without a salary disclosure may still appear — APEC's filter is inclusive of unspecified salaries. |

#### Limits

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `maxResults` | integer | `100` | Hard cap on total job rows returned across all sources. Default 100 — increase for bigger runs, or set to 0 for no cap (an internal upper bound of 50,000 still applies to prevent runaway pagination). The actor stops requesting new pages once this number is hit but keeps the full last page even if it slightly overshoots. |

### Output

One row per job listing. Flat schema, French-language values preserved.

```json
{
    "id": "178206207W",
    "title": "Directeur de projet Hydraulique urbaine confirmé F/H",
    "company": "L'USINE NOUVELLE",
    "location": "Paris 01 - 75",
    "department": "75",
    "region": null,
    "salary": "60 - 75 k€",
    "salaryMin": 60000,
    "salaryMax": 75000,
    "contractType": "CDI",
    "experienceLevel": "Minimum 7 ans",
    "telework": "Télétravail partiel possible",
    "publishedAt": "2026-02-20T05:17:10Z",
    "descriptionHtml": "<p>Au sein de notre direction technique...</p>",
    "descriptionText": "Au sein de notre direction technique...",
    "applyLink": "https://example.com/apply",
    "url": "https://www.apec.fr/candidat/recherche-emploi.html/emploi/detail-offre/178206207W",
    "companyDescription": "Premier média B2B français...",
    "companySize": null,
    "industry": "Édition / Presse",
    "competences": ["Hydraulique", "Gestion de projet", "Conception"],
    "scrapedAt": "2026-05-08T12:34:56Z"
}
```

#### Core Fields

| Field | Type | Description |
|-------|------|-------------|
| `id` | string | APEC offer reference (e.g. `"178206207W"`) |
| `title` | string | Job title in French |
| `url` | string | APEC detail page URL |
| `publishedAt` | string | ISO 8601 publication timestamp |
| `scrapedAt` | string | ISO 8601 extraction timestamp |
| `applyLink` | string | null | External application URL when APEC links externally |

#### Location

| Field | Type | Description |
|-------|------|-------------|
| `location` | string | Job location text as displayed by APEC (e.g. `"Paris 01 - 75"`, `"Lyon - 69"`) |
| `department` | string | French département code parsed from the listing (e.g. `"75"`, `"69"`) |
| `region` | string | null | Région name when available |

#### Compensation & Contract

| Field | Type | Description |
|-------|------|-------------|
| `salary` | string | null | Salary text as displayed by APEC (e.g. `"45 - 55 k€"`, `"À négocier"`) |
| `salaryMin` | integer | null | Parsed minimum annual salary in EUR — best-effort, null when freeform |
| `salaryMax` | integer | null | Parsed maximum annual salary in EUR — best-effort, null when freeform |
| `contractType` | string | `"CDI"`, `"CDD"`, `"Stage"`, or `"Alternance"` |
| `experienceLevel` | string | null | Required experience as labelled by APEC (e.g. `"Minimum 5 ans"`, `"Tous niveaux d'expérience"`) |
| `telework` | string | null | Telework arrangement (e.g. `"Télétravail total possible"`, `"Télétravail partiel possible"`, `"Pas de télétravail autorisé"`) |

#### Company & Description

| Field | Type | Description |
|-------|------|-------------|
| `company` | string | null | Recruiting company name (or recruiter name when the employer is anonymized) |
| `companyDescription` | string | null | Company blurb when shown |
| `companySize` | string | null | Company size band — APEC rarely exposes this; usually null |
| `industry` | string | null | Industry / sector classification |
| `competences` | string\[] | null | Skills and competencies tagged on the listing (typically up to 40 entries when APEC tags any) |
| `descriptionHtml` | string | null | Full job description with HTML formatting preserved |
| `descriptionText` | string | Cleaned plain-text job description |

### Tips for Best Results

- **Start with a tight `maxResults`.** Set 25–50 on your first run to verify the filter combination matches what you expect, then scale up.
- **Use the département code for tricky locations.** Smaller cities and suburbs that aren't in the 28-city alias list will resolve cleanly with their two-digit département code (`13` for Marseille area, `33` for Bordeaux, `06` for Nice).
- **Combine `experienceLevels=senior` with `teleworkLevels=full` to surface remote senior cadre roles** — one of the strongest signals of a forward-looking employer in the French market.
- **APEC's experience buckets are pre-grouped** — `1to7` aligns with APEC's `3-5 ans` facet and `7plus` with `6-9 ans`. If you need an exact range, post-filter on `experienceLevel` text in your downstream pipeline.
- **Salary is in EUR on input, k€ on display** — type `45000` to mean €45k. APEC publishes some salaries as free-text (`"À négocier"`, `"Selon profil"`); those rows still appear unless you add a `minSalary` filter, which excludes them.
- **`searchUrls` overrides every structured filter.** Build the perfect search on apec.fr, copy the URL, paste it in. The actor parses `motsCles`, `typesContrat`, `niveauxExperience`, `typesTeletravail`, `lieux`, and `salaireMinimum` straight from the URL.
- **For zero-result inputs the actor exits cleanly** — the run's status message lists the exact keyword and filter combination, so you can copy-paste, broaden one filter, and rerun.

### Pricing

**$0.90 per 1,000 results** — flat pay-per-result, billed only on rows actually delivered to your dataset.

| Results | Estimated Cost |
|---------|----------------|
| 100 | $0.09 |
| 1,000 | $0.90 |
| 10,000 | $9.00 |
| 100,000 | $90.00 |

A "result" is any job-listing row in the output dataset. **No compute charges — you only pay per result returned.**

### Integrations

Export data in JSON, CSV, Excel, XML, or RSS. Connect to 1,500+ apps via:

- **Zapier** / **Make** / **n8n** — Workflow automation
- **Google Sheets** — Direct spreadsheet export
- **Slack** / **Email** — Notifications on new results
- **Webhooks** — Trigger custom APIs on run completion
- **Apify API** — Full programmatic access

### Legal & Ethical Use

This actor collects job listings from APEC's public candidate search at apec.fr. Users are responsible for complying with applicable laws, APEC's Terms of Service, and the French CNIL (Commission nationale de l'informatique et des libertés) guidance on personal-data handling — particularly when company contacts or recruiter names appear in the output. Do not use extracted data for spam, harassment, candidate poaching that violates contractual non-solicit clauses, or any illegal purpose.

# Actor input Schema

## `searchUrls` (type: `array`):

Paste one or more APEC search URLs directly from your browser (e.g. https://www.apec.fr/candidat/recherche-emploi.html?motsCles=ingenieur\&lieux=...). Use this when you've already built a complex filter set in the APEC UI and want to copy the URL in. When provided, the structured filters below are ignored.

## `keyword` (type: `string`):

Job title, role, or skill keywords (e.g. 'ingénieur logiciel', 'chef de projet', 'data scientist'). French preferred — APEC is a French site.

## `location` (type: `string`):

City or region name (e.g. 'Paris', 'Lyon', 'Île-de-France', 'Toulouse'). When set, takes priority over Department.

## `department` (type: `string`):

French department code, two digits (e.g. '75' Paris, '69' Rhône, '13' Bouches-du-Rhône, '92' Hauts-de-Seine). Ignored when Location is set.

## `contractTypes` (type: `array`):

Only include jobs matching these contract types. Empty = all types.

## `experienceLevels` (type: `array`):

Only include jobs matching these experience levels. Empty = all levels.

## `teleworkLevels` (type: `array`):

Only include jobs offering these telework arrangements. Empty = all.

## `minSalary` (type: `integer`):

Only include jobs with an annual salary at or above this amount, in euros (e.g. 45000 for 45 k€). Listings without a salary disclosure may still appear — APEC's filter is inclusive of unspecified salaries. Leave blank for no filter.

## `maxResults` (type: `integer`):

Hard cap on total job rows returned across all sources. Default 100 — increase for bigger runs, or set to 0 for no cap (an internal upper bound of 50,000 still applies to prevent runaway pagination). The actor stops requesting new pages once this number is hit but keeps the full last page even if it slightly overshoots.

## Actor input object example

```json
{
  "searchUrls": [],
  "keyword": "ingénieur",
  "contractTypes": [],
  "experienceLevels": [],
  "teleworkLevels": [],
  "maxResults": 100
}
```

# Actor output Schema

## `overview` (type: `string`):

Table of jobs with the most useful fields — title, company, location, salary, contract type, and link.

## `details` (type: `string`):

Full per-job rows with descriptions, company info, telework, experience level, and competencies.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "searchUrls": [],
    "keyword": "ingénieur",
    "contractTypes": [],
    "experienceLevels": [],
    "teleworkLevels": [],
    "maxResults": 100
};

// Run the Actor and wait for it to finish
const run = await client.actor("solidcode/apec-fr-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "searchUrls": [],
    "keyword": "ingénieur",
    "contractTypes": [],
    "experienceLevels": [],
    "teleworkLevels": [],
    "maxResults": 100,
}

# Run the Actor and wait for it to finish
run = client.actor("solidcode/apec-fr-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "searchUrls": [],
  "keyword": "ingénieur",
  "contractTypes": [],
  "experienceLevels": [],
  "teleworkLevels": [],
  "maxResults": 100
}' |
apify call solidcode/apec-fr-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=solidcode/apec-fr-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "APEC France Jobs Scraper",
        "description": "[💰 $0.9 / 1K] Extract executive and cadre-level job listings from APEC.fr — France's national jobs board. Search by keyword, location, or department; filter by contract type, experience, and salary. Returns title, company, salary, full description, contract type, and application link per job.",
        "version": "1.0",
        "x-build-id": "S7QqQJhoVqeLd6A4r"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/solidcode~apec-fr-scraper/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-solidcode-apec-fr-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/solidcode~apec-fr-scraper/runs": {
            "post": {
                "operationId": "runs-sync-solidcode-apec-fr-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/solidcode~apec-fr-scraper/run-sync": {
            "post": {
                "operationId": "run-sync-solidcode-apec-fr-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": {
                    "searchUrls": {
                        "title": "APEC Search URLs",
                        "type": "array",
                        "description": "Paste one or more APEC search URLs directly from your browser (e.g. https://www.apec.fr/candidat/recherche-emploi.html?motsCles=ingenieur&lieux=...). Use this when you've already built a complex filter set in the APEC UI and want to copy the URL in. When provided, the structured filters below are ignored.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "keyword": {
                        "title": "Keyword",
                        "type": "string",
                        "description": "Job title, role, or skill keywords (e.g. 'ingénieur logiciel', 'chef de projet', 'data scientist'). French preferred — APEC is a French site."
                    },
                    "location": {
                        "title": "Location",
                        "type": "string",
                        "description": "City or region name (e.g. 'Paris', 'Lyon', 'Île-de-France', 'Toulouse'). When set, takes priority over Department."
                    },
                    "department": {
                        "title": "Department",
                        "type": "string",
                        "description": "French department code, two digits (e.g. '75' Paris, '69' Rhône, '13' Bouches-du-Rhône, '92' Hauts-de-Seine). Ignored when Location is set."
                    },
                    "contractTypes": {
                        "title": "Contract Types",
                        "uniqueItems": true,
                        "type": "array",
                        "description": "Only include jobs matching these contract types. Empty = all types.",
                        "items": {
                            "type": "string",
                            "enum": [
                                "CDI",
                                "CDD",
                                "internship",
                                "apprenticeship"
                            ],
                            "enumTitles": [
                                "CDI (permanent contract)",
                                "CDD (fixed-term contract)",
                                "Internship (Stage)",
                                "Apprenticeship (Alternance)"
                            ]
                        },
                        "default": []
                    },
                    "experienceLevels": {
                        "title": "Experience Levels",
                        "uniqueItems": true,
                        "type": "array",
                        "description": "Only include jobs matching these experience levels. Empty = all levels.",
                        "items": {
                            "type": "string",
                            "enum": [
                                "junior",
                                "1to7",
                                "7plus",
                                "senior"
                            ],
                            "enumTitles": [
                                "Junior (0-3 years)",
                                "Mid-level (1-7 years)",
                                "Senior (7+ years)",
                                "Expert / Director"
                            ]
                        },
                        "default": []
                    },
                    "teleworkLevels": {
                        "title": "Telework / Remote Work",
                        "uniqueItems": true,
                        "type": "array",
                        "description": "Only include jobs offering these telework arrangements. Empty = all.",
                        "items": {
                            "type": "string",
                            "enum": [
                                "full",
                                "partial",
                                "onsite"
                            ],
                            "enumTitles": [
                                "Full remote (Télétravail total)",
                                "Hybrid / Partial (Télétravail partiel)",
                                "On-site only (Sur site)"
                            ]
                        },
                        "default": []
                    },
                    "minSalary": {
                        "title": "Minimum Annual Salary (EUR)",
                        "minimum": 0,
                        "maximum": 500000,
                        "type": "integer",
                        "description": "Only include jobs with an annual salary at or above this amount, in euros (e.g. 45000 for 45 k€). Listings without a salary disclosure may still appear — APEC's filter is inclusive of unspecified salaries. Leave blank for no filter."
                    },
                    "maxResults": {
                        "title": "Maximum Results",
                        "minimum": 0,
                        "type": "integer",
                        "description": "Hard cap on total job rows returned across all sources. Default 100 — increase for bigger runs, or set to 0 for no cap (an internal upper bound of 50,000 still applies to prevent runaway pagination). The actor stops requesting new pages once this number is hit but keeps the full last page even if it slightly overshoots.",
                        "default": 100
                    }
                }
            },
            "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
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
