# France Travail Scraper $1💰 Jobs, Salaries & Apply Links (`abotapi/francetravail-fr-scraper`) Actor

Scrape job offers from France Travail with titles, companies, locations, salaries, contracts, descriptions, skills, employer details, and apply links. Search by keyword, location, contract, sector, salary, sort options, or paste job/search URLs.

- **URL**: https://apify.com/abotapi/francetravail-fr-scraper.md
- **Developed by:** [Abot API](https://apify.com/abotapi) (community)
- **Categories:** Jobs, Automation, Lead generation
- **Features:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 92.9% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 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.
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

## France Travail Scraper: Job Listings, Salaries and Employer Details

France Travail Scraper turns France Travail (formerly Pole emploi), France's national employment agency job board, into structured job data. Search by keyword plus filters such as location, contract type, working time, professional domain, salary floor, experience and qualification level, or paste France Travail search links directly. Each job comes back with employer, location, contract type, salary range, the full description, required skills and apply links. Export the results to JSON, CSV or Excel, or pull them straight into your app through the API.

### Why This Scraper?

- **Two ways to find jobs.** Search by keyword plus filters, or paste France Travail search links and let the actor walk them forward page by page.
- **Full job records, not just the search card.** Turn on detail fetching to add the full description, salary range, required skills, soft skills, employer block and apply/contact links to every result.
- **Location resolved the way the site itself resolves it.** A typed city, department or region name is looked up against France Travail's own location autocomplete and sent as the matching area code, instead of a free-text value the server can silently ignore.
- **Granular filters.** Contract type, working time, professional domain, salary floor, experience level, qualification level, and disability-inclusive or adapted-company employer badges.
- **One cap to think about.** Max listings is the single run cap; Max pages defaults to unlimited and simply stops once Max listings is reached.
- **Built for schedules.** Incremental mode returns only new, updated and reappeared jobs on recurring runs, with expired-job detection once a run completes a full scan of the tracked search.
- **Resume support.** Continue an interrupted large pull without re-scraping, or re-paying for, jobs a previous run already collected.

### Use Cases

- **Labor-market research:** track hiring volume, salary ranges and contract mix across regions, professional domains and time windows.
- **Recruiting intelligence:** benchmark an employer's or a sector's hiring patterns, contract mix and pay bands over time.
- **Data engineering and BI:** feed structured job records into a warehouse, dashboard, alerting tool or a search/matching engine.
- **Lead generation:** identify employers and recruiters actively hiring, with role-level context for outreach.
- **Workforce monitoring:** schedule the actor against a saved search and get notified only of new or changed postings.

### Data You Get

> Sample shape: values are illustrative placeholders, not from a live record.

| Field | Example |
| --- | --- |
| `type` | `"job"` |
| `id` | `"123XYZA"` |
| `url` | France Travail link to the job detail page |
| `title` | `"Développeur web"` |
| `company` | `"Sample Recruiter SARL"` |
| `location` | `"75 - Paris"` |
| `postalCode` | `"75015"` |
| `contractType` | `"CDI"` |
| `workTime` | `"Temps plein"` |
| `publishedAt` | `"Publié hier"` (the publication text the search card shows) |
| `updatedAt` | `"2026-01-15"` (ISO date the offer was last modified, when detail fetching is on) |
| `description` | full job description text (when detail fetching is on) |
| `salaryText` | `"35 - 45 k€ brut annuel"` |
| `salaryMin` / `salaryMax` | `35000` / `45000` |
| `salaryUnit` | `"YEAR"` |
| `experienceRequirements` | `["De 1 à 3 ans"]` (when detail fetching is on) |
| `skills` / `softSkills` | `["JavaScript", "SQL"]` / `["Autonomie"]` (when detail fetching is on) |
| `romeCodes` / `romeLabel` | `["M1805"]` / `"Développeur / Développeuse full-stack"` (when detail fetching is on) |
| `educationLevel` / `educationRequired` | `["Bac+5 et plus ou équivalents Informatique"]` / `false` (required diplomas, when detail fetching is on) |
| `recruiterName` | `"Sample Recruiter SARL"` |
| `employerPageUrl` / `applyActionUrl` | France Travail links (when detail fetching is on) |
| `industry` / `qualification` | `"Informatique"` / `"Non cadre"` (when detail fetching is on) |
| `inclusiveEmployer` / `adaptedCompany` | `false` / `false` |
| `scrapedAt` | `"2026-01-15T09:00:00.000Z"` |

In the Console the dataset has three views: **Job offers** (overview), **Offer details** and **Changes (incremental mode)**.

Every record also carries `seedId`, `seedType` and `seedValue` (which search produced it) and `pageIndex`; address fields `addressLocality`, `addressRegion`, `addressCountry` and `mapUrl`; employer fields `companySize`, `recruiterDescription`, `employerLogoUrl`, `employerPhone`, `employerEmail` and `contactActionUrl`; and `additionalInformation`, `jobLabels`, `employmentType`, `workHours` and `validThrough`. In incremental mode, records also carry `changeType`, `changedFields`, `firstSeenAt` and `lastSeenAt`.

### How to Use

1. Pick a **mode**: `search` (keywords plus filters) or `url` (paste France Travail search links).
2. For search mode, fill in your keywords and any filters (location, contract type, salary, professional domain, and so on). For url mode, paste one or more search URLs instead.
3. Set **Max listings** (the run cap) and, if you want a hard per-search page limit, **Max pages**, then click **Start**.
4. Download the dataset as JSON, CSV or Excel, or read it through the API.

**Query-driven run:**

```json
{
  "mode": "search",
  "queries": ["data analyst", "business analyst"],
  "location": "Paris",
  "publishedDate": "Last Week",
  "contractType": ["Permanent contract (CDI) | CDI"],
  "maxItems": 50
}
```

**Targeted domain and salary floor:**

```json
{
  "mode": "search",
  "queries": ["cariste"],
  "location": "Lille",
  "jobCategory": ["Logistics | Transport, Logistique"],
  "contractDuration": ["Full-time | Temps plein"],
  "minSalary": 2000,
  "maxItems": 30
}
```

**Paste a search URL:**

```json
{
  "mode": "url",
  "urls": [
    "https://candidat.francetravail.fr/offres/recherche?motsCles=developpeur&lieux=75D"
  ],
  "maxItems": 40
}
```

#### URL mode: what you can paste

URL mode accepts keyword searches, searches with a location code, a `range` (used as the start page), filters and sort, domain plus department listings, location-only listings, and single offer pages (`.../offres/recherche/detail/<id>`). The older candidat.pole-emploi.fr host is accepted and switched to the current France Travail host. A single offer page returns that one job with full details, including employer name and place. A pasted single offer link is always opened for its details whatever the Fetch full job details setting says (that setting governs search results only), and each delivered offer row is billed as one detail enrichment; an offer that is no longer online, or one suppressed as unchanged in incremental mode, is not billed. An offer link whose offer no longer exists ends the run with a warning and zero results, not an error.

Behavior change: the `urls` field now ships with a default list holding one example of each URL shape. If you switch to url mode and do not edit it, the run scrapes those examples (still limited by Max listings). Clear the list and paste your own URLs for a targeted run.

#### Run it from your code

Python:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("abotapi/francetravail-fr-scraper").call(run_input={"mode": "search", "queries": ["developpeur"], "location": "Paris"})
for job in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(job["title"], job["company"], job["salaryText"])
```

JavaScript:

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

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('abotapi/francetravail-fr-scraper').call({ mode: 'search', queries: ['developpeur'], location: 'Paris' });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the **Integrations** tab.

#### The one cap rule

**Max listings** (`maxItems`) is the single global cap on how many jobs the run returns, across every search. With several keywords or URLs, the cap is shared out evenly: each search returns at most Max listings divided by the number of searches (rounded up), and a search that finds fewer jobs does not pass its unused share to the others. For example, `maxItems: 50` with two keywords returns up to 25 jobs per keyword. **Max pages** (`maxPages`) defaults to unlimited and simply stops once Max listings is reached; set it only when you also want a hard per-search page limit (20 jobs per page) regardless of Max listings.

#### Resume and recurring updates

- **Resume** (`resumeFromRunId`) continues one interrupted run: paste its run or dataset ID and the actor reads every job id it already saved there, then appends only new jobs, so you do not pay twice.
- **Incremental mode** (`incrementalMode`) is for scheduled runs over the same search. Each job is classified `NEW`, `UPDATED` (with `changedFields`), `UNCHANGED` (suppressed and not billed unless `emitUnchanged` is on), `REAPPEARED`, or `EXPIRED` (only after a run that scanned this search's full scope end to end, and only with `emitExpired` on). If you use `emitExpired`, set Max listings comfortably above the total number of jobs your searches return: when Max listings is smaller than a results page, the jobs beyond it on that page are skipped without being checked, and can be reported as `EXPIRED` even though they are still online. `stateKey` names or shares the stored state; leaving it empty derives a key automatically from the mode, queries, filters or URLs, and the detail-fetch setting, so differently configured searches never mix state. `ignoreFieldsForChanges` lists extra fields (dotted paths reach nested fields) whose changes should not make a job `UPDATED`; they stay in the output. The first incremental run after the detail-source update re-matches your already tracked jobs once without reporting them as `UPDATED`, even though fields such as salary, skills, qualification, description and the new `educationLevel`, `romeLabel` and `updatedAt` are now filled in; from the following run on, a real change to any of those fields is reported and billed as usual. Page position, search-query fields, the scrape time and relative publication text are always ignored when comparing, so a job that moves to another page or is matched by another search is not reported as changed. With incremental mode off, output is exactly as before.

### Send results into your apps (MCP connectors)

Optionally pipe the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. This is an extra delivery step **after** the dataset push has finished: the Apify dataset is never changed.

**What gets written to the connector:** a condensed, human-readable **summary** of each job, not the full JSON. Each item becomes one entry with a **title** and its key fields flattened to plain text. The **complete record always stays in the Apify dataset**.

1. Authorize a connector once under **Apify > Settings > Integrations** (Notion, Linear, Airtable, or Apify).
2. Select it in the **"Pipe results into your apps"** input field. (If the picker is empty, you haven't authorized a connector yet.)
3. For **Notion**, also set `notionParentPageUrl` to the page under which job pages should be created. This field is required only for the Notion connector; if it's missing, the actor logs a warning and skips the Notion export instead of failing the run.
4. Use `maxNotifyListings` to cap how many items are written to each connector per run. It never affects how many items land in the dataset.

The connection is mediated by Apify's MCP proxy, so this actor never sees your third-party credentials. Leave the field empty to skip.

### Input Parameters

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `mode` | string | `search` | `search` (keywords plus filters) or `url` (paste France Travail search links). |
| `queries` | array | `["developpeur"]` | Search keywords, one per line. Each line runs its own search. |
| `location` | string | `Paris` | City, department code, region, or postal area, applied to every search. Free text is resolved to the site's own area code through its autocomplete; unresolved text is sent as is. Leave empty to search across all of France. |
| `radius` | integer | `10` | Search radius in kilometres around the location. `0` means no radius. |
| `publishedDate` | string | `Last Week` | Keep only jobs posted within this window: `Last 24 hours`, `Last 3 Days`, `Last Week`, `Last 2 Weeks`, or `Last Month`. |
| `contractType` | array | `["Permanent contract (CDI) \| CDI"]` | Contract types to include (CDI, CDD, Intérim, apprenticeship and more). |
| `contractDuration` | array | `["Full-time \| Temps plein"]` | Working time. Use the full labels exactly: `Full-time \| Temps plein`, `Part-time \| Temps partiel`, `Not specified \| Non renseignée`; other spellings are ignored. |
| `jobCategory` | array | `["IT \| Informatique, Télécommunication"]` | Professional domains to include (IT, healthcare, logistics, hospitality and more). |
| `minSalary` | integer | `0` | Drop listings below this minimum monthly gross salary in EUR. `0` keeps all. |
| `experience` | array | (none) | Experience levels to include, matched by keywords in the job detail page's experience text, so it needs Fetch full job details on. Use the full labels exactly: `Entry-level \| Moins de 1 an`, `Mid-level \| De 1 à 3 ans`, `Senior \| Plus de 3 ans`, `Not specified \| Non renseignée`. Matching is approximate: neighbouring levels can overlap. |
| `seniority` | array | (none) | Qualification level. Use the full labels exactly: `Executive \| Cadre`, `Non-executive \| Non cadre`; other spellings are ignored. |
| `onlyFranceTravail` | boolean | `false` | Keep only offers published directly on France Travail, excluding partner offers. |
| `inclusiveEmployer` | boolean | `false` | Keep only offers from employers showing the disability-inclusive commitment badge. |
| `adaptedCompany` | boolean | `false` | Keep only offers from adapted companies. |
| `sortBy` | string | `Newest \| Date` | Sort by relevance or by publication date. |
| `urls` | array | one example per URL shape | France Travail search or single offer URLs (url mode). |
| `maxItems` | integer | `20` | The run cap: stop after this many jobs across all searches, shared evenly between searches. |
| `maxPages` | integer | `0` | Per-search page limit (20 jobs per page). `0` means unlimited, stops at Max listings. |
| `fetchDetails` | boolean | `true` | Open each job page for the full description, salary, skills, employer block and apply links, billed as a detail-enrichment surcharge per enriched job returned. |
| `resumeFromRunId` | string | (none) | Continue one interrupted run without re-scraping, or re-paying for, jobs it already collected. |
| `incrementalMode` | boolean | `false` | Return only new, updated and reappeared jobs on repeated scheduled runs of the same search. |
| `stateKey` | string | (none) | Name or share an incremental-mode monitoring campaign. |
| `emitUnchanged` | boolean | `false` | Also return (and bill) jobs that have not changed since the last run. |
| `emitExpired` | boolean | `false` | Also return (and bill) jobs no longer found, once a run completes a full scan. |
| `ignoreFieldsForChanges` | string\[] | `[]` | Incremental mode only: fields whose changes do not make a job `UPDATED` (they stay in the output). |
| `proxy` | object | Apify datacenter proxy | Connection settings. |
| `mcpConnectors` | array | (none) | Optional: send a summary of each job to apps you authorized under Integrations. |
| `notionParentPageUrl` | string | (none) | Notion connector only: page under which job pages are created. |
| `maxNotifyListings` | integer | `50` | Cap on items written to each connector per run. Does not affect the dataset. |

### Output Example

> Sample shape: values are illustrative placeholders, not from a live record.

```json
{
  "type": "job",
  "id": "123XYZA",
  "url": "https://candidat.francetravail.fr/offres/recherche/detail/123XYZA",
  "title": "Développeur web",
  "seedId": "a1b2c3d4e5f6",
  "seedType": "query",
  "seedValue": "developpeur",
  "pageIndex": 1,
  "company": "Sample Recruiter SARL",
  "location": "75 - Paris",
  "postalCode": "75015",
  "addressLocality": "Paris",
  "addressRegion": null,
  "addressCountry": "France",
  "mapUrl": null,
  "contractType": "CDI",
  "workTime": "Temps plein",
  "publishedAt": "Publié hier",
  "updatedAt": "2026-01-15",
  "validThrough": null,
  "description": "Sample job description text describing the role, responsibilities and required profile.",
  "salary": 45000,
  "salaryCurrency": "EUR",
  "salaryMin": 35000,
  "salaryMax": 45000,
  "salaryUnit": "YEAR",
  "salaryText": "35 - 45 k€ brut annuel",
  "employmentType": "FULL_TIME",
  "workHours": "Temps plein",
  "experienceRequirements": ["De 1 à 3 ans"],
  "skills": ["JavaScript", "SQL"],
  "softSkills": ["Autonomie"],
  "additionalInformation": [],
  "companySize": "50 à 99 salariés",
  "recruiterName": "Sample Recruiter SARL",
  "recruiterDescription": null,
  "employerPageUrl": "https://recrute.francetravail.fr/page-employeur/sample-recruiter-000",
  "employerLogoUrl": null,
  "employerPhone": null,
  "employerEmail": null,
  "applyActionUrl": "https://candidat.francetravail.fr/offres/recherche/detail.detailstandalone.postuler.contact:chargercontact?t:ac=123XYZA",
  "contactActionUrl": "https://candidat.francetravail.fr/offres/recherche/detail.detailstandalone.postuler.contact:chargercontact?t:ac=123XYZA",
  "industry": "Informatique",
  "qualification": "Non cadre",
  "jobLabels": [],
  "romeCodes": ["M1805"],
  "romeLabel": "Développeur / Développeuse full-stack",
  "educationLevel": ["Bac+5 et plus ou équivalents Informatique"],
  "educationRequired": false,
  "inclusiveEmployer": false,
  "adaptedCompany": false,
  "scrapedAt": "2026-01-15T09:00:00.000Z"
}
```

### Plan Requirement

Billing has three parts: a one-time Actor Start charge per run, a charge for each job returned, and a detail-enrichment charge for each returned job whose full details were fetched. **Fetch full job details is ON by default**, so the detail-enrichment charge applies to a default run; switch it off to pay only for Actor Start and jobs returned (a pasted single offer link is still billed one detail enrichment per delivered row, see URL mode). Jobs dropped by a filter or suppressed as unchanged are not charged. The **Pricing** tab shows the current rates. Use **Max listings** to cap the cost of any run.

No special plan is needed. The default connection uses Apify datacenter proxies, which loaded France Travail search and job pages in our checks. If a connection is refused the actor rotates to fresh connections automatically and, on a free plan (no residential proxies), continues on a backup connection with a warning in the log; some pages may then be unavailable. You can supply your own proxy URLs in the Connection setting.

### FAQ

#### How much does it cost?

You pay a one-time Actor Start charge per run, a charge per job returned, and a detail-enrichment charge per returned job whose details were fetched. Fetch full job details is on by default; switch it off to skip that charge for search results (pasted single offer links are always billed one detail enrichment per delivered row). The **Pricing** tab shows the current rates. Use **Max listings** to cap the cost of any run.

#### Is it legal to scrape France Travail?

This actor collects only publicly available job-posting information from France Travail. You are responsible for how you use it: follow France Travail's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution.

#### Why isn't my location filter narrowing the results?

Location is resolved against France Travail's own autocomplete before the search runs, so a typed city or department name is normally matched to the site's own area code. When the autocomplete offers suggestions but none closely matches your text, the actor takes the site's top suggestion, as the search box would. Only when the autocomplete returns nothing at all (or the lookup fails) does the actor send your text verbatim, and France Travail then ignores an unrecognized value and returns nationwide results instead of erroring. Check the run log: it states which code and place name your location was resolved to, or that it was sent as is. Typing a precise name, or a site code such as `75D`, avoids surprises.

#### Can I get only new or changed jobs on a schedule?

Yes. Schedule the actor from the **Schedules** tab and turn on **Incremental mode**. Each run then returns only new, updated and reappeared jobs, and unchanged ones are not billed unless you also turn on Emit unchanged.

#### Why did a run return zero jobs, and why did some runs fail instead?

Most rejected requests are retried automatically and simply narrow what a search returns; if every attempt for a search still fails, that search's log line says so and the run continues with the next search, so a hard connection issue usually shows up as fewer or zero jobs rather than a failed run. A run fails outright only in a few specific cases: no working connection at all could be set up, a pasted Resume run or dataset ID could not be read, or Incremental mode already has saved state for this exact search and you also passed a Resume run ID (a conflicting combination). The run's status message always explains which one applies.

#### Can I use it with AI agents or MCP?

Yes. Call it from any Apify integration or MCP client, and use the connector field to push results into Notion, Linear or Airtable.

### 🔗 Want more jobs data?

Pair this actor with these related scrapers from the same team:

<table>
<tr><td>💼 <a href="https://apify.com/abotapi/irishjobs-ie"><b>IrishJobs.ie Scraper</b></a><br>Scrape job listings from IrishJobs.ie by keyword, location, job type, salary, recency, or...</td><td>💼 <a href="https://apify.com/abotapi/hh-ru-jobs-scraper"><b>HH.ru Jobs Scraper</b></a><br>Scrape HH.ru job listings with 50+ structured fields. Search by filters or URLs and...</td></tr>
<tr><td>💼 <a href="https://apify.com/abotapi/jora-jobs-scraper"><b>Jora Jobs Scraper</b></a><br>Scrape Jora.com job listings across supported countries by keyword, filters, or URL...</td><td>💼 <a href="https://apify.com/abotapi/jobs-cz-scraper"><b>Jobs.cz</b></a><br>Scrape Czech job listings from Jobs.cz. Search builder (keyword, location, field, salary...</td></tr>
<tr><td>💼 <a href="https://apify.com/abotapi/reed-co-uk-scraper"><b>Reed.co.uk Scraper</b></a><br>Scrape Reed.co.uk jobs, companies, courses and reviews by keyword, location, filters or...</td><td>🏷️ <a href="https://apify.com/abotapi/finn-no-jobs-scraper"><b>FINN.no Jobs Scraper</b></a><br>Scrape active FINN.no job listings by search filters or URL. Extract 30+ fields including...</td></tr>
</table>

👉 [Browse all abotapi scrapers](https://apify.com/abotapi)

### 💬 Support

- 🐞 **Found a bug or a missing field?** Open a ticket on the [Issues tab](https://apify.com/abotapi/francetravail-fr-scraper/issues/open). We usually reply within hours.
- 🛠️ **Need another site, extra fields or a private build?** Email <contact@abotapi.com>.
- ⭐ **Enjoying it?** A quick review on the actor page helps other users find it.

# Actor input Schema

## `mode` (type: `string`):

search = build a search from keywords and filters. url = paste one or more France Travail search URLs and walk them forward.

## `queries` (type: `array`):

Job titles, skills, or hiring phrases such as "data analyst", "cariste", or "assistant comptable". Each line runs a separate search.

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

A city, department code, region, or postal area applied to all searches (e.g. "Paris", "75D", "69000"). Free text is resolved to the site's own lieu code through its autocomplete (Paris -> 75D); a non-code value sent verbatim is IGNORED by the server and returns nationwide results, so unresolved text is still sent as-is. Leave empty to search across France.

## `radius` (type: `integer`):

Radius around the location, in kilometres (0 = no radius). Only used when a location is set.

## `publishedDate` (type: `string`):

Keep only jobs posted within the chosen window.

## `contractType` (type: `array`):

Contract types to include (permanent, fixed-term, temporary, apprenticeship, …). Allowed values: CDI, CDD, Intérim, CDI Intérimaire, Saisonnier, Contrat apprentissage, Cont. professionnalisation, Prépa.opérationnel.emploi, CDI de chantier ou d'opération, Contrat d'Engagement Educatif ….

## `contractDuration` (type: `array`):

Keep only full-time, part-time, or unspecified working-time listings. Allowed values: Temps plein, Temps partiel, Non renseignée.

## `jobCategory` (type: `array`):

Professional domains to include (IT, healthcare, logistics, hospitality, construction, …). Allowed values: Achats, Comptabilité, Gestion, Arts, Artisanat d'art, Banque, Assurance, Bâtiment, Travaux Publics, Commerce, Vente, Communication, Multimédia, Conseil, Etudes, Direction d'entreprise, Espaces verts et naturels, Agriculture, Pêche, Soins aux animaux, Hôtellerie - Restauration, Tourisme, Animation ….

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

Drop listings whose salary is below this monthly gross amount. Leave at 0 to keep all.

## `experience` (type: `array`):

Experience requirements to include (applied client-side from the job detail). Allowed values: Moins de 1 an, De 1 à 3 ans, Plus de 3 ans, Non renseignée.

## `seniority` (type: `array`):

Executive, non-executive, or unspecified qualification levels. Allowed values: Cadre, Non cadre, Non renseignée.

## `onlyFranceTravail` (type: `boolean`):

When ON, keep only offers published directly on France Travail (partner offers excluded).

## `inclusiveEmployer` (type: `boolean`):

When ON, keep only offers from employers showing the Handi-engage commitment.

## `adaptedCompany` (type: `boolean`):

When ON, keep only offers from adapted companies (entreprises adaptées).

## `sortBy` (type: `string`):

Sort by relevance or by publication date.

## `urls` (type: `array`):

One or more France Travail URLs: search listings (keyword, location, domain, filters, sort, optional range as start page) or single offer pages (.../offres/recherche/detail/<id>). The older candidat.pole-emploi.fr host is accepted. Filters above are ignored in this mode. Leaving the default list runs one example of each URL shape; clear it and paste your own URLs for a targeted run.

## `maxItems` (type: `integer`):

Global cap on the number of jobs to return across all searches. This is THE cap — Max pages defaults to unlimited and stops at this number.

## `maxPages` (type: `integer`):

Per-search page limit (20 jobs per page). 0 means no page limit — the run stops at Max listings.

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

Open each job page to add the full description, salary range, experience, skills, soft skills, employer block, and apply/contact links. Adds one request per job (billed as a detail-enrichment surcharge). Turn off for a faster, lighter run using only the search-card fields. Pasted single offer links are always opened for their details and billed one detail enrichment per delivered row.

## `proxy` (type: `object`):

Connection used to reach the site. The default works out of the box; the actor rotates connections automatically when one is refused.

## `resumeFromRunId` (type: `string`):

Paste a previous run ID or dataset ID to continue a large crawl of job listings without returning or charging for jobs already collected there. Use this after an interrupted run, or when continuing a job catalogue pull in another run. For recurring daily monitoring of the same search, use Incremental mode below instead.

## `incrementalMode` (type: `boolean`):

Turn this on for daily or recurring monitoring. The first run returns all matching jobs as NEW. Later runs normally return only NEW, UPDATED, and REAPPEARED jobs. Turn on "Emit unchanged" or "Emit expired" only when you also want those jobs returned (and billed). State is kept separately for each mode/queries/filters/URL and detail-mode setup; use State key when you want to name or deliberately share a monitoring campaign. To continue one specific interrupted run instead, use Resume from a previous run above.

## `stateKey` (type: `string`):

Optional. Name this monitoring campaign to keep its state stable, or to deliberately share state across differently-configured runs. Leave empty to let the actor derive a key automatically from the mode/queries/filters/URL and detail settings — different searches then never mix state with each other.

## `emitUnchanged` (type: `boolean`):

Off by default. Turn on to also return jobs that have not changed since the last run, marked UNCHANGED. This returns — and bills — extra rows you already have, so leave it off unless you specifically want the full snapshot every run.

## `emitExpired` (type: `boolean`):

Off by default. Turn on to also return jobs that were present in a previous run but are no longer found, marked EXPIRED. Only produced once a run has fully scanned the tracked search — not when Max listings capped it or when Resume was used. This returns — and bills — extra synthetic rows, so leave it off unless you need expiry tracking.

## `ignoreFieldsForChanges` (type: `array`):

Incremental mode only. Field names (for example salaryText or description; a dotted path like a.b reaches nested fields) whose changes should NOT make a job UPDATED. The fields stay in the output and are never removed. Page position, search-query fields, the scrape time and relative publication text are always ignored. The id can never be ignored. Leave empty to compare everything else.

## `mcpConnectors` (type: `array`):

Optionally send a condensed summary of each result into the apps you already use, via Model Context Protocol (MCP) connectors. The full record stays in the dataset. Leave empty to skip. Supported: Notion, Linear, Airtable, Apify.

## `notionParentPageUrl` (type: `string`):

URL (or id) of the Notion page under which item pages are created. Required to enable the Notion export; ignored by other connectors.

## `maxNotifyListings` (type: `integer`):

Cap on items written to each connector per run. Does not affect the dataset.

## Actor input object example

```json
{
  "mode": "search",
  "queries": [
    "developpeur"
  ],
  "location": "Paris",
  "radius": 10,
  "publishedDate": "Last Week",
  "contractType": [
    "Permanent contract (CDI) | CDI"
  ],
  "contractDuration": [
    "Full-time | Temps plein"
  ],
  "jobCategory": [
    "IT | Informatique, Télécommunication"
  ],
  "minSalary": 0,
  "experience": [],
  "seniority": [],
  "onlyFranceTravail": false,
  "inclusiveEmployer": false,
  "adaptedCompany": false,
  "sortBy": "Newest | Date",
  "urls": [
    "https://candidat.francetravail.fr/offres/recherche?motsCles=developpeur",
    "https://candidat.francetravail.fr/offres/recherche?motsCles=developpeur&lieux=75D&offresPartenaires=true&range=0-19&rayon=10&tri=0",
    "https://candidat.francetravail.fr/offres/recherche?motsCles=developpeur&range=20-39",
    "https://candidat.francetravail.fr/offres/recherche?typeContrat=CDI&lieux=69D&tri=1",
    "https://candidat.francetravail.fr/offres/recherche?domaine=M&motsCles=cariste&departement=69",
    "https://candidat.francetravail.fr/offres/recherche/detail/215PPHG",
    "https://candidat.francetravail.fr/offres/recherche/detail/8265879",
    "https://candidat.pole-emploi.fr/offres/recherche?motsCles=comptable"
  ],
  "maxItems": 20,
  "maxPages": 0,
  "fetchDetails": true,
  "proxy": {
    "useApifyProxy": true
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "maxNotifyListings": 50
}
```

# Actor output Schema

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

No description

## `detail` (type: `string`):

No description

## `changes` (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 = {
    "mode": "search",
    "queries": [
        "developpeur"
    ],
    "location": "Paris",
    "radius": 10,
    "publishedDate": "Last Week",
    "contractType": [
        "Permanent contract (CDI) | CDI"
    ],
    "contractDuration": [
        "Full-time | Temps plein"
    ],
    "jobCategory": [
        "IT | Informatique, Télécommunication"
    ],
    "minSalary": 0,
    "experience": [],
    "seniority": [],
    "onlyFranceTravail": false,
    "inclusiveEmployer": false,
    "adaptedCompany": false,
    "sortBy": "Newest | Date",
    "urls": [
        "https://candidat.francetravail.fr/offres/recherche?motsCles=developpeur",
        "https://candidat.francetravail.fr/offres/recherche?motsCles=developpeur&lieux=75D&offresPartenaires=true&range=0-19&rayon=10&tri=0",
        "https://candidat.francetravail.fr/offres/recherche?motsCles=developpeur&range=20-39",
        "https://candidat.francetravail.fr/offres/recherche?typeContrat=CDI&lieux=69D&tri=1",
        "https://candidat.francetravail.fr/offres/recherche?domaine=M&motsCles=cariste&departement=69",
        "https://candidat.francetravail.fr/offres/recherche/detail/215PPHG",
        "https://candidat.francetravail.fr/offres/recherche/detail/8265879",
        "https://candidat.pole-emploi.fr/offres/recherche?motsCles=comptable"
    ],
    "maxItems": 20,
    "maxPages": 0,
    "fetchDetails": true,
    "proxy": {
        "useApifyProxy": true
    },
    "incrementalMode": false,
    "emitUnchanged": false,
    "emitExpired": false
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/francetravail-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 = {
    "mode": "search",
    "queries": ["developpeur"],
    "location": "Paris",
    "radius": 10,
    "publishedDate": "Last Week",
    "contractType": ["Permanent contract (CDI) | CDI"],
    "contractDuration": ["Full-time | Temps plein"],
    "jobCategory": ["IT | Informatique, Télécommunication"],
    "minSalary": 0,
    "experience": [],
    "seniority": [],
    "onlyFranceTravail": False,
    "inclusiveEmployer": False,
    "adaptedCompany": False,
    "sortBy": "Newest | Date",
    "urls": [
        "https://candidat.francetravail.fr/offres/recherche?motsCles=developpeur",
        "https://candidat.francetravail.fr/offres/recherche?motsCles=developpeur&lieux=75D&offresPartenaires=true&range=0-19&rayon=10&tri=0",
        "https://candidat.francetravail.fr/offres/recherche?motsCles=developpeur&range=20-39",
        "https://candidat.francetravail.fr/offres/recherche?typeContrat=CDI&lieux=69D&tri=1",
        "https://candidat.francetravail.fr/offres/recherche?domaine=M&motsCles=cariste&departement=69",
        "https://candidat.francetravail.fr/offres/recherche/detail/215PPHG",
        "https://candidat.francetravail.fr/offres/recherche/detail/8265879",
        "https://candidat.pole-emploi.fr/offres/recherche?motsCles=comptable",
    ],
    "maxItems": 20,
    "maxPages": 0,
    "fetchDetails": True,
    "proxy": { "useApifyProxy": True },
    "incrementalMode": False,
    "emitUnchanged": False,
    "emitExpired": False,
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/francetravail-fr-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 '{
  "mode": "search",
  "queries": [
    "developpeur"
  ],
  "location": "Paris",
  "radius": 10,
  "publishedDate": "Last Week",
  "contractType": [
    "Permanent contract (CDI) | CDI"
  ],
  "contractDuration": [
    "Full-time | Temps plein"
  ],
  "jobCategory": [
    "IT | Informatique, Télécommunication"
  ],
  "minSalary": 0,
  "experience": [],
  "seniority": [],
  "onlyFranceTravail": false,
  "inclusiveEmployer": false,
  "adaptedCompany": false,
  "sortBy": "Newest | Date",
  "urls": [
    "https://candidat.francetravail.fr/offres/recherche?motsCles=developpeur",
    "https://candidat.francetravail.fr/offres/recherche?motsCles=developpeur&lieux=75D&offresPartenaires=true&range=0-19&rayon=10&tri=0",
    "https://candidat.francetravail.fr/offres/recherche?motsCles=developpeur&range=20-39",
    "https://candidat.francetravail.fr/offres/recherche?typeContrat=CDI&lieux=69D&tri=1",
    "https://candidat.francetravail.fr/offres/recherche?domaine=M&motsCles=cariste&departement=69",
    "https://candidat.francetravail.fr/offres/recherche/detail/215PPHG",
    "https://candidat.francetravail.fr/offres/recherche/detail/8265879",
    "https://candidat.pole-emploi.fr/offres/recherche?motsCles=comptable"
  ],
  "maxItems": 20,
  "maxPages": 0,
  "fetchDetails": true,
  "proxy": {
    "useApifyProxy": true
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false
}' |
apify call abotapi/francetravail-fr-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,abotapi/francetravail-fr-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/1OvR0Wt04Z8o6Cw1a/builds/OsrmgnNIjsNxk8ajH/openapi.json
