# France Job Scraper — WTTJ, France Travail and Hellowork (`jobsapi/france-job-search-scraper`) Actor

Extract complete public French job records from France Travail and Hellowork with bounded native HTTPS and transparent KVS diagnostics for client-rendered or access-boundary sources.

- **URL**: https://apify.com/jobsapi/france-job-search-scraper.md
- **Developed by:** [Jobs API](https://apify.com/jobsapi) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.99 / 1,000 france job records

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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 Job Scraper — WTTJ, France Travail and Hellowork

This local Actor collects complete, public job records from France Travail and Hellowork. It uses ordinary native HTTPS requests and Cheerio, verifies each candidate against its public detail page, normalizes public `itemprop` and `JobPosting` data, and keeps diagnostics in the key-value store instead of mixing them into the dataset.

### Modes

- `search`: search the selected `source` (`all` by default) with `keyword` and `location`.
- `searchMultiple`: run the bounded `searches` array; duplicate source IDs are suppressed.
- `single`: fetch one `jobUrl`.
- `multiple`: fetch `jobUrls`.
- `startUrls`: accept official public search or detail URLs and infer the source from each URL.
- `all` and `details` remain compatibility aliases for the old input contract.

The default run is bounded to three records per selected source and one search page. `maxItems` and `maxPages` are the only public workload controls; request timeout, request budget, headers, retries, proxy, and fingerprint settings are fixed internally. Search records are buffered and emitted only after successful public detail fetches, completeness checks, and direct-source receipts are available.

### Dataset

Every dataset row is a `job` record with a stable source-prefixed `id`, canonical public detail URL, title, employer, location, description and sanitized HTML description. Portable aliases (`companyName`, `descriptionText`, `url`, `applyUrl`, and `scrapedAt`) support cross-actor consumers without removing the richer source-native names. When exposed by the source it also contains sections, bullets, responsibilities, requirements, qualifications, benefits, skills, compensation, employment type, dates, company URL, application URL, and source metadata. Optional values are omitted rather than written as null, blank, placeholder, or empty-array values.

`RUN_SUMMARY` and its compatibility alias `OUTPUT` separate the Apify platform `status` (`SUCCEEDED` or `FAILED`) from dataset `resultStatus` (`COMPLETE`, `LIMITED`, `SKIPPED`, `DEFERRED`, or `FAILED`). `RUN_DIAGNOSTICS` stores structured request, parsing, identity, completeness, and source-boundary evidence; `RUN_REQUEST_RECEIPTS` stores HTTP request receipts; and `RUN_HEALTH` records aggregate run health. These artifacts remain in the key-value store, never as job dataset rows. A confirmed source barrier produces `SUCCEEDED` / `SKIPPED` and discards every buffered row. Generic transport failures and ambiguous empty parses are `DEFERRED`, not blocks or confirmed `NO_DATA`. If complete rows were stored before a later non-block error, they remain in the dataset and the outcome is `SUCCEEDED` / `LIMITED`; a fatal zero-row failure is `FAILED` / `FAILED`.

### Source boundaries

France Travail and Hellowork currently expose public HTML and detail data in ordinary local requests. WTTJ currently returns a client-rendered public shell without server-rendered job records for the tested search; the Actor records `NO_SERVER_RENDERED_DATA` and does not call private application APIs, use login, use a proxy, fingerprint evasion, CAPTCHA solving, or bypass an access boundary. A WTTJ detail row is emitted only if the public page itself exposes a complete `JobPosting` record.

`npm run validate` reads the default local dataset and KVS artifacts without modifying them; it checks strict job fields, duplicate IDs/URLs, diagnostic rows, status/count reconciliation, and null/blank/empty values. Historical rows and summaries from before the KVS-only receipt/status contract are reported as legacy warnings and are not rewritten.

Use the sources only where their current robots.txt and terms permit the intended collection, respect rate limits, and stop when a site presents an access boundary.

# Actor input Schema

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

search queries one source set; searchMultiple runs the searches array; single and multiple fetch detail URLs; startUrls accepts public search or detail URLs. all and details are retained as compatibility aliases.

## `source` (type: `string`):

Source set used by search modes. Direct URL modes infer the source from each official URL.

## `keyword` (type: `string`):

Public search keyword, for example developpeur or data analyst.

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

Optional public location filter supported by the selected board.

## `searches` (type: `array`):

Optional bounded search definitions used by searchMultiple. Each item can choose a source, keyword, location, and per-search item cap.

## `startUrls` (type: `array`):

HTTPS public search or detail URLs on France Travail, Hellowork, or WTTJ. The Actor validates the exact supported route before requesting it.

## `jobUrl` (type: `string`):

One official HTTPS job-detail URL for single mode.

## `jobUrls` (type: `array`):

Bounded list of official HTTPS job-detail URLs for multiple mode.

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

Maximum records per search task or direct URL list. Complete records only; duplicate IDs are suppressed.

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

Bounded pagination cap per search task. Native page parameters are used only on the official search route.

## Actor input object example

```json
{
  "mode": "search",
  "source": "all",
  "keyword": "developpeur",
  "location": "",
  "searches": [],
  "startUrls": [],
  "jobUrls": [],
  "maxItems": 3,
  "maxPages": 1
}
```

# Actor output Schema

## `dataset` (type: `string`):

No description

## `runSummary` (type: `string`):

No description

## `diagnostics` (type: `string`):

No description

## `requestReceipts` (type: `string`):

No description

## `runHealth` (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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("jobsapi/france-job-search-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 = {}

# Run the Actor and wait for it to finish
run = client.actor("jobsapi/france-job-search-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 '{}' |
apify call jobsapi/france-job-search-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,jobsapi/france-job-search-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/2vEbJs5YtG3MxfYd1/builds/CJxgjwvOHPKxYKpnA/openapi.json
