France Travail Scraper $1💰 Jobs, Salaries & Apply Links
Pricing
from $1.00 / 1,000 results
France Travail Scraper $1💰 Jobs, Salaries & Apply Links
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.
Pricing
from $1.00 / 1,000 results
Rating
0.0
(0)
Developer
Abot API
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
7 days ago
Last modified
Categories
Security
Limited permissionsShare
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
- Pick a mode:
search(keywords plus filters) orurl(paste France Travail search links). - 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.
- Set Max listings (the run cap) and, if you want a hard per-search page limit, Max pages, then click Start.
- Download the dataset as JSON, CSV or Excel, or read it through the API.
Query-driven run:
{"mode": "search","queries": ["data analyst", "business analyst"],"location": "Paris","publishedDate": "Last Week","contractType": ["Permanent contract (CDI) | CDI"],"maxItems": 50}
Targeted domain and salary floor:
{"mode": "search","queries": ["cariste"],"location": "Lille","jobCategory": ["Logistics | Transport, Logistique"],"contractDuration": ["Full-time | Temps plein"],"minSalary": 2000,"maxItems": 30}
Paste a search URL:
{"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:
from apify_client import ApifyClientclient = 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:
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 classifiedNEW,UPDATED(withchangedFields),UNCHANGED(suppressed and not billed unlessemitUnchangedis on),REAPPEARED, orEXPIRED(only after a run that scanned this search's full scope end to end, and only withemitExpiredon). If you useemitExpired, 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 asEXPIREDeven though they are still online.stateKeynames 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.ignoreFieldsForChangeslists extra fields (dotted paths reach nested fields) whose changes should not make a jobUPDATED; they stay in the output. The first incremental run after the detail-source update re-matches your already tracked jobs once without reporting them asUPDATED, even though fields such as salary, skills, qualification, description and the neweducationLevel,romeLabelandupdatedAtare 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.
- Authorize a connector once under Apify > Settings > Integrations (Notion, Linear, Airtable, or Apify).
- Select it in the "Pipe results into your apps" input field. (If the picker is empty, you haven't authorized a connector yet.)
- For Notion, also set
notionParentPageUrlto 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. - Use
maxNotifyListingsto 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.
{"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:
| 💼 IrishJobs.ie Scraper Scrape job listings from IrishJobs.ie by keyword, location, job type, salary, recency, or... | 💼 HH.ru Jobs Scraper Scrape HH.ru job listings with 50+ structured fields. Search by filters or URLs and... |
| 💼 Jora Jobs Scraper Scrape Jora.com job listings across supported countries by keyword, filters, or URL... | 💼 Jobs.cz Scrape Czech job listings from Jobs.cz. Search builder (keyword, location, field, salary... |
| 💼 Reed.co.uk Scraper Scrape Reed.co.uk jobs, companies, courses and reviews by keyword, location, filters or... | 🏷️ FINN.no Jobs Scraper Scrape active FINN.no job listings by search filters or URL. Extract 30+ fields including... |
💬 Support
- 🐞 Found a bug or a missing field? Open a ticket on the Issues tab. 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.