NewSearch millions of jobs from your AI agent with MCP
All posts
GuideTravail
Guide·Sep 1, 2026·8 min read

France Travail API: the free French jobs API, and its 1,150-result window

France Travail, the public employment service formerly called Pole emploi, publishes its live vacancy database as an open REST API - OAuth2 client credentials, no partner review, no contract. It is one of the best public job APIs in Europe and it has three hard edges: a 1,150-result ceiling per query, a French-only ROME taxonomy with no ISCO crosswalk, and a reuse licence that requires you to display the entire offer including its logo. Here is the working auth flow, the search parameters, the paging workaround, and the part of the French market the API cannot see.

Dvir Atias

Dvir Atias

Founder, JobsPipe

France runs one of the few genuinely open job APIs in Europe. France Travail, the public employment service renamed from Pôle emploi in January 2024, publishes its live vacancy database as a REST API that any developer can register for, authenticate against with OAuth2 and query in production - no partner review, no sales call, no contract. Next door in Germany the official StepStone API is write-side only. Measured against that, the French one looks remarkable.

It is also narrower than it first appears. This post covers the exact auth dance, the search endpoint and its parameters, the 1,150-result ceiling that decides your architecture, the ROME taxonomy you inherit with it, the reuse licence that constrains what you are allowed to build, and the part of the French market the API structurally cannot see.

Access and the OAuth dance

Access starts with a free account on francetravail.io, where you declare an application and subscribe it to the API you want. That gives you a client id and a client secret. Everything after that is standard OAuth2 client credentials against the partner realm:

curl -X POST \
  "https://entreprise.francetravail.fr/connexion/oauth2/access_token?realm=%2Fpartenaire" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=YOUR_CLIENT_ID" \
  -d "client_secret=YOUR_CLIENT_SECRET" \
  -d "scope=api_offresdemploiv2"

Both halves are live and answer honestly, which is easy to confirm before you have credentials at all. Post junk to the token endpoint and it returns 400 with {"error":"invalid_client"}. Call the search route with no bearer token and it returns 401. Nothing is hidden behind a contact form. You get a short-lived bearer token and send it as Authorization: Bearer on every call.

The search endpoint

One route does the work: https://api.francetravail.io/partenaire/offresdemploi/v2/offres/search. It takes camelCase query parameters - motsCles for keywords, codeROME for occupation, commune, departement and distance for geography, typeContrat for contract type, and minCreationDate plus maxCreationDate for a window, in ISO-8601:

curl -G "https://api.francetravail.io/partenaire/offresdemploi/v2/offres/search" \
  -H "Authorization: Bearer $TOKEN" \
  --data-urlencode "motsCles=data engineer" \
  --data-urlencode "departement=75" \
  --data-urlencode "minCreationDate=2026-08-01T00:00:00Z" \
  --data-urlencode "range=0-149"

The response has two halves. resultats is the list of offers. filtresPossibles is a set of aggregate counts across the facets, which is genuinely useful: you can read how a query distributes across contract types or experience levels without paging through it. Alongside search there are reference routes under /referentiel/ - communes, metiers and the rest - that hand you the code lists the filters expect.

The 1,150-result window

Here is the constraint that shapes every integration built on this API. Paging is done with a range parameter that defaults to 0-149, and three rules apply to it: a page can hold at most 150 results, the first element cannot exceed 1000, and the second cannot exceed 1149. So a single query tops out at 1,150 offers, walked as 0-149, 150-299, 300-449 and so on. The response carries a Content-Range telling you the true total, which is how you discover that your query matched far more than you can page through.

The workaround is the same one every capped API forces: slice the query until each slice fits. Split by minCreationDate and maxCreationDate into days or hours, or fan out across departments and ROME codes, then deduplicate. Budget for it - the published rate limit is 10 calls per second, with 99.8% advertised availability, so the ceiling is on breadth per query, not on throughput. Reed’s UK API has the same shape with different numbers, and any job feed you build on a public board will hit some version of it.

ROME: the taxonomy you inherit

Filter by occupation and you are using ROME, the Répertoire Opérationnel des Métiers et des Emplois - France’s national occupation reference, maintained by France Travail and exposed through its own API. A ROME code is one letter plus four digits. Version 4.0 went into service in March 2023 and is updated at least twice a year. Today it holds:

  • 1,911 fiches métiers, up from 532 in the previous version;
  • 14,301 appellations, the job titles that map onto those sheets;
  • 21,320 savoir-faire and 17,319 savoirs, the practical skills and knowledge areas;
  • 2,269 compétences socio-comportementales and 191 normed work contexts.

That is a serious taxonomy, and it is the reason French labour-market data is unusually well structured at the source. It is also a wall the moment your product crosses a border. ROME is French, it is not ISCO, and it ships no official crosswalk to one. If you are comparing a Paris role to a Berlin one you need a mapping layer of your own, or a corpus already normalised to a shared standard - which is the job ISCO-08 does globally and ESCO does across the EU. Note also that the ROME API is rate limited to 1 call per second, a tenth of the job-offers budget, so cache the reference data rather than looking it up per job.

The licence is the real constraint

Free access is not the same as free use. The offers database carries its own reuse licence, and the obligations attach to every offer you display. The reuser has to show the entirety of the content the API provides for that offer, including the logo attached to it. Any redistribution has to name France Travail as the source, state the date of the last update, say that reuse is governed by the licence, and carry a hyperlink back.

Read that before you design your UI, not after. It rules out the pattern most products reach for first: pulling three fields into your own card layout and ranking them next to inventory from somewhere else. If you are building a French job board that redirects to the original ad, the licence fits what you were going to do anyway. If you are building a matching engine, a sourcing tool or an analytics product, check the terms against your actual design early, because a full-content display requirement is a product decision, not a footer.

What the API cannot see

The scope is precise: the API returns offers collected by France Travail, plus offers from partners who consented to redistribution through it. That is a large and high-quality slice of the French market. It is not the French market. Roles that live only on an employer’s own careers site, on LinkedIn, on an ATS-hosted board or on a platform that does not syndicate to France Travail are outside it by construction, and no parameter brings them back.

For scale, here is the French footprint of an index built the other way round, from employers and ATS platforms rather than from one board. Measured on the day this post published: 155,344 active postings located in France, across 33,921 hiring companies. By source: LinkedIn 103,141, Indeed 49,900, public Workday tenants 1,917, SmartRecruiters 313. Paris alone accounts for 22,969, then Lyon 4,301, Toulouse 3,592 and Marseille 2,073.

The occupation mix is a useful corrective to how tech-heavy job data usually looks. The largest single ISCO-08 group in France is 5223, shop sales assistants, at 8,112 live postings - ahead of 2512, software developers, at 7,921. Then come commercial sales representatives (4,238) and sales and marketing managers (4,177). Contract labels follow the French pattern too: 120,453 full-time, 4,296 part-time, 3,878 internships and 884 explicitly temporary.

One honest gap in both directions: French ads rarely state a number. Only 492 of those 155,344 records carry a parsed numeric salary range. If compensation is your use case, the free-text salaire field on the France Travail side is the better read, which is one more argument for using both sources rather than picking one.

Reading the French market from the open layer

Everything above is classified on the way in - occupation code on 81% of French records, industry division on 64%, extracted skills on 61% - so the same question you would ask ROME can be asked across countries:

curl -X POST "https://api.jobspipe.dev/v1/jobs/search" \
  -H "Authorization: Bearer jp_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "job_country_code_or": ["FR"],
    "job_location_or": ["Paris", "Lyon"],
    "occupation_code_or": ["2512"],
    "posted_at_max_age_days": 30,
    "limit": 25
  }'

Swap occupation_code_or for skills_or to sweep by technology, add employment_type_or to isolate contract work, or widen job_country_code_or to compare France against its neighbours in one call. Records come back with the company, the URL of the original listing, the posting date, salary fields where the employer published one, and a status plus close date so dead vacancies leave your pipeline. The free tier returns 1,000 jobs a month.

So which path is yours

  • A French job board or aggregator that redirects. Use the France Travail API. It is free, real-time, well documented, and the licence obligations line up with what you were building.
  • French labour-market analysis. Also the France Travail API, plus the ROME API for the taxonomy. Plan the date-slicing around the 1,150-result window before you write the collector.
  • Anything that crosses borders or leaves the board. The API stops at the French public-service perimeter and at ROME. You need a corpus normalised to a shared occupation standard, covering the employer and ATS layer as well - which is what the jobs API comparison walks through option by option.

Live French postings with ISCO-08 occupations, industry codes and extracted skills - free tier, API key in 30 seconds.

Get a free API key