# French Jobs Scraper: France Job Offers API (Open Data) (`capable_coach/emploi-france-aggregator`) Actor

French jobs scraper and France job offers API from open-licence sources only (Choisir le service public, employer open data, Jobicy): one normalised, deduplicated feed with contract type (CDI/CDD), remote level and salary annualised in EUR. For HR analytics, job boards and AI agents.

- **URL**: https://apify.com/capable_coach/emploi-france-aggregator.md
- **Developed by:** [jules imbert](https://apify.com/capable_coach) (community)
- **Categories:** Jobs, Lead generation, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.54 / 1,000 job offers

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## French Jobs Scraper & API — France job offers from open-licence sources (Emploi France)

**One normalised, deduplicated feed of French job offers, built for AI agents (Apify MCP server) and HR analytics — using only sources whose licence or terms allow reuse.**

🇫🇷 *Version française plus bas.*

### What it does

Give it keywords, a location, a remote preference and contract types. It queries several **legally reusable** sources in parallel and returns one clean dataset:

| Source | What it covers | Licence / terms |
|---|---|---|
| **Choisir le service public** (choisirleservicepublic.gouv.fr) | The official job board of the French civil service (State, territorial and hospital civil service): ~50,000 live offers | Licence Ouverte / Etalab 2.0 |
| **Employer & territorial open data** | Groupe BPCE (Banque Populaire, Caisse d'Epargne, Natixis…), Région Île-de-France, Ville de Tours / Tours Métropole, France Travail 7-day feeds for Hautes-Pyrénées and Pyrénées-Atlantiques | Licence Ouverte / Etalab 2.0 |
| **Jobicy remote jobs API** | 100 % remote jobs open to candidates based in France | Jobicy public API, attribution required |

Every record has the same schema, whatever the source:

- `title`, `company`, `location`, `city`, `departmentCode`, `department`, `region`
- `remoteLevel`: `full_remote` · `hybrid` · `on_site` · `unknown`
- `contractType`: `CDI` · `CDD` · `INTERIM` · `FREELANCE` · `STAGE` · `ALTERNANCE` · `FONCTION_PUBLIQUE` · `AUTRE`, plus `contractRaw`
- Salary: `salaryMin`, `salaryMax`, `salaryCurrency`, `salaryPeriod`, plus **`salaryAnnualMinEur` / `salaryAnnualMaxEur`**, annualised (×12 for monthly pay, ×1,607 for hourly, ×218 for daily) when the currency is EUR. `salaryText` keeps the raw string.
- `publishedAt`, `applicationDeadline`, `category`, `descriptionSnippet` (400 characters), `url`
- `source`, `sourceName`, `sourceLicense`, `attribution` (keep them when you republish)
- `alsoFoundOn`: the same offer found on another source

### Why use it

- **Clean legal footing.** No scraping of private job boards whose terms forbid it. No personal data: recruiter names, e-mails and phone numbers are never requested, and e-mails or phone numbers inside descriptions are masked.
- **Agent-ready.** It is a flat, typed schema with enums, so an LLM agent calling it through the Apify MCP server gets consistent fields across sources.
- **Cheap.** It uses plain HTTP with no browser and no proxy.

### Input example

```json
{
  "keywords": "développeur",
  "location": "Paris",
  "remote": "any",
  "contractTypes": [],
  "maxResults": 100,
  "sources": ["choisirleservicepublic", "opendata", "jobicy"],
  "keywordMatch": "title",
  "fetchDetails": true
}
```

- **`location`** accepts a city ("Lyon"), a département name or number ("Gironde", "69") or a région ("Île-de-France"). Cities are resolved with the official geo.api.gouv.fr. Leave it empty for all of France.
- **`remote`**:
  - `full_remote` returns full-remote offers wherever the employer is.
  - `hybrid` returns offers marked "télétravail possible".
- **`includeRemoteOutsideLocation`** also adds full-remote offers when a location is set.
- **`keywordMatch`**:
  - `title` (default) is precise: the keywords must appear in the job title. "développeur" does not match "développement".
  - `title_or_description` is broader.
- French keywords also match common English equivalents for remote listings (développeur → developer / software engineer, comptable → accountant…).

### Output example

```json
{
  "id": "choisirleservicepublic:O075260724001374",
  "title": "Développeur Windev - Crédit Municipal de Paris",
  "company": "Etablissements publics locaux",
  "location": "Paris (75)", "city": "Paris", "departmentCode": "75", "department": "Paris", "region": "Île-de-France",
  "remoteLevel": "hybrid",
  "contractType": "FONCTION_PUBLIQUE", "contractRaw": "Emploi ouvert aux titulaires et aux contractuels",
  "salaryMin": 45000, "salaryMax": 50000, "salaryCurrency": "EUR", "salaryPeriod": "year",
  "salaryAnnualMinEur": 45000, "salaryAnnualMaxEur": 50000, "salaryText": "45/50 K€ € brut/an",
  "publishedAt": "2026-10-05", "applicationDeadline": "2026-12-04",
  "category": "Numérique",
  "url": "https://choisirleservicepublic.gouv.fr/offre-emploi/developpeur-windev---credit-municipal-de-paris-reference-O075260724001374/",
  "source": "choisirleservicepublic", "sourceLicense": "Licence Ouverte / Etalab 2.0",
  "attribution": "Source : choisirleservicepublic.gouv.fr (DGAFP) — Licence Ouverte / Etalab 2.0",
  "alsoFoundOn": []
}
```

### Deduplication policy

1. Offers with the same URL are dropped.
2. Offers from the **same source are never merged**: each one has its own vacancy reference, and identical titles are often distinct posts (for example one per lycée).
3. Offers from **different sources** are merged only when they match on all of the following:

   - the same département;
   - the same canonical title (gender variants, H/F markers and employer suffixes are removed);
   - a compatible employer;
   - an unambiguous match: exactly one candidate on each side.

   The kept record lists the other one in `alsoFoundOn`.

### Pricing (pay per event)

Pay per event, with discounts on paid Apify plans:

| Apify plan | Per job offer | Per 1,000 |
|---|---|---|
| Free | $0.0009 | $0.90 |
| Starter (Bronze) | $0.00081 | $0.81 |
| Scale (Silver) | $0.000675 | $0.68 |
| Business (Gold) and above | $0.00054 | $0.54 |

Plus $0.0003 per run start. No extra platform-usage fee: compute and storage are included.

You only pay for unique offers that pass your filters. `maxResults` caps the cost.

### Limits (honest)

- **Coverage.** Most volume comes from the public sector and a few large employers. This is *not* a copy of Indeed, Welcome to the Jungle or APEC, whose terms forbid automated reuse.
- **Full remote.** Full-remote offers come only from Jobicy and are mostly international tech, sales and support roles. French full-remote jobs in trades such as accounting are rare in these sources.
- **Salary.** Public-sector offers often say "non renseignée", so salary coverage is partial.
- **Remote level.** `hybrid` for public-sector offers means the offer says "télétravail possible". It does not tell you how many days.
- **Employer name.** For some public-sector offers, `company` is the employer category ("Etablissements publics locaux"); the actual employer is then in the title.
- **Jobicy cache.** Jobicy data is cached for 60 minutes, per Jobicy's fair-use rules.

### Use cases

- **HR & labour-market analytics**: track hiring volume, contract types (CDI, CDD, internships) and salary ranges by city or keyword in France.
- **Job boards & newsletters**: feed a niche French job board or a weekly jobs newsletter with deduplicated offers and their source URLs.
- **Public-sector recruiting watch**: follow civil-service openings (State, territorial and hospital) for a region or a job family.
- **AI agents**: let an assistant search French jobs through the Apify MCP server and get structured JSON back.

### How to scrape French job offers

1. Enter **keywords** (e.g. `développeur`, `comptable`, `infirmier`) and a **location** (city, département or `France`).
2. Optionally pick a **remote** level, **contract types** and a **maximum number of results**.
3. Click **Start**. Download the dataset as JSON, CSV or Excel, or call the Actor through the Apify API.

### FAQ

**Does it scrape Indeed, LinkedIn, Welcome to the Jungle or France Travail's website?** No. Their terms forbid automated reuse. This Actor only uses sources whose licence or terms allow reuse (see the table above).

**Is there a French job search API I can call from code?** Yes: run this Actor through the Apify API (HTTP, Python or JavaScript client) and read the dataset. Each run returns a deduplicated list of offers.

**How fresh are the offers?** Sources are queried live at each run; use `publishedWithinDays` to keep only recent offers.

**Can I schedule it?** Yes. Use an Apify schedule (e.g. daily) and an integration (Google Sheets, Slack, webhook, Make, Zapier).

### Integrations and API

- **API**: run the Actor and read results over HTTP or with the Apify Python and JavaScript clients (see the **API** tab for ready-made code).
- **Schedules**: run it daily or weekly from Apify schedules.
- **Integrations**: send results to Google Sheets, Slack, e-mail, Airtable or a webhook, or connect it to Make, Zapier or n8n.
- **AI agents**: available as a tool through the Apify MCP server.

### Attribution

When you display or republish results, keep `attribution` and `url`:

- Etalab 2.0 requires mentioning the source and the date of last update.
- Jobicy requires a credit and a link to the original listing.

### Other Actors by the same developer

- [France Public Tenders Scraper (BOAMP)](https://apify.com/capable_coach/france-public-tenders): French public procurement notices.
- [France New Companies (BODACC + SIRENE)](https://apify.com/capable_coach/france-new-companies): newly registered French companies.
- [France Building Permits Scraper (Sitadel)](https://apify.com/capable_coach/france-building-permits): construction leads.
- [ATS Jobs Scraper](https://apify.com/capable_coach/ats-jobs-scraper): Greenhouse, Lever, Ashby, SmartRecruiters, Recruitee, Personio.

***

### 🇫🇷 Emploi France — offres d'emploi unifiées, sources ouvertes

**Un flux unique, normalisé et dédoublonné d'offres d'emploi en France. Il est conçu pour les agents IA (serveur MCP d'Apify) et l'analyse RH, et n'utilise que des sources dont la licence ou les conditions autorisent la réutilisation.**

#### Sources

- **Choisir le service public** : site officiel de l'emploi public, environ 50 000 offres, sous Licence Ouverte / Etalab 2.0.
- **Open data employeurs et territoires**, sous Licence Ouverte / Etalab 2.0 :
  - Groupe BPCE ;
  - Région Île-de-France ;
  - Ville de Tours et Tours Métropole ;
  - flux France Travail à 7 jours des Hautes-Pyrénées et des Pyrénées-Atlantiques.
- **API publique Jobicy** : offres 100 % télétravail ouvertes aux candidats en France, avec mention de la source obligatoire.

#### Ce que vous obtenez

- **Un schéma unique pour toutes les sources** : intitulé, employeur, lieu, département, région, `remoteLevel` (`full_remote` / `hybrid` / `on_site` / `unknown`), type de contrat normalisé, date de publication, date limite, extrait et lien.
- **Un salaire ramené à l'année en euros** (×12 pour un salaire mensuel, ×1 607 pour un taux horaire, ×218 pour un taux journalier).
- **Aucune donnée personnelle** : les noms, e-mails et téléphones des recruteurs ne sont jamais collectés, et ceux présents dans les textes sont masqués.
- **Un dédoublonnage prudent entre sources**, selon les règles décrites plus haut.

#### Entrée

- **Mots-clés**, en français ou en anglais.
- **Lieu** : ville, département (nom ou numéro) ou région.
- **Télétravail** : tous, 100 % télétravail, hybride ou sur site.
- **Types de contrat.**
- **Nombre maximum de résultats.**
- **Sources.**

#### Tarif

- 0,0009 $ par offre renvoyée (de 0,00081 $ à 0,00054 $ sur les offres payantes Apify) ;
- 0,0003 $ par lancement ;
- aucun frais de plateforme en plus.

#### Limites

- **Couverture** : surtout le secteur public et quelques grands employeurs. Ce n'est pas une copie d'Indeed, de Welcome to the Jungle ou de l'APEC, dont les CGU interdisent la réutilisation automatisée.
- **Salaire** : souvent non renseigné dans les offres publiques.
- **« hybride »** signifie « télétravail possible », sans nombre de jours.

# Actor input Schema

## `keywords` (type: `string`):

Job title or keywords, in French or English (e.g. "développeur", "comptable", "data analyst"). French terms are also matched against common English equivalents for remote listings.

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

City, département (name or number, e.g. "Paris", "69", "Gironde") or région ("Île-de-France"). Leave empty for all of France. Full-remote offers open to France are kept whatever the location when remote = full_remote or when 'Include full-remote offers' is on.

## `remote` (type: `string`):

Filter on the normalised remote level.

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

Keep only these normalised contract types. Empty = all.

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

Maximum number of unique offers returned (you are charged per returned offer).

## `sources` (type: `array`):

Sources to query. All are open-licence or public APIs that allow reuse.

## `keywordMatch` (type: `string`):

"title" = precise (keywords must be in the job title). "title_or_description" = broader.

## `publishedWithinDays` (type: `integer`):

Only offers published in the last N days. 0 = no limit.

## `includeRemoteOutsideLocation` (type: `boolean`):

Also return 100 % remote offers open to candidates in France even if a location is set.

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

For Choisir le service public, open each offer page to get salary range, télétravail, contract nature and deadline. Slower but richer.

## Actor input object example

```json
{
  "keywords": "développeur",
  "location": "Paris",
  "remote": "any",
  "contractTypes": [],
  "maxResults": 100,
  "sources": [
    "choisirleservicepublic",
    "opendata",
    "jobicy"
  ],
  "keywordMatch": "title",
  "publishedWithinDays": 0,
  "includeRemoteOutsideLocation": false,
  "fetchDetails": true
}
```

# Actor output Schema

## `offers` (type: `string`):

No description

## `overview` (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 = {
    "keywords": "développeur",
    "location": "Paris"
};

// Run the Actor and wait for it to finish
const run = await client.actor("capable_coach/emploi-france-aggregator").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 = {
    "keywords": "développeur",
    "location": "Paris",
}

# Run the Actor and wait for it to finish
run = client.actor("capable_coach/emploi-france-aggregator").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 '{
  "keywords": "développeur",
  "location": "Paris"
}' |
apify call capable_coach/emploi-france-aggregator --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,capable_coach/emploi-france-aggregator"
        }
    }
}
```

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/XhSDNMEcZrAvc7cfo/builds/U2EYL2gYPMR6SCPkb/openapi.json
