Welcome to the Jungle Scraper (WTTJ) | $2/1K Jobs + Apply URLs
Pricing
from $2.00 / 1,000 jobs
Welcome to the Jungle Scraper (WTTJ) | $2/1K Jobs + Apply URLs
Scrape Welcome to the Jungle job listings through the official search API. Full job details, direct ATS apply URLs, company data, 15+ filters, exports beyond the 1000-result limit.
Pricing
from $2.00 / 1,000 jobs
Rating
5.0
(1)
Developer
Alex Morain
Maintained by CommunityActor stats
0
Bookmarked
3
Total users
1
Monthly active users
15 days ago
Last modified
Categories
Share
Welcome to the Jungle Scraper (WTTJ): jobs, apply URLs, company data
Scrape Welcome to the Jungle job listings through the official search API the website itself uses. No headless browser, no HTML parsing, no CSS selectors that break on the next redesign. You get the full listing, the company behind it, and, when the employer uses their own ATS, the direct apply URL.
The corpus is 88,902 live listings as of 5 September 2026, of which 70,853 in France, 16,677 in the United States, 4,750 in the United Kingdom, 1,877 in Canada and the rest spread across Spain, Germany, Belgium, Switzerland, Italy and the Czech Republic. Welcome to the Jungle is the reference job board for French tech and startup hiring, so this is the shortest path to structured recruitment data France-side.
Price: $2 per 1,000 jobs ($0.002 per job), plus $0.00001 per dataset item and $0.00005 per GB of memory to start a run. A 1,000-job export costs about $2.01. Nothing else is billed, and full job details cost the same as titles only.
How to scrape Welcome to the Jungle
From the Apify console
- Open the Input tab. Every field is optional and one search is already prefilled, so pressing Start right away gives you 100 jobs and shows the shape of the output.
- Replace Search query with your own keyword, or clear it to browse the entire corpus and filter instead. Clearing it is the right move for lead generation, where you want every company hiring in a sector rather than one job title.
- Narrow with the filters: country and city, contract type, remote policy, sector, company size, salary floor. They are sent to the Welcome to the Jungle search API before anything is collected, so a narrow search is both faster and cheaper.
- Set Max jobs to
0when you want every matching job rather than a sample. The search API caps any single query at 1,000 results; this Actor detects the cap and splits the export by publication date to walk past it. - Press Start. When the run ends, download the dataset as JSON, CSV or Excel, or read it from the API.
From the API
Every run is a POST to the Apify API with the same input object. The three code examples further down are copy-pastable in Python, JavaScript and cURL. Two things worth knowing before you write the call:
run-sync-get-dataset-itemsreturns the results in the HTTP response, which is convenient up to a few thousand jobs. Past that, start the run asynchronously and read the dataset afterwards, because a synchronous call is capped by the request timeout.- Add
maxTotalChargeUsdto the run parameters to cap what a run may cost. The Actor watches that ceiling and stops cleanly when it is reached, with a log line saying why, instead of failing or silently truncating.
Which fields should I fill?
Nothing is mandatory. An empty input is a valid run: it returns the 100 most relevant jobs of the whole corpus.
| If you want to... | Fill in | Leave empty |
|---|---|---|
| Find one type of role | query, countryCodes | everything else |
| Build a lead list of companies that hire | sectors, companySizeMin / companySizeMax, countryCodes | query |
| Feed a job board daily | publishedWithinDays: 1, sortBy: "newest" | query |
| Watch specific companies | companySlugs | all other filters |
| Do salary research | salaryYearlyMin, professionCategories | query |
| Cover a whole labour market | aroundLatLng, aroundRadiusKm | cities |
| Take a fast inventory of what exists | includeDetails: false, maxItems: 0 | everything else |
Input fields
| Field | Type | What it does | Example |
|---|---|---|---|
query | string | Free-text search across the whole listing: job title, skill, keyword, company name. Empty means browse the entire corpus. | "data engineer" |
searchOnlyInJobTitle | boolean | Restricts the search to job titles. Use it when a common word floods your results: python in full text matches every listing that mentions it in passing. | true |
resultsLanguage | string | Language of the localized labels in the output: profession names, skills, sectors. fr, en, es, cs, sk. It does not filter and does not translate the job text. | "en" |
sortBy | string | relevance uses the Welcome to the Jungle ranking, newest sorts by publication date. Use newest for recurring runs. | "newest" |
publishedWithinDays | string | Publication window: any, 1, 3, 7, 14, 30. | "1" |
countryCodes | array | ISO 3166-1 alpha-2 country codes of the office. | ["FR", "BE"] |
cities | array | City of the office, matched exactly on the name as Welcome to the Jungle writes it. A misspelling returns nothing. | ["Paris", "Lyon"] |
aroundLatLng | string | Search around a GPS point, format lat, lng. Catches surrounding towns a city filter would miss. | "45.7640, 4.8357" |
aroundRadiusKm | integer | Radius around that point. Defaults to 20 km when a point is set, ignored otherwise. | 50 |
contractTypes | array | full_time (French CDI), part_time, temporary, fixed_term, internship, apprenticeship, freelance, vie, graduate_program, volunteer, other. | ["apprenticeship"] |
remote | array | fulltime, partial, punctual, no, unknown. unknown is frequent and real: many employers leave the field empty. | ["fulltime"] |
jobLanguages | array | Language the listing is written in, a decent proxy for the working language of the team. 15 values from fr to ja. | ["en"] |
maxExperienceRequired | integer | Keeps jobs asking for at most this many years. 0 keeps the roles open to beginners. Listings with no declared level are kept. | 2 |
educationLevels | array | French scale: no_diploma, cap, bep, bac, bac_1 to bac_5, phd. | ["bac_5"] |
salaryYearlyMin | integer | Keeps jobs whose published yearly minimum is at least this amount. Listings with no salary are excluded, and most have none. | 50000 |
sectors | array | Industry of the company: tech-1, banking-insurance-finance, consulting-audit, health-social-environment and 16 more. | ["tech-1"] |
professionCategories | array | Profession family of the job itself, which is not the company sector: a developer at a bank is Tech & Engineering in Banking. | ["tech-engineering-3NjUy"] |
companySizeMin | integer | Lower bound on headcount. | 20 |
companySizeMax | integer | Upper bound on headcount. With the minimum, this is how you target a segment. | 200 |
companySlugs | array | Export every job of named companies, whatever the other filters. Slug from the company URL, or the exact company name. | ["doctolib", "swile"] |
maxItems | integer | Cap on jobs returned. 0 exports everything that matches. Default 100. | 0 |
includeDetails | boolean | Fetches the full listing of every job: description, recruitment process, skills, tools, apply URL, company website and LinkedIn. On by default. Turning it off does not lower the price. | true |
proxyConfiguration | object | Leave it off. The search API needs no proxy and detail requests are retried automatically. Set a residential proxy only if a large run logs repeated detail failures. | {} |
What each Welcome to the Jungle job listing contains
Every item in the dataset is one job. The fields below always come back, whatever the value of includeDetails, because they come from the search index in the same pass as the search itself.
Job identity
| Field | Type | Description | Example |
|---|---|---|---|
id | string | Welcome to the Jungle job UUID, stable across runs. | "73fb7661-cded-4c5b-b3a2-9a8e99ffa513" |
title | string | Job title as published. | "Data Engineer (H/F)" |
url | string | Listing page on welcometothejungle.com, in the language set by resultsLanguage. | "https://www.welcometothejungle.com/fr/companies/link-consulting/jobs/data-engineer-h-f_toulouse_LC_x9OYDZY" |
slug | string | Job slug, the last path segment of the URL. | "data-engineer-h-f_toulouse_LC_x9OYDZY" |
reference | string | Internal Welcome to the Jungle reference, equal to id in practice. | "73fb7661-cded-4c5b-b3a2-9a8e99ffa513" |
wttjReference | string | Short public reference shown on the listing. | "LC_x9OYDZY" |
publishedAt | string | ISO 8601 publication date. Renewing a listing refreshes it, so treat it as "last put forward", not "first published". | "2026-09-05T00:02:36Z" |
language | string | Language the listing is written in. | "fr" |
scrapedAt | string | ISO 8601 timestamp of collection. | "2026-09-05T13:31:25.639Z" |
Contract, seniority, salary
| Field | Type | Description | Example |
|---|---|---|---|
contractType | string | One of the eleven contract values listed in the input table. | "full_time" |
contractDurationMinMonths | integer | Minimum contract duration in months, for fixed-term and apprenticeships. | 24 |
contractDurationMaxMonths | integer | Maximum contract duration in months. | null |
remote | string | fulltime, partial, punctual, no or unknown. | "partial" |
experienceMinYears | integer | Minimum years of experience required, null when not declared. | 4 |
educationLevel | string | Required diploma on the French scale. | "bac_5" |
salaryMin | integer | Lower bound of the published salary, in the unit given by salaryPeriod. | 500 |
salaryMax | integer | Upper bound. | 550 |
salaryCurrency | string | Currency of the published figures. | "EUR" |
salaryPeriod | string | yearly, monthly or daily. Daily rates are common on freelance and consulting listings. | "daily" |
salaryYearlyMin | integer | Yearly minimum normalized by Welcome to the Jungle, which is what makes daily and monthly rates comparable. This is the field the salaryYearlyMin filter compares against. | 109000 |
salaryText | string | Ready-to-display string built from the four fields above. | "500-550 EUR/daily" |
Classification
| Field | Type | Description | Example |
|---|---|---|---|
professionCategory | string | Profession family, localized. | "Technologie et ingénierie" |
professionSubCategory | string | Sub-family. | "Données/Business Intelligence" |
profession | string | Normalized job title, useful to group listings that spell the same role differently. | "Ingénieur de données" |
sectors | array | Sectors of the company, localized. | ["Recrutement", "Digital"] |
Location
| Field | Type | Description | Example |
|---|---|---|---|
offices | array | One object per office: city, state, district, country, countryCode. A job can be attached to several. | [{"city": "Toulouse", "state": "Occitanie", "district": "Haute-Garonne", "country": "France", "countryCode": "FR"}] |
cities | string | Comma-joined, deduplicated city names, for spreadsheets that dislike nested arrays. | "Lille, Suresnes" |
geoloc | array | GPS coordinates of each office, lat and lng. | [{"lat": 43.61199, "lng": 1.44334}] |
Content of the listing
| Field | Type | Description | Example |
|---|---|---|---|
summary | string | Short plain-text summary written by Welcome to the Jungle. | "Rejoignez Link Consulting, une entreprise qui valorise..." |
keyMissions | array | Three or four sentences describing the actual work. | ["Concevoir et fiabiliser les flux de transformation / ETL..."] |
profile | string | Candidate profile the employer is asking for, HTML. | "<p>De formation supérieure Bac+5...</p>" |
benefits | array | Perks declared by the company: remote days, meal vouchers, profit sharing, health cover. | ["Horaires de travail flexibles", "Prime de cooptation"] |
Welcome to the Jungle company data
| Field | Type | Description | Example |
|---|---|---|---|
companyName | string | Company name. | "Link Consulting" |
companySlug | string | Company slug, the identifier to pass back in companySlugs. | "link-consulting" |
companySummary | string | One-line company pitch. | "Cabinet de conseil en recrutement et assistance technique IT." |
companyLogo | string | CDN URL of the logo. | "https://cdn-images.welcometothejungle.com/..." |
companyLabels | array | Certifications and awards claimed on the profile. | ["happy-index-at-work", "we-impact-index"] |
companyNbEmployees | integer | Headcount. | 80 |
Fields that need includeDetails: true
These come from a second request per job, against the public Welcome to the Jungle jobs API. They are the reason the Actor exists, which is why the option is on by default.
| Field | Type | Description | Example |
|---|---|---|---|
applyUrl | string | Direct ATS apply URL on the employer's own system, bypassing the Welcome to the Jungle listing page. null when the employer uses the built-in Welcome to the Jungle ATS. | "https://jobaffinity.fr/apply/n9owui5rph70kedm0p?src=WelcometotheJungle" |
ats | string | Which application route the employer uses: external (their own ATS), kombo (an ATS connected through an integration), wkit (Welcome to the Jungle's own kit, no external URL). | "external" |
description | string | Full job description, HTML. | "<p>Notre client, acteur industriel...</p>" |
recruitmentProcess | string | Interview steps as published by the employer, HTML. Filled in on a minority of listings. | "<p>1. LE MATCHING...</p>" |
companyDescription | string | Long company description, HTML, when the employer wrote one. | null |
startDate | string | Declared start date. | "2026-10-01" |
updatedAt | string | ISO 8601 timestamp of the last edit to the listing. | "2026-09-05T00:02:36.690111Z" |
skills | array | Skills tagged by Welcome to the Jungle, localized. | ["Intégration continue", "Gestion de la documentation"] |
tools | array | Tools and technologies named in the listing. | ["Azure Data Factory", "Power BI", "SQL", "Talend"] |
officeAddress | string | Street address of the office of this job, deduplicated against city and country so it never reads "Toulouse, France, 31000 Toulouse, FR". | "15 Rue de Verdun, 31000 Toulouse, France" |
applicationFields | array | What the employer asks a candidate to submit: name and mode (mandatory or optional). Disabled fields are dropped. | [{"name": "resume", "mode": "mandatory"}, {"name": "cover_letter", "mode": "optional"}] |
companyWebsite | string | Company website. | "https://link-consulting.fr/" |
companyLinkedin | string | Company LinkedIn page, normalized to a full URL. | "https://www.linkedin.com/company/sas-link-consulting/" |
companyIndustry | string | Industry labels in English, whatever resultsLanguage says. | "Recruitment, Digital" |
companyCreationYear | integer | Year the company was founded. | 2016 |
companyJobsCount | integer | Number of jobs the company currently has open on Welcome to the Jungle. A blunt but effective hiring-intensity signal. | 83 |
companyAverageAge | integer | Average age of employees, when the company published it. | 34 |
companyRevenue | string | Revenue as written on the company profile, free text. | "35M € depuis 2016" |
companyHeadquarter | string | Headquarters address. | "15, Rue de Verdun, 31000 Toulouse, FR" |
Output example
One real item, collected on 5 September 2026 with includeDetails: true. The long HTML strings are cut short here; in the dataset they are complete.
{"id": "73fb7661-cded-4c5b-b3a2-9a8e99ffa513","title": "Data Engineer (H/F)","url": "https://www.welcometothejungle.com/fr/companies/link-consulting/jobs/data-engineer-h-f_toulouse_LC_x9OYDZY","applyUrl": "https://jobaffinity.fr/apply/n9owui5rph70kedm0p?src=WelcometotheJungle","publishedAt": "2026-09-05T00:02:36Z","language": "fr","contractType": "full_time","contractDurationMinMonths": null,"contractDurationMaxMonths": null,"remote": "partial","educationLevel": "bac_5","experienceMinYears": null,"salaryMin": 500,"salaryMax": 550,"salaryCurrency": "EUR","salaryPeriod": "daily","salaryYearlyMin": 109000,"salaryText": "500-550 EUR/daily","professionCategory": "Technologie et ingénierie","professionSubCategory": "Données/Business Intelligence","profession": "Ingénieur de données","sectors": ["Recrutement", "Digital"],"offices": [{"city": "Toulouse","state": "Occitanie","district": "Haute-Garonne","country": "France","countryCode": "FR"}],"cities": "Toulouse","geoloc": [{ "lat": 43.61199, "lng": 1.44334 }],"summary": "Rejoignez Link Consulting, une entreprise qui valorise l'humilité et le savoir-être. En tant que Data Engineer, vous serez intégré à l'équipe Data BI...","keyMissions": ["Produire des livrables concrets et directement exploitables en intégrant et en consolidant les données issues des différents ERP.","Concevoir et fiabiliser les flux de transformation / ETL, et structurer le datawarehouse et les datamarts.","Produire la documentation technique et fonctionnelle associée, et faire évoluer les modèles de données."],"profile": "<p>De formation supérieure Bac+5, vous justifiez d'une expérience réussie de minimum 3 ans sur des fonctions de Data Engineer...</p>","benefits": ["Horaires de travail flexibles","Ouvert au télétravail total","Entre 1-2 jours de télétravail","Congés payés supplémentaires","Prime de cooptation","Subventions CSE (Sport, Culture,...)","Chèques cadeaux","Team building","Mentorat","Coaching"],"companyName": "Link Consulting","companySlug": "link-consulting","companySummary": "Cabinet de conseil en recrutement et assistance technique IT.","companyLogo": "https://cdn-images.welcometothejungle.com/uvTN4ji9y1K14S3VsDwbjPEqlwFZCYYD5_qBavrue-s/rs:auto:400::/q:85/...","companyLabels": ["happy-index-at-work", "we-impact-index"],"companyNbEmployees": 80,"reference": "73fb7661-cded-4c5b-b3a2-9a8e99ffa513","wttjReference": "LC_x9OYDZY","slug": "data-engineer-h-f_toulouse_LC_x9OYDZY","scrapedAt": "2026-09-05T13:31:25.639Z","ats": "external","description": "<p>Notre client, acteur industriel dans le secteur de l'aéronautique, recherche son futur Data Engineer (H/F) à Toulouse...</p>","recruitmentProcess": null,"companyDescription": null,"startDate": null,"updatedAt": "2026-09-05T00:02:36.690111Z","skills": ["Aptitudes à motiver les autres","Gestion de la documentation","Intégration continue","Aptitude à résoudre les problèmes"],"tools": ["Azure Data Factory", "Power BI", "SQL", "Talend"],"officeAddress": "15 Rue de Verdun, 31000 Toulouse, France","applicationFields": [{ "name": "resume", "mode": "mandatory" },{ "name": "cover_letter", "mode": "optional" },{ "name": "picture", "mode": "optional" },{ "name": "media_website", "mode": "optional" },{ "name": "media_linkedin", "mode": "optional" },{ "name": "media_twitter", "mode": "optional" }],"companyWebsite": "https://link-consulting.fr/","companyLinkedin": "https://www.linkedin.com/company/sas-link-consulting/","companyIndustry": "Recruitment, Digital","companyCreationYear": 2016,"companyJobsCount": 83,"companyAverageAge": null,"companyRevenue": "35M € depuis 2016","companyHeadquarter": "15, Rue de Verdun, 31000 Toulouse, FR"}
Input examples
Every fully remote permanent tech job in France paying at least 50k, all of them:
{"countryCodes": ["FR"],"contractTypes": ["full_time"],"remote": ["fulltime"],"sectors": ["tech-1"],"salaryYearlyMin": 50000,"maxItems": 0}
Keyword search around a GPS point, 50 km around Lyon:
{"query": "data engineer","aroundLatLng": "45.7640, 4.8357","aroundRadiusKm": 50,"maxItems": 200}
Everything three named companies are currently hiring for:
{"companySlugs": ["doctolib", "swile", "pennylane"],"maxItems": 0}
Yesterday's new listings only, for a daily scheduled run:
{"publishedWithinDays": "1","sortBy": "newest","countryCodes": ["FR"],"maxItems": 0}
Call the Welcome to the Jungle jobs API from code
Python
from apify_client import ApifyClientclient = ApifyClient("<YOUR_APIFY_TOKEN>")run_input = {"query": "data engineer","countryCodes": ["FR"],"contractTypes": ["full_time"],"remote": ["fulltime", "partial"],"salaryYearlyMin": 45000,"maxItems": 500,"includeDetails": True,}run = client.actor("alexmorain/wttj-jobs-scraper").call(run_input=run_input)for job in client.dataset(run["defaultDatasetId"]).iterate_items():print(job["title"], "|", job["companyName"], "|", job["cities"], "|", job["applyUrl"])
Straight into a DataFrame, for salary or market analysis:
import pandas as pdfrom apify_client import ApifyClientclient = ApifyClient("<YOUR_APIFY_TOKEN>")run = client.actor("alexmorain/wttj-jobs-scraper").call(run_input={"professionCategories": ["tech-engineering-3NjUy"],"countryCodes": ["FR"],"salaryYearlyMin": 1,"includeDetails": False,"maxItems": 0,})df = pd.DataFrame(client.dataset(run["defaultDatasetId"]).list_items().items)print(df.groupby("professionSubCategory")["salaryYearlyMin"].describe())
JavaScript
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });const run = await client.actor('alexmorain/wttj-jobs-scraper').call({query: 'product designer',countryCodes: ['FR'],publishedWithinDays: '7',sortBy: 'newest',maxItems: 300,includeDetails: true,});const { items } = await client.dataset(run.defaultDatasetId).listItems();for (const job of items) {console.log(`${job.title} — ${job.companyName} — ${job.applyUrl ?? job.url}`);}
cURL
Synchronous run, JSON back in the response:
curl -X POST "https://api.apify.com/v2/acts/alexmorain~wttj-jobs-scraper/run-sync-get-dataset-items?token=<YOUR_APIFY_TOKEN>" \-H "Content-Type: application/json" \-d '{"query": "data engineer","countryCodes": ["FR"],"maxItems": 50,"includeDetails": true}'
Export WTTJ jobs to CSV
Same call, format=csv on the query string, and the response is a spreadsheet-ready file:
curl -X POST "https://api.apify.com/v2/acts/alexmorain~wttj-jobs-scraper/run-sync-get-dataset-items?token=<YOUR_APIFY_TOKEN>&format=csv" \-H "Content-Type: application/json" \-d '{"countryCodes": ["FR"], "sectors": ["tech-1"], "maxItems": 1000}' \-o wttj-jobs.csv
format also accepts json, jsonl, xlsx, xml and rss. For a large export, start the run asynchronously with POST /v2/acts/alexmorain~wttj-jobs-scraper/runs, then read GET /v2/datasets/<datasetId>/items?format=csv when it finishes. Add &maxTotalChargeUsd=5 to either call to hard-cap what the run may spend.
What people use this scraper for
Candidate sourcing and recruitment data France
Recruiters and agencies use it the other way round from a job seeker: the listing tells you which companies are short of which profiles, right now, with a salary band and a required seniority. Filter on professionCategories and maxExperienceRequired to isolate a segment, and you have the live demand curve for a role in a given city. The tools array is what makes it operational, because it names the actual stack behind a vague title: two listings both called "Data Engineer" split cleanly once one says Databricks and the other says Talend.
Feeding a job board or aggregator
This is the classic French job board scraper use case, and the reason for the publishedWithinDays and sortBy inputs. Schedule a daily run with publishedWithinDays: "1" and sortBy: "newest", which returns roughly 2,700 listings a day across the corpus, and merge them into your own index. Each item carries what a listing page needs: title, company, logo, city, contract, salary string, HTML description, benefits, and a link that sends the candidate either to the employer's ATS or back to Welcome to the Jungle. One caveat worth planning for is that Welcome to the Jungle refreshes publishedAt when an employer renews a listing, so deduplicate on id, not on the date.
B2B prospecting: finding companies that are hiring
A company that opens five roles is a company with a budget and a problem. Leave query empty, set sectors and a headcount range, and the output becomes a lead list rather than a job list: name, website, LinkedIn page, headcount, founding year, headquarters address, self-declared revenue, and companyJobsCount as a rough intensity score. Sales teams selling to HR, to IT, or to any function currently being staffed up can rank prospects by how many roles are open and in which discipline. Deduplicate on companySlug and you have a clean account list.
Tracking the French tech job market
The corpus is large enough to be a real market indicator: 32,922 listings are attached to the Tech sector, and 29,488 across the whole corpus publish a salary. Running the same filtered query on a schedule and storing the counts gives you a time series of demand by profession, by city and by contract type. The contractType breakdown is specific to France and genuinely informative, since apprenticeship listings alone account for 2,765 of the French corpus and behave on a completely different seasonal cycle from permanent roles.
Salary analysis
salaryMin, salaryMax, salaryCurrency and salaryPeriod come straight from the listing, and salaryYearlyMin is the yearly-normalized minimum Welcome to the Jungle computes so that a 550 EUR daily rate and a 42k yearly package can sit in the same column. Combine it with experienceMinYears, educationLevel, remote and cities and you can build a compensation benchmark for a role in a city, which is the sort of dataset consultancies charge for. The honest caveat is in the numbers above: roughly a third of listings publish salary at all, so treat the sample as indicative of the market that discloses, not of the whole market.
Competitive hiring intelligence
Pass a list of competitors, prospects or portfolio companies into companySlugs and the Actor exports every job they currently have open, ignoring the other filters. Watching that list over time tells you which teams they are building, which technologies they are moving to, which cities they are opening, and how fast. The benefits array is an underrated part of this, because it is where remote policy, meal vouchers and equity plans are declared, which is exactly what a competitor's offer looks like from the candidate's side.
Why this Welcome to the Jungle scraper
It uses the Welcome to the Jungle jobs API, not a headless browser
Search runs against the same public search index the website queries, and job details against the same public jobs API endpoint the listing page calls. No Chromium, no page rendering, no waiting on selectors. In a local run on 5 September 2026, 200 jobs with full details came back in four seconds. That architecture is also why the price can be $2 per 1,000 jobs: a browser-based scraper burns roughly five times the compute for the same result, and the buyer pays for it in the end.
It exports past the 1,000-result ceiling
The search API caps any single query at 1,000 reachable hits, which is the wall every WTTJ scraper hits, and the reason most of them quietly return 1,000 rows and call it a day. This one counts the matches first, and when the count exceeds the cap it splits the query recursively on the publication timestamp until every segment fits under it, then walks the segments newest first. maxItems: 0 therefore means every matching job, verified on exports of 3,693 and 9,064 jobs, and the full 88,902-listing corpus is reachable the same way.
Direct ATS apply URL
When the employer routes applications to their own system, applyUrl is that system's URL and ats is external or kombo. In a 300-listing sample taken on 5 September 2026, 213 carried one, landing on SmartRecruiters, Greenhouse, Workday, Talentsoft, Aplitrak, Njoyn and a long tail of French ATS vendors. That single field is what turns a scraped listing into something actionable: it identifies the employer's recruiting stack for ATS-integration sales, and it lets an aggregator send candidates to the source rather than to an intermediate page.
Fifteen filters, applied before collection
Country, city, GPS radius, contract type, remote policy, listing language, maximum experience, education level, minimum yearly salary, company sector, profession category, company size range, publication window, company list, title-only search. All of them are translated into search-API filters and applied server-side, so a narrow query is faster and costs less, rather than fetching everything and throwing most of it away.
It keeps working when the key rotates
The public search key is read from the live Welcome to the Jungle runtime config at the start of every run, with a bundled fallback if that request fails. Key rotations do not break scheduled runs. The search itself is tried across four API hosts in turn, so a single host having a bad day does not fail the run.
It respects your spend cap and fails honestly
Charging happens as each job is written, through the mechanism that enforces the per-run maxTotalChargeUsd limit you set. When the cap is reached the run stops cleanly and says so in the log and the status message. And when something genuinely breaks, an invalid GPS point for instance, the run is marked FAILED rather than reported as a successful empty run.
Pricing
Pay-per-event, no monthly fee, no compute-unit surprise on top.
| Event | Price | When it fires |
|---|---|---|
job-scraped | $0.002 | Once per job written to the dataset, so $2 per 1,000 jobs |
apify-default-dataset-item | $0.00001 | Once per dataset item |
apify-actor-start | $0.00005 | Once per run, per GB of memory allocated |
Worked examples, at 1 GB of memory:
- 100 jobs, the default run: 100 × $0.00201 + $0.00005 = $0.20
- 1,000 jobs: $2.01
- 5,000 jobs: $10.05
- Every French listing, 70,853 jobs on 5 September 2026: $142.42
- The entire corpus, 88,902 jobs: $178.69
Two things that are not obvious and are worth stating plainly. Full job details cost exactly the same as titles only, because you are billed per job, not per request, so there is no reason to run with includeDetails: false unless you want speed. And filtering is free: the filters are applied by the search API before collection, so narrowing a search cuts the bill proportionally.
Limits, and what this scraper does not do
It does not collect personal data. No candidate profiles, no recruiter names, no applicant information. Only public job listings and the public company profile attached to them.
applyUrl is null when the employer uses Welcome to the Jungle's own application kit, which the ats field reports as wkit. There is no external URL to return in that case, because the application genuinely happens on Welcome to the Jungle. Use url as the fallback link.
description, skills, tools, applyUrl, companyWebsite, companyLinkedin and the rest of the detail block only exist when includeDetails is true. With the option off, those keys are absent or null.
Salary is published on a minority of listings: 29,488 of 88,902 on 5 September 2026. Setting salaryYearlyMin excludes every listing without a figure, which is the right call for compensation research and the wrong one for exhaustive collection.
Descriptions, candidate profiles and recruitment processes come back as HTML, the way the employer wrote them in the editor. Strip the tags yourself if your pipeline wants plain text.
Localized labels follow resultsLanguage, but the job text is never translated. A French listing stays in French whatever you set, and companyIndustry comes back in English regardless.
There is no cross-run deduplication yet. Results are deduplicated within a run, on the job UUID; if you run daily, keep your own register of seen id values, or merge into a named dataset.
publishedAt is refreshed when an employer renews a listing, so it is not a reliable first-publication date. Use it as a recency signal, not as an archive timestamp.
The cities filter is an exact match on the name as Welcome to the Jungle writes it, so a typo returns nothing rather than an approximation. For anything that looks like a labour market rather than a single town, aroundLatLng with a radius is the better tool.
A very large export with details takes real time, roughly ten jobs per second in steady state once the detail API starts rate-limiting, so plan around 600 jobs per minute and raise the run timeout accordingly. The Actor logs a warning with an estimate when a run is going to be long.
Company slugs that currently have no open job return nothing. That is not a bug, it is a company with no active listings.
This Actor scrapes jobs. It does not scrape Welcome to the Jungle company pages on their own, employee reviews, ratings, media articles, or the video content on company profiles.
FAQ
Can I scrape Welcome to the Jungle without an account? Yes. The Actor uses public endpoints and never logs in. You need an Apify account to run it, not a Welcome to the Jungle one.
Is scraping Welcome to the Jungle legal? The Actor collects publicly available job listings through the same public APIs the website itself calls. It accesses no personal data, does not authenticate, and bypasses no access control. Job listings are information employers pay to have distributed. That said, legality depends on what you do with the data, so check the site's terms of service and the rules that apply to your use case and jurisdiction, particularly if you republish content.
How do I get the direct apply URL for a WTTJ job?
Leave includeDetails on, which is the default, and read applyUrl. When it is null, check ats: a value of wkit means the employer receives applications on Welcome to the Jungle itself and there is no external URL.
How many Welcome to the Jungle job listings are there?
88,902 on 5 September 2026, of which 70,853 in France. The number moves daily. Set maxItems: 0 with no filters and the Actor exports all of them.
Can I export more than 1,000 WTTJ jobs? Yes, that is one of the reasons this Actor exists. The search API caps a single query at 1,000 hits; the Actor splits the export by publication date until every slice fits under the cap. Exports of 3,693 and 9,064 jobs are verified, and the full corpus is reachable.
How do I export WTTJ jobs to CSV or Excel?
From the console, open the dataset after the run and pick CSV, Excel, JSON, XML or RSS. From the API, add format=csv or format=xlsx to the dataset items call. The cities field exists precisely so that spreadsheets get a readable location column without unpacking the offices array.
Does it cover jobs outside France?
Yes. The corpus is shared across all Welcome to the Jungle regional sites, and the country filter works everywhere: 16,677 listings in the United States, 4,750 in the United Kingdom, 1,877 in Canada, plus Spain, Germany, Belgium, Switzerland, Italy and the Czech Republic. resultsLanguage only changes the language of labels, not which jobs you see.
How fresh is the data? The Actor queries the live search index the website itself uses, so results reflect the site at the moment of the run. Around 2,700 listings enter or are renewed in the corpus every 24 hours.
Can I run the Welcome to the Jungle scraper on a schedule?
Yes, with Apify Schedules. The usual setup is a daily run with publishedWithinDays: "1" and sortBy: "newest". Handle deduplication downstream on the id field, since Welcome to the Jungle refreshes publication dates on renewal.
Does it return salary data? When the employer published it, which is roughly a third of listings. You get the raw range, the currency, the period, a ready-to-display string, and the yearly-normalized minimum that makes daily rates comparable with annual packages.
Can I get all the jobs of one company?
Put the company slug from its Welcome to the Jungle URL, or its exact name, into companySlugs. Multiple companies in one run is fine, and the other filters are ignored for them.
Do I need a proxy? No. The search API needs none and detail requests work from Apify's datacenter IPs, with automatic retries when one is rate-limited. If a very large run logs repeated detail failures, set a residential proxy in the Advanced section as a fallback.
A filter I need is missing, or something looks wrong. What do I do? Open an Issue on the Actor page with the input you ran. Filters that exist in the underlying search index are usually quick to add.
Integrations
The dataset is a standard Apify dataset, so everything the platform offers applies to it. Export as JSON, JSONL, CSV, Excel, XML or RSS, from the console or by adding format= to the API call. Point a webhook at your own endpoint so a finished run pushes straight into your pipeline instead of you polling for it. Connect Zapier, Make, n8n, Google Sheets, Slack or Airtable through the Apify integrations, or write to Google Drive and Amazon S3 from the run.
For agents, Apify exposes its Actors through its MCP server, so an assistant that speaks MCP can call this scraper as a tool and read the results back without any glue code of yours. The input schema is descriptive enough for a model to fill in on its own, which is the point of every field having a description that says what happens when you leave it empty.
Built and maintained by Alex Morain. Bug reports and filter requests go in the Issues tab of the Actor page and get looked at.