HelloWork Jobs Scraper
Scrape HelloWork jobs in France by keyword, city or contract type. Company, department, salary, skills and posted date. $0.49 per 1,000 jobs.
How it works
- 1Open it on Apify
Hit Run on Apify — it opens the tool in the cloud, no install.
- 2Set the inputs
Adjust
searchQueries,location,radiusKm(sensible defaults are pre-filled). - 3Click Run
The tool runs on Apify’s cloud and collects the data for you.
- 4Export the results
Download as JSON, CSV or Excel, or pipe straight into your app, Google Sheets, or an AI agent.
Pricing
$0.00049 per job = $0.49 per 1,000
| You are charged for | When | Price |
|---|---|---|
| Job returned | Charged once per genuine HelloWork job posting: title, company, city and department, contract type, working time, remote band, the employer-published salary when there is one, exact posted date, skills and the job URL. Sample rows, diagnostics and empty runs are never charged. | $0.00049 |
Pay-per-event pricing: you are billed per result, not per subscription. Billing is handled by Apify on your own account. These are the live Apify store prices, in effect since 2026-08-08, and they are what you are actually charged.
Inputs
| Field | What it does | Type |
|---|---|---|
searchQueries | One or more searches, exactly as you would type them into HelloWork. French works best ("développeur", "comptable", "aide soignant"), English job titles also match. Each keyword is walked separately, so ["développeur", "data analyst"] runs two searches. | array |
location | City, department or region in France, for example "Paris", "Lyon", "Bordeaux" or "Île-de-France". Leave empty to search the whole country. | string |
radiusKm | Search radius around the location, in kilometres. Only used when a location is set. | integer |
contractTypes | Restrict to one or more French contract types. Leave empty for all of them. | array |
telework | Restrict to HelloWork's own télétravail bands. Note that only about 18% of postings carry any remote tag at all, so filtering here narrows results hard. | array |
workingTime | Full time, part time, or postings that do not say. | array |
experience | HelloWork's own experience bands. | array |
employerType | Who is hiring: the company itself, a recruitment agency, a consultancy (ESN), a temp agency or a training centre. | array |
postedWithin | Only postings published inside this window. Every row also carries the posted date, so you can filter afterwards too. | string |
minSalary | Only postings at or above this yearly salary, using HelloWork's own salary filter. Careful: HelloWork applies it to its own salary estimate as well as to employer-published figures, so most matches still have no published salary. Combine it with "Only postings that show a salary" if you want real numbers. | integer |
onlyWithSalary | Ask HelloWork for postings that display a salary. Measured on a 35,033-result search, this cut it to 27,696 and every card on the first page carried an employer-published figure. Leaving it off returns more rows but only about 63% of them have a salary. | boolean |
sortBy | HelloWork's own ordering: its relevance ranking, or newest first. | string |
startUrls | Paste HelloWork URLs instead of building a search. Works with a single posting (https://www.hellowork.com/fr-fr/emplois/82120782.html) and with a search page (https://www.hellowork.com/fr-fr/emploi/recherche.html?k=comptable&l=Lyon). | array |
includeDetails | Reads each posting's own page, which adds the exact posted date and expiry date, postal code and region, sector, required skills, education level, the company's HelloWork profile, the job reference, and HelloWork's salary estimate when the employer published none. Turn this off for a faster, listing-only run: you keep title, company, location, contract, salary and the approximate posted date. | boolean |
includeDescription | Adds the complete posting text as HTML and as plain text, plus the requirements section. Needs "Open each posting" to be on. It comes from the same request, so it costs no extra time and no extra charge - it only makes the rows larger. | boolean |
maxItems | Stop after this many postings. HelloWork serves at most 74 pages (2,220 postings) per individual search, so use several keywords, cities or filters to go beyond that. | integer |
What you get
A structured dataset — each result includes fields like:
titlecompanyNamecitydepartmentCodecontractTypeworkingTimeteleworkLabelsalaryTextsalaryIsEstimatepostedAtjobUrlExport every run as JSON, CSV or Excel, or send it to your app, a database, Google Sheets, or an AI agent.
Related tools in Developer & Research Tools
Other ready-to-run tools in the same category — all pay-per-use on the Apify cloud.
GitHub Scraper
Search GitHub repos and users: stars, forks, language, topics, licence, plus user bio, company and followers. No token needed. $0.90 per 1,000 rows.
Stack Overflow / Stack Exchange Scraper
Search Stack Overflow and Stack Exchange by keyword or tag. Score, answer count, views, reputation and body text. $2 per 1,000 questions.
Package Registry Scraper (npm + PyPI)
Get npm and PyPI package metadata as JSON. Version, license, author, repo, keywords and npm monthly downloads. $2 per 1,000 packages.
arXiv Scraper
Search arXiv papers by title, author, abstract or category. Get full abstracts, authors, categories, DOI, dates and PDF links. $2 per 1,000 papers.
OpenAlex Scholarly Works Scraper
Search 250M+ OpenAlex papers with no API key. Get titles, authors, venue, year, citations, DOI, OA links and full abstracts. $2.00 per 1,000 papers.
Crossref Scholarly Works Scraper
Search 150M+ papers on Crossref: DOI, title, authors, journal, publisher, date, citations and abstract. No API key. $1.00 per 1,000 works.
Where this tool sits
- Categories
- Developer & Research Tools
HelloWork Jobs Scraper: French job postings with contract type and real salaries
HelloWork is one of France's big generalist job boards. Search by keyword and city, filter by contract type, remote band, experience or salary, and get rows back: title, company, city and department, CDI or CDD or intérim or stage or alternance, the salary, the posted date and the link.
The salary field needs a warning before anything else. When an employer publishes no figure, HelloWork fills the gap with its own estimate. Both arrive in the same place, so every row carries salaryIsEstimate and you should read it before you treat a number as an offer.
| Input | Job keywords and a French location, or HelloWork URLs |
| Output | One row per posting |
| Ceiling | 20,000 postings per run |
| Account needed | None |
| Price | $0.49 per 1,000 postings, flat on every plan |
🔍 What HelloWork Jobs Scraper does
Each keyword you give it is walked as its own search, so ["développeur", "data analyst"] runs two. French terms work best, and English job titles match plenty of postings too. Add a city, department or region and a radius in kilometres to keep it local.
Every filter HelloWork's own sidebar has is here: contract type, télétravail band, working time, experience, who is hiring, posted-within window and a minimum salary. Broad searches repeat rows across pages, and duplicates are removed before anything counts toward your maximum, so maxItems means distinct postings.
By default each posting's own page is opened, which is where the exact posted date, the expiry date, the postal code and region, the sector, the skills list and the education level come from. Turn that off for a faster listing-only run and you keep title, company, location, contract, salary and an approximate posted date.
📥 What you give it
{
"searchQueries": ["comptable"],
"location": "Lyon",
"radiusKm": 20,
"contractTypes": ["cdi"],
"onlyWithSalary": true,
"maxItems": 200
}
| Field | Default | What it is |
|---|---|---|
searchQueries | none | One or more searches. The box starts with développeur in it, which is a suggestion and not a default. |
location | none | A city, department or region: Paris, Lyon, Île-de-France. Empty searches the whole country. |
radiusKm | none | Kilometres around the location, 1 to 200. Only used when a location is set. |
contractTypes | all | cdi, cdd, interim, stage, alternance, freelance, independant, fonctionnaire, franchise, associe, stage-lycee. |
telework | all | full, partial, occasional, none. Only about 18% of postings carry any remote tag, so this one narrows hard. |
workingTime | all | full-time, part-time, unspecified. |
experience | all | HelloWork's bands: 0-1, 2-3, 4-5, 6-10, 10+, unspecified. |
employerType | all | company, agency, consultancy, temp-agency, training-centre. |
postedWithin | any time | day, 3days, week, month. |
minSalary | none | Yearly euros. HelloWork applies this to its own estimate as well as to published figures, so pair it with the next field if you want real offers. |
onlyWithSalary | false | Ask HelloWork for postings that display a salary. Leaving it off returns more rows, and only about 63% of them carry one. |
sortBy | relevance | relevance or date. |
startUrls | none | Paste HelloWork URLs instead of building a search. A single posting works, and so does a search page. |
includeDetails | true | Open each posting for the full record. |
includeDescription | false | Add the whole posting text as HTML and plain text plus the requirements section. Needs includeDetails on. It rides on the same request, so it adds no time and no charge. |
maxItems | 50 | Stop after this many distinct postings, up to 20,000. |
proxyConfiguration | {"useApifyProxy": true} | Leave it alone. Your own servers are used exactly as given. |
📤 What you get back
A real row. Note salaryIsEstimate, because this one is HelloWork's estimate rather than the employer's figure:
{
"jobId": "80369776",
"title": "Développeur H/F",
"companyName": "Neqo",
"jobUrl": "https://www.hellowork.com/fr-fr/emplois/80369776.html",
"companyProfileUrl": "https://www.hellowork.com/fr-fr/entreprises/neqo-133722.html",
"locationLabel": "Vannes - 56",
"city": "Vannes",
"departmentCode": "56",
"region": "Bretagne",
"postalCode": "56000",
"countryCode": "FR",
"contractType": "CDI",
"contractDuration": null,
"employmentType": "FULL_TIME",
"workingTime": "Full time",
"teleworkLabel": "Hybrid",
"isRemote": false,
"isHybrid": true,
"salaryMin": 28000,
"salaryMax": 44000,
"salaryCurrency": "EUR",
"salaryPeriod": "YEAR",
"salaryText": "28000 - 44000 EUR / year",
"salaryIsEstimate": true,
"salaryEstimateMin": 28000,
"salaryEstimateMedian": 37500,
"salaryEstimateMax": 44000,
"postedAt": "2026-09-18T00:13:47Z",
"postedRelative": "il y a 2 jours",
"postedAtEstimated": "2026-09-18T07:03:19.923Z",
"expiresAt": "2026-10-18T00:13:47Z",
"occupationalCategory": "Informatique",
"sectors": ["Secteur informatique", "ESN"],
"skills": ["Service client", "SQL", "CakePHP", "Git", "HTML5", "Node.js", "PHP", "MySQL", "..."],
"educationLevels": ["bachelor degree", "associate degree"],
"experienceRequired": null,
"reference": "3937186/29254901 D/56V",
"directApply": true,
"searchQuery": "développeur",
"page": 1,
"position": 2,
"scrapedAt": "2026-09-20T07:03:35.992Z"
}
The skills list is cut short; the real row carries sixteen.
| Field | What it is |
|---|---|
salaryIsEstimate | false means the employer published the figure. true means it is HelloWork's own estimate for that title and region, and the same numbers are repeated in the salaryEstimate* fields so one filter drops them. null means no salary at all. If you only want real offers, keep salaryIsEstimate === false. |
departmentCode | The French department number, 75 for Paris, 69 for Lyon, 56 for Morbihan. |
contractType | CDI, CDD, Intérim, Stage, Alternance, Freelance and the rest. contractDuration carries 6 mois and the like on fixed terms. |
teleworkLabel | HelloWork's own télétravail band, and mostly empty because most employers do not set one. isRemote and isHybrid are the booleans derived from it. |
postedAt | The exact timestamp, and it only exists when postings are opened. On a listing-only run you get postedRelative and postedAtEstimated instead. |
expiresAt | When HelloWork takes the posting down. |
skills, sectors, educationLevels | Arrays, and they come from the posting page. |
descriptionHtml, descriptionText, requirementsText | Only filled in when you switch the full description on. |
detailNote | Present when the posting page could not be read. That row keeps its listing fields and counts as a posting. |
🧾 Reading the output
| Row | How to spot it | Charged |
|---|---|---|
| A posting | It has a jobId | yes |
| The sample row | _sample: true and charged: false | no |
| A diagnostic | An errorCode and no jobId | no |
errorCode | What it means |
|---|---|
NO_RESULTS | The search ran and matched nothing. Loosen a filter or try a broader keyword. |
BAD_URL | A pasted URL is not a HelloWork posting or search page. |
JOB_NOT_FOUND | That posting is gone. Expired postings do this. |
JOB_UNPARSED | The page could not be read. Re-run it. |
HTTP_ERROR | HelloWork answered badly or was unreachable. |
▶️ How to run it
1. Open HelloWork Jobs Scraper and click Try for free. 2. Put your terms into Job keywords, one per line. 3. Add a Location and a radius if you want it local. 4. Set Maximum postings, then click Start. 5. Download the dataset as JSON, CSV or Excel, or read it from the Apify API.
💰 How much does it cost?
$0.49 per 1,000 postings. Flat on every Apify plan, no volume tiers.
You pay per posting row. Duplicates across pages are dropped before they count, the sample row is not charged, and diagnostic rows are not charged. Adding the full description does not add a second charge, it only makes the rows bigger. A posting whose own page could not be read still arrives as a row, carries detailNote, and counts as a posting.
💡 What people use it for
- Salary research that survives scrutiny, by keeping only
salaryIsEstimate === falseso the numbers
are the ones employers actually printed.
- Watching one city and one contract type daily, with
postedWithinset today, and deduping on
jobId.
- Mapping demand by department, since every row carries
departmentCodeand most carry a postal
code.
- Pulling the
skillsarrays for a role across a few hundred postings to see which tools keep
appearing.
- Telling employers apart from agencies with
employerType, which changes what a listing count
actually means.
🚧 What it does not do
- France only. That is HelloWork's own coverage.
- The salary can be an estimate. HelloWork produces one when the employer published nothing, and
it lands in the same fields. salaryIsEstimate is the only thing that tells you apart, so read it.
minSalaryfilters estimates too. HelloWork applies its own filter to its own estimate, so a
high minimum still returns postings with no published figure. Combine it with Only postings that show a salary.
- The remote band is usually empty. Only about 18% of postings carry one, so filtering on it cuts
results hard and drops postings that are remote but untagged.
- No applications, no candidate data, no recruiter contact details. Postings only.
- A single search runs out before the cap. Going wide means several keywords, cities or filters,
not a bigger maxItems.
- Postings expire.
expiresAttells you when, and a pasted link to a dead posting returns
JOB_NOT_FOUND.
- Listing-only runs have no exact posted date. Turn postings on if you need
postedAtrather
than postedAtEstimated.
🧭 Which job board scraper do you need?
| If you want | Use |
|---|---|
| French postings from HelloWork | This one |
| Germany, Austria and Switzerland | XING Jobs Scraper |
| Germany, Austria, UK, Netherlands and Belgium | StepStone Jobs Scraper |
| Startup roles from Wellfound | Wellfound Jobs Scraper |
| Hackathons, competitions and campus hiring in India | Unstop Scraper |
❓ Questions people ask
Do I need a HelloWork account? No. Everything here is on public posting pages.
How do I get only real salaries? Switch on Only postings that show a salary, then keep rows where salaryIsEstimate is false. The first narrows what HelloWork sends, the second catches the estimates that still come through.
Should I search in French or English? French matches more. English job titles work for the technical roles that are advertised that way.
Can I paste a single posting? Yes, into HelloWork URLs. A search page URL works there too.
Why is postedAt empty? You ran with postings closed. The exact date lives on the posting page.
Can I schedule it? Yes. jobId is stable, so a daily run with postedWithin set to day gives you just what is new.
Is this legal? These are public job adverts, and they can carry personal data, which GDPR covers, so have a reason for collecting it. Apify's write-up on scraping and the law is a good starting point, and we are not lawyers.
🆘 If something breaks
Open the Issues tab on the actor page. Send the keyword, the location and the run ID. If you got a diagnostic row, its errorCode usually names the problem on its own.