# HelloWork Jobs Scraper (`solidcode/hellowork-scraper`) Actor

\[💰 $0.95 / 1K] Extract job listings from HelloWork — France's leading job board. Search by keyword and city, filter by contract type, telework, and date posted, and get titles, companies, salaries, full descriptions, and apply links.

- **URL**: https://apify.com/solidcode/hellowork-scraper.md
- **Developed by:** [SolidCode](https://apify.com/solidcode) (community)
- **Categories:** Jobs, Automation, Developer tools
- **Features:** Limited permissions
- **Stats:** 252 total users, 105 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.95 / 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

## HelloWork Jobs Scraper

Pull job listings from HelloWork — France's leading job board — at scale, complete with French-format salary ranges, contract codes (CDI / CDD / Alternance / Stage / Intérim / Freelance), telework mode, hiring company, sector, full descriptions, and ISO-8601 posted dates. Built for French recruiters, EU talent-acquisition teams, and salary-benchmarking analysts who need clean, structured French job-market data without parsing HelloWork's French HTML one page at a time.

### Why This Scraper?

- **Six French contract types in one filter** — CDI (permanent), CDD (fixed-term), Alternance (work-study), Stage (internship), Intérim (temporary), and Freelance / Indépendant. The biggest filter facet on HelloWork, exposed as a multi-select.
- **Four telework modes** — Full remote (télétravail complet), Partial, Occasional, and On-site only. Filter the French remote-job market without learning the French labels.
- **Parsed salary ranges in euros** — every row carries `salaryMin`, `salaryMax`, `salaryCurrency` (EUR), and `salaryPeriod` (YEAR / MONTH / DAY / HOUR), parsed from French formats like `50 000 - 55 000 € / an` including narrow no-break-space thousand separators.
- **Five recency windows** — Any time, Last 24 hours, Last 3 days, Last 7 days, or Last 30 days. Surface only fresh leads when you need them.
- **Minimum annual salary filter** — set `minSalary: 60000` and HelloWork constrains the search server-side before pagination starts.
- **Multi-keyword + multi-URL batch input** — run `["développeur", "data engineer", "product manager"]` in one go, or paste any HelloWork search URLs you already built in the UI. Mix both in the same run.
- **Up to 5,000 jobs per search** — sustained pagination across HelloWork's ~30-per-page SERP, with overshoot-don't-trim semantics that keep your final page whole.
- **Structured detail extraction** — title, hiring company, full HTML and plain-text description, 200-character snippet, required skills array, sector (Services aux Entreprises, Tech, etc.), education and experience requirements, valid-through date, and stable HelloWork `jobId` on every row.

### Use Cases

**Recruitment & Talent Sourcing**

- Build a target-company list of every employer hiring CDI développeurs in Île-de-France this week
- Track Alternance and Stage postings across France for student-pipeline programs
- Find freelance and Intérim opportunities matched to specific skill sets

**Salary Benchmarking**

- Build EUR salary bands per role and city from parsed `salaryMin` / `salaryMax` ranges
- Compare CDI vs. Freelance compensation for the same job title
- Track salary inflation over time by re-running the same query monthly

**Market & Competitive Intelligence**

- Monitor which French companies are hiring fastest in your sector
- Benchmark competitor headcount expansion by counting active listings per employer
- Detect emerging skill demand by skill-array frequency across thousands of rows

**Lead Generation**

- Surface hiring companies for B2B HR-tech, recruitment, or training sales
- Filter to companies posting 10+ open roles — high-intent buyers for talent solutions
- Build region-targeted prospect lists by French département or region

**Job Aggregation & Vertical Search**

- Power a niche French job board or Slack alert bot with fresh HelloWork listings
- Feed jobs into a CRM, ATS, or candidate-matching engine via webhook
- De-duplicate against your existing dataset using stable `jobId` values

### Getting Started

#### Basic Search

The simplest possible run — one keyword, one city:

```json
{
    "searchQueries": ["développeur"],
    "location": "Paris",
    "maxResults": 50
}
```

#### Filtered Search (Contract + Telework + Recency)

Fresh permanent and freelance full-remote roles posted in the last week:

```json
{
    "searchQueries": ["data engineer", "data scientist"],
    "location": "Lyon",
    "contractType": ["CDI", "FREELANCE"],
    "telework": ["FULL", "PARTIAL"],
    "datePosted": "1w",
    "minSalary": 55000,
    "maxResults": 200,
    "includeJobDetails": true
}
```

#### Paste a Pre-Built HelloWork URL

Already built a search with custom filters in the HelloWork UI? Paste the URL directly:

```json
{
    "startUrls": [
        "https://www.hellowork.com/fr-fr/emploi/recherche.html?k=chef+de+projet&l=Toulouse&c=CDI",
        "https://www.hellowork.com/fr-fr/emploi/recherche.html?k=comptable&l=Bordeaux"
    ],
    "maxResults": 500
}
```

### Input Reference

#### Search

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `searchQueries` | string\[] | `["développeur"]` | Job titles or keyword phrases such as `développeur`, `comptable`, or `chef de projet`. Each entry runs its own search. |
| `location` | string | `"Paris"` | City, département, or region in France (e.g. `Paris`, `Lyon`, `Marseille`, `Île-de-France`). Applied to every keyword. Leave blank to search the whole country. |
| `startUrls` | string\[] | `[]` | Optional. Paste full HelloWork search URLs to use them as-is. Useful when you've already built a search with custom filters in the HelloWork UI. |

#### Filters & Limits

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `maxResults` | integer | `100` | Maximum jobs per keyword or URL. Use `50` for a quick scan, `500` for deep research. Set to `0` for no cap (an internal upper limit of 5,000 still applies). The actor keeps the full final page even if it slightly overshoots. |
| `contractType` | select\[] | `[]` | Only return jobs matching these contract types: `CDI`, `CDD`, `ALTERNANCE`, `STAGE`, `INTERIM`, `FREELANCE`. Empty = all types. |
| `telework` | select\[] | `[]` | Filter by remote work mode: `FULL`, `PARTIAL`, `OCCASIONAL`, `NONE` (on-site only). Empty = all modes. |
| `datePosted` | select | `"any"` | Only include jobs posted within: `any`, `24h`, `3d`, `1w`, or `1m`. |
| `minSalary` | integer | null | Only return jobs offering at least this annual salary in euros. Listings without a posted salary are still included. |

#### Output

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `includeJobDetails` | boolean | `true` | Fetch the full description, skills, sector, and structured posting metadata from each job's detail page. Disable for faster light scans that return only the search-card data. |

### Output

Each row is a single job. Here's a representative result with full details enabled. Most fields populate when the employer fills them in on HelloWork; `salary*`, `skills`, `experience`, `telework`, and `sector` can be `null` / `[]` when not provided.

```json
{
    "title": "Développeur Python H/F",
    "company": "Team.is",
    "companyUrl": "https://www.hellowork.com/fr-fr/entreprises/teamis-101426.html",
    "companyLogo": "https://f.hellowork.com/img/entreprises/160_160/teamis.png",
    "location": "Paris 1er - 75",
    "city": "Paris 1er",
    "postalCode": "75001",
    "region": "Île-de-France",
    "country": "France",
    "salary": "50 000 - 55 000 € / an",
    "salaryMin": 50000,
    "salaryMax": 55000,
    "salaryCurrency": "EUR",
    "salaryPeriod": "YEAR",
    "contractType": "CDI",
    "employmentTypeRaw": "CDI",
    "employmentTypeEnum": "FULL_TIME",
    "telework": "FULL",
    "datePosted": "2026-05-13",
    "validThrough": "2026-06-12",
    "skills": ["Python", "Django", "PostgreSQL"],
    "qualifications": "Bac +5 ou équivalent en informatique ...",
    "education": "Bac +3, Bac +4",
    "experience": "3 ans",
    "sector": "Banque/Assurance/Finance",
    "descriptionHtml": "<p>Nous recherchons un développeur expérimenté...</p>",
    "descriptionText": "Nous recherchons un développeur expérimenté...",
    "snippet": "Nous recherchons un développeur expérimenté pour rejoindre notre équipe Python...",
    "jobId": "75605607",
    "jobUrl": "https://www.hellowork.com/fr-fr/emplois/75605607.html",
    "listPageUrl": "https://www.hellowork.com/fr-fr/emploi/recherche.html?k=developpeur&l=Paris",
    "searchQuery": "développeur",
    "scrapedAt": "2026-05-16T11:46:58Z"
}
```

#### Core Fields

| Field | Type | Description |
|-------|------|-------------|
| `title` | string | Job title (often ends with " H/F" — French equal-opportunity convention). |
| `company` | string | Hiring company name. |
| `companyUrl` | string | Company website when shown on HelloWork. |
| `companyLogo` | string | Logo image URL when available. |
| `jobId` | string | Stable HelloWork numeric ID — perfect for deduplication. |
| `jobUrl` | string | Canonical detail-page URL. |
| `listPageUrl` | string | Originating search URL. |
| `searchQuery` | string | Which keyword produced this row. |
| `scrapedAt` | string | ISO-8601 UTC timestamp of extraction. |

#### Location

| Field | Type | Description |
|-------|------|-------------|
| `location` | string | Full location string as displayed. |
| `city` | string | Parsed city. |
| `postalCode` | string | French postal code (e.g. `75001`). |
| `region` | string | French region (e.g. `Île-de-France`, `Auvergne-Rhône-Alpes`). |
| `country` | string | Always `France`. |

#### Salary & Contract

| Field | Type | Description |
|-------|------|-------------|
| `salary` | string | Raw salary string as displayed on HelloWork. |
| `salaryMin` | number | Lower bound parsed in EUR. |
| `salaryMax` | number | Upper bound parsed in EUR. |
| `salaryCurrency` | string | Currency code — always `EUR` in practice. |
| `salaryPeriod` | string | Pay period — `YEAR`, `MONTH`, `DAY`, or `HOUR`. |
| `contractType` | string | French contract label exactly as HelloWork shows it: `CDI`, `CDD`, `Alternance`, `Stage`, `Intérim`, `Freelance`, or `Indépendant`. |
| `employmentTypeRaw` | string | Same French contract label, kept as a stable raw column for integrations that map it themselves. Identical in light and full-detail runs. |
| `employmentTypeEnum` | string | Google standard code (`FULL_TIME`, `PART_TIME`, `INTERN`, `TEMPORARY`, `CONTRACTOR`) for integrations that expect it. Populated when `includeJobDetails` is on. |
| `telework` | string | Remote work mode: `FULL` when the listing is fully remote, `null` otherwise. Round-trips with the `telework` input filter. |
| `datePosted` | string | ISO-8601 publication date. |
| `validThrough` | string | ISO-8601 date the listing expires. |

#### Description & Detail

| Field | Type | Description |
|-------|------|-------------|
| `descriptionHtml` | string | Full job description in HTML markup. Populated when `includeJobDetails` is on. |
| `descriptionText` | string | Full job description as plain text. |
| `snippet` | string | 200-character preview for quick scanning. |
| `skills` | string\[] | Required skills as HelloWork classifies them, in French (e.g. `Normes d'hygiène`, `Travail en équipe`). Empty when the posting has no structured skill list. |
| `qualifications` | string | Qualifications text. |
| `education` | string | Education level (e.g. `Master`, `Bac +5`, `Bac +3, Bac +4`). |
| `experience` | string | Experience required as a French label (e.g. `1 an`, `3 ans`, `Sans expérience requise`). |
| `sector` | string | HelloWork's own industry label (e.g. `Banque/Assurance/Finance`, `Services aux Entreprises`, `Santé/Social/Association`). |

### Tips for Best Results

- **Mix `searchQueries` and `startUrls` in one run.** Pay for one start and get both a fresh keyword sweep and any pre-filtered URLs you've built in the HelloWork UI — sector pages, RSE-label searches, or autocomplete pages with no equivalent filter input. Results are deduplicated across all sources by `jobId`, so the same posting only appears once in the dataset even when it surfaces in multiple keywords.
- **Use the French label.** HelloWork is a French-only site — `développeur` returns thousands of hits, `developer` returns a handful. The same applies for cities: `Lyon` works, `Lyons` does not.
- **For full coverage of Paris, run per-arrondissement.** Paris alone has 20 arrondissements (`Paris 1er`, `Paris 2e`, ..., `Paris 20e`) — splitting the query is the cleanest way to push past the per-search soft cap.
- **Combine `contractType: ["ALTERNANCE", "STAGE"]` for student pipelines.** These two French-specific contracts cover work-study and internships — the entire early-career recruitment funnel in one filter.
- **Turn off `includeJobDetails` for fast scans.** When you only need title / company / salary for a market sweep, disabling detail fetches skips one request per job and roughly halves run time.
- **Use `datePosted: "24h"` for daily alerts.** Pair with a webhook to push every new posting matching your filters into Slack, email, or your ATS.
- **Deduplicate by `jobId`.** HelloWork sometimes repeats the same listing across overlapping searches — the numeric `jobId` is stable across runs, so use it as your dedupe key.

### Pricing

**From $0.95 per 1,000 results** — you pay only for the job rows delivered to your dataset, with no compute or time-based charges. From October 25, 2026, the price per result follows your Apify discount tier: Bronze, Silver, and Gold pay progressively less. Until then, every plan pays the Gold rate of $0.95 per 1,000. The table below shows the cost of results at each tier.

| Results | No discount | Bronze | Silver | Gold |
|---------|-------------|--------|--------|------|
| 100 | $0.113 | $0.107 | $0.101 | $0.095 |
| 1,000 | $1.13 | $1.07 | $1.01 | $0.95 |
| 10,000 | $11.30 | $10.70 | $10.10 | $9.50 |
| 100,000 | $113.00 | $107.00 | $101.00 | $95.00 |

A "result" is one job row in the output dataset.

### Integrations

Export data in JSON, CSV, Excel, XML, or RSS. Connect to 1,500+ apps via:

- **Zapier** / **Make** / **n8n** — Workflow automation
- **Google Sheets** — Direct spreadsheet export
- **Slack** / **Email** — Notifications on new results
- **Webhooks** — Trigger custom APIs on run completion
- **Apify API** — Full programmatic access

### Legal & Ethical Use

This actor is designed for legitimate recruitment research, salary benchmarking, market intelligence, and job aggregation use cases. Users are responsible for complying with applicable laws — including French and EU data protection rules (RGPD/GDPR) — and with HelloWork's terms of service. Make reasonable-rate requests, respect content usage rules for any descriptions or company data extracted, and do not use the data for spam, harassment, or any illegal purpose.

# Actor input Schema

## `searchQueries` (type: `array`):

Job titles or keyword phrases to search for, such as 'développeur', 'comptable', or 'chef de projet'. Each entry runs a separate search. Leave empty if you only want to use direct HelloWork URLs.

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

City, département, or region in France (e.g., Paris, Lyon, Marseille, Île-de-France). Applied to every keyword above. Leave blank to search the whole country.

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

Optional. Paste full HelloWork job search URLs (e.g., https://www.hellowork.com/fr-fr/emploi/recherche.html?k=developpeur\&l=Paris) to use them as-is. Useful when you've already built a search with custom filters in the HelloWork UI.

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

Maximum jobs to collect per search keyword or URL. Use 50 for a quick scan, 500 for deep research. Set to 0 for no cap (an internal upper limit of 5,000 still applies to prevent runaway pagination). The actor stops requesting new pages once this number is reached but keeps the full final page even if it slightly overshoots.

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

Only return jobs matching these French contract types. Leave empty for all types.

## `telework` (type: `array`):

Filter by remote work mode. Leave empty for all.

## `datePosted` (type: `string`):

Only include jobs posted within this window.

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

Only return jobs offering at least this annual salary in euros. Leave blank to apply no salary filter. Note: many HelloWork listings omit a salary — those rows are still included even when this filter is set.

## `includeJobDetails` (type: `boolean`):

Fetch the full description, skills, sector, and structured posting metadata from each job's detail page. Slightly slower but produces much richer data. Disable for faster light scans that return only the search-card data.

## Actor input object example

```json
{
  "searchQueries": [
    "développeur"
  ],
  "location": "Paris",
  "startUrls": [],
  "maxResults": 100,
  "contractType": [],
  "telework": [],
  "datePosted": "any",
  "includeJobDetails": true
}
```

# Actor output Schema

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

Table of scraped job listings with key fields.

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

Complete job records including descriptions and salary breakdowns.

# 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 = {
    "searchQueries": [
        "développeur"
    ],
    "location": "Paris",
    "startUrls": [],
    "maxResults": 100,
    "contractType": [],
    "telework": [],
    "datePosted": "any",
    "includeJobDetails": true
};

// Run the Actor and wait for it to finish
const run = await client.actor("solidcode/hellowork-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 = {
    "searchQueries": ["développeur"],
    "location": "Paris",
    "startUrls": [],
    "maxResults": 100,
    "contractType": [],
    "telework": [],
    "datePosted": "any",
    "includeJobDetails": True,
}

# Run the Actor and wait for it to finish
run = client.actor("solidcode/hellowork-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 '{
  "searchQueries": [
    "développeur"
  ],
  "location": "Paris",
  "startUrls": [],
  "maxResults": 100,
  "contractType": [],
  "telework": [],
  "datePosted": "any",
  "includeJobDetails": true
}' |
apify call solidcode/hellowork-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,solidcode/hellowork-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/mKrpmBatihcFby4AI/builds/ro48uUKciayOwoAFe/openapi.json
