# APEC Scraper - French Executive Job Offers (`studio-amba/apec-scraper`) Actor

Scrape executive (cadre) job offers from APEC (apec.fr), France's job board for managers and professionals. Search by keyword, location, and contract type. Extract titles, companies, salaries, skills, and full descriptions. No login or cookies required.

- **URL**: https://apify.com/studio-amba/apec-scraper.md
- **Developed by:** [Studio Amba](https://apify.com/studio-amba) (community)
- **Categories:** Jobs
- **Features:** Limited permissions
- **Stats:** 1 total users, 0 monthly users, 94.4% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 result scrapeds

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/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## APEC Scraper — French Executive (Cadre) Job Offers

Scrape executive and managerial job offers from [APEC](https://www.apec.fr) (Association pour l'emploi des cadres), France's dedicated job board for cadres and professionals. Search by keyword, location, and contract type, then export clean structured data with salaries, skills, and full descriptions. No login, no cookies.

### Why use this actor?

APEC is where French companies post their higher-skilled roles — management, engineering, finance, IT, sales, and consulting. If you build a jobs aggregator, run salary benchmarks, track hiring demand by sector, or feed a recruitment CRM, this actor turns the APEC search into a structured feed you can query on a schedule.

It reads APEC's own JSON search API directly, so results are fast and complete: every offer already carries its title, hiring company, location, salary range, contract type, and publication date. Turn on full descriptions and each row also gets the complete job text, the required profile, the company description, and the listed skills.

### How to scrape APEC data

1. Open the actor and set a **Search Query** in French — for example `chef de projet`, `développeur`, `comptable`, or `commercial`.
2. Optionally set a **Location** (city, department name, or postal-code prefix such as `Paris`, `Lyon`, `75`, or `69`) and a **Contract Type** (CDI, CDD, Interim, Freelance, Alternance).
3. Set **Max Results** to the number of offers you want.
4. Leave **Fetch Full Descriptions** on for the complete job text and skills, or turn it off for faster listing-only results.
5. Keep the French residential proxy prefill and click **Start**.
6. When the run finishes, export the dataset as JSON, CSV, or Excel, or pull it from the Apify API.

The actor paginates through the APEC search results automatically until it reaches your Max Results or runs out of matching offers.

### Input

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `searchQuery` | String | No | Job search keyword in French (e.g. `chef de projet`, `développeur`). Defaults to `chef de projet` if empty. |
| `location` | String | No | Filter by city, department name, or postal-code prefix (e.g. `Paris`, `Lyon`, `75`). Leave empty for all of France. |
| `contractType` | String | No | `all`, `CDI`, `CDD`, `INTERIM`, `FREELANCE`, or `ALTERNANCE`. |
| `maxResults` | Integer | No | Maximum number of offers to scrape (default: 100). |
| `fetchDetails` | Boolean | No | Fetch each offer's detail page for the full description and skills (default: true). |
| `proxyConfiguration` | Object | No | Proxy settings. French residential proxies recommended. |

### Output

Each result contains:

| Field | Type | Example |
|-------|------|---------|
| `jobTitle` | String | `"Chef de projet F/H"` |
| `company` | String | `"MOMENTI"` |
| `location` | String | `"Paris 15 - 75"` |
| `postalCode` | String | `"75015"` |
| `department` | String | `"75"` |
| `contractType` | String | `"CDI"` |
| `contractTypeCode` | Number | `101888` |
| `salary` | String | `"35 - 45 k€ brut annuel"` |
| `salaryMin` | Number | `35000` |
| `salaryMax` | Number | `45000` |
| `salaryPeriod` | String | `"annual"` |
| `jobDescription` | String | Full job description text |
| `profileWanted` | String | Required profile / candidate section |
| `companyDescription` | String | Description of the hiring company |
| `skills` | Array | `["Esprit d'équipe", "Gestion de projet", ...]` |
| `sector` | String | Sector of activity |
| `positions` | Number | `1` |
| `remote` | Boolean | `true` |
| `latitude` | String | `"48.8566"` |
| `longitude` | String | `"2.3522"` |
| `publishedDate` | String | `"2026-06-24T09:58:28.000+0000"` |
| `offerId` | String | `"178910904W"` |
| `applicationUrl` | String | External application URL (when present) |
| `url` | String | Full URL to the offer on apec.fr |
| `scrapedAt` | String | ISO timestamp of when the row was scraped |

### Example output

```json
{
    "jobTitle": "Chef de projet F/H",
    "company": "MOMENTI",
    "location": "Paris 15 - 75",
    "postalCode": "",
    "department": "75",
    "contractType": "CDI",
    "contractTypeCode": 101888,
    "salary": "35 - 45 k€ brut annuel",
    "salaryMin": 35000,
    "salaryMax": 45000,
    "salaryPeriod": "annual",
    "jobDescription": "Au sein de notre équipe, vous pilotez les projets de bout en bout...",
    "profileWanted": "Vous êtes issu d'une formation Bac +5 avec 3 ans d'expérience...",
    "companyDescription": "Nous sommes un cabinet de conseil spécialisé...",
    "skills": ["Esprit d'équipe", "Gestion de projet", "Reporting"],
    "sector": "",
    "positions": 1,
    "remote": false,
    "latitude": "48.8566",
    "longitude": "2.3522",
    "publishedDate": "2026-06-24T09:58:28.000+0000",
    "offerId": "178910904W",
    "applicationUrl": "https://easyapply.jobs/r/3HS0Rx327HlO231ueINV",
    "url": "https://www.apec.fr/candidat/recherche-emploi/emploi/detail-offre/178910904W",
    "scrapedAt": "2026-07-09T10:00:00.000Z"
}
```

### Salary parsing

APEC publishes salaries as free text (for example `"35 - 45 k€ brut annuel"` or `"A partir de 40 k€ brut annuel"`). The actor keeps the original text in `salary` and also parses it into `salaryMin`, `salaryMax`, and `salaryPeriod` where possible, converting `k€` figures into full euro amounts.

### Cost estimate

The search returns 100 offers per request, so listing-only runs are cheap. With **Fetch Full Descriptions** on, the actor makes one extra request per offer for the detail record (full description, profile, company description, skills, positions, application URL), which takes about 2-4 seconds each: 100 offers with details take roughly 5 minutes. Turn off `fetchDetails` when you only need titles, companies, locations, salaries, and links.

You pay the pay-per-result fee for each offer in the dataset. A run's usage cost only settles after the run SUCCEEDS.

### Limitations / known issues

- **Executive roles only.** APEC covers cadre and professional roles. It is not a general job board — use it alongside a France Travail scraper for full-market coverage.
- **Location filter is text-based.** The `location` input matches against each offer's location text (city and department). It is not a radius search, so `Paris` will not automatically include the wider Île-de-France region.
- **`postalCode` is often empty.** APEC's listing gives the city and 2-digit department, not always a full 5-digit postal code, so `department` is more reliably populated than `postalCode`.
- **`applicationUrl` is present only for external applications.** Offers that are applied to through APEC's own system leave this field empty; use `url` in that case.
- **APEC uses DataDome bot protection** on its search and offer-detail APIs. The actor tries French residential proxies first and switches to an unblocking service for the rest of the run when APEC answers with a challenge, so you do not need to configure anything.
- **A search with no matches succeeds with 0 results** and a status message saying so. A run only fails when APEC could not be reached at all.

### Related scrapers

- [HelloWork / European Job Search](https://apify.com/studio-amba/european-job-search) — multi-source European job aggregation.
- [Jobat Scraper](https://apify.com/studio-amba/jobat-scraper) — Belgian job board.
- [Pracuj Scraper](https://apify.com/studio-amba/pracuj-scraper) — Poland's largest job board.
- [InfoJobs Scraper](https://apify.com/studio-amba/infojobs-scraper) — Spain's largest job board.

### Disclaimer

This actor collects only publicly available job-offer data from apec.fr for legitimate use such as market research and job aggregation. Respect APEC's terms of service and applicable data-protection law when using the output.

# Actor input Schema

## `searchQuery` (type: `string`):

Job search keyword in French (e.g. 'chef de projet', 'developpeur', 'commercial', 'comptable'). Leave empty to browse the most recent cadre offers.

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

Filter by city, department name, or postal-code prefix (e.g. 'Paris', 'Lyon', '75', '69'). Matches the offer location text. Leave empty for all of France.

## `contractType` (type: `string`):

Filter by contract type.

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

Maximum number of job offers to scrape.

## `fetchDetails` (type: `boolean`):

Fetch each offer's detail page for the full job description, required profile, company description, and skills. Slower but richer. Turn off for faster, listing-only results.

## `brightDataApiKey` (type: `string`):

Optional. The actor already has an unblocking key configured; only set this to use your own Bright Data account.

## `proxyConfiguration` (type: `object`):

Proxy settings. French residential proxies recommended for reliable access.

## Actor input object example

```json
{
  "searchQuery": "chef de projet",
  "contractType": "all",
  "maxResults": 20,
  "fetchDetails": true,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "FR"
  }
}
```

# Actor output Schema

## `results` (type: `string`):

No description

# 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 = {
    "searchQuery": "chef de projet",
    "maxResults": 20,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "FR"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("studio-amba/apec-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 = {
    "searchQuery": "chef de projet",
    "maxResults": 20,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "FR",
    },
}

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

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

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

```

## CLI example

```bash
echo '{
  "searchQuery": "chef de projet",
  "maxResults": 20,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "FR"
  }
}' |
apify call studio-amba/apec-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,studio-amba/apec-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/98Pma0T9pIlZDCQV8/builds/B3jJHfEsdMOz82YYn/openapi.json
