# Welcome To The Jungle Jobs Scraper (`nice_dev/welcometothejungle-jobs-scraper`) Actor

Scrape Welcome to the Jungle job ads by keyword, city, country, contract, salary, sector or company: title, description, apply link, skills, salary, office GPS and company data. Export to JSON, CSV or Excel.

- **URL**: https://apify.com/nice\_dev/welcometothejungle-jobs-scraper.md
- **Developed by:** [Nice Dev](https://apify.com/nice_dev) (community)
- **Categories:** Jobs, Lead generation, MCP servers
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.34 / 1,000 job ads

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

### 💼 What is Welcome To The Jungle Jobs Scraper?

**Welcome To The Jungle Jobs Scraper** extracts **job ads from [Welcome to the Jungle](https://www.welcometothejungle.com)**: **title, full description, apply link, salary, contract, remote policy, skills, office address with GPS and the employer's profile**, for any keyword, city, country, sector or company.

Type a **keyword** (`data engineer`), pick a **city**, or paste a job page or search URL, click **Start**, and download the jobs in JSON, CSV or Excel. No login, nothing to set up, and it is **light: one request per job page, one per 100 job cards** — on the Apify platform, with its proxy and the default settings, 100 jobs with their full details took about 1 minute.

### 📋 What data can you extract from Welcome to the Jungle?

One item per job, 108 fields:

| Category | What you get |
| --- | --- |
| 💼 **Job** | title, short pitch, full description (text and HTML), required profile, key missions — `Chef de projet comptabilité` |
| 📄 **Contract** | contract type and duration, start date, experience and education required — full-time, 6 to 12 months, 4-5 years |
| 🏠 **Remote** | remote policy — full, partial, occasional or none |
| 💰 **Salary** | minimum and maximum, currency, period, yearly figure — 45,000-55,000 EUR a year |
| 🎁 **Benefits and skills** | benefits, skills, tools — days off (RTT), business development, Jira |
| 📍 **Location** | every office with its full address, ZIP code, city, region, country and GPS — Saint-Herblain, 44800, France |
| ✉️ **How to apply** | the employer's own application link, the hiring steps, the screening questions of the form |
| 🏢 **Company** | name, size, creation year, sector, revenue, website, logo, headquarters, gender parity, average age, equality index, labels and commitments |
| 🔗 **Company social accounts** | LinkedIn, X (Twitter), Facebook, Instagram and YouTube account names |
| 🗂️ **Job category** | job family, sub-family and role, sectors, in the site's language — Affaires et finance › Comptabilité › Contrôleur de gestion |
| 🕒 **Dates and monitoring** | publication and update date, republished-ad flag, a fingerprint that changes when the ad changes |

Every field, with an example, is listed in the **Output** section below.

Fields marked `(detail)` in the Output tab (the full description, `applyUrl`, `skills`, `tools`, `officeZipCode`, the company profile…) come from the job page and need **Extract job details**, on by default — at **the same price**. With it off, the search results alone still fill 68 of the 108 fields. Fields marked `(company)` need **Extract company details**.

### ✅ Why use Welcome To The Jungle Jobs Scraper?

- 🚀 **Light**: one request per job page, one per 100 job cards — measured on the Apify platform, with its proxy and the default settings: 100 jobs with their full details in about 1 minute, 1,000 in under 9 minutes, and the 3,113 jobs posted in the last day, without details, in 1 minute.
- 🎯 **Type the keyword, not a URL**: keyword, city, region, country, contract, salary, sector, job family, company or employer label.
- 🧩 **Nothing missed, nothing twice**: pagination is automatic, a search bigger than the site paginates is read in pieces, and each job is returned once.
- 🗂️ **Several searches in one run**: keywords × cities, with a cap per search so that one big city cannot eat the whole budget.
- 🔔 **Monitoring built in**: tick **Only new jobs**, schedule the Actor, and each run returns (and charges) only what it has never delivered before, plus the ads republished since (flagged `isRepost`, and skippable).
- 📅 **Filters**: `postedAfter: "7 days"`, excluded keywords, excluded companies, salary required — a filtered-out job is not saved and never counts in `maxItems`; `postedWithinDays` is applied by the site itself, for free.
- 🔌 API, scheduling, monitoring, integrations (Make, Zapier, n8n, Google Sheets…), proxy rotation and JSON/CSV/Excel export via the Apify platform.

### 🚀 How to scrape Welcome to the Jungle

1. Create a free Apify account.
2. Open **Welcome To The Jungle Jobs Scraper** and type a **Search keyword** (e.g. `data engineer`), then a **City** if you want one (`Paris`).
3. Or paste your own Welcome to the Jungle URLs into **Start URLs**: single job pages, or a search URL (`https://www.welcometothejungle.com/fr/jobs?query=...`, pagination is automatic).
4. Set **Max jobs** (100 by default, 0 = no limit) — and **Max jobs per search** when you run several searches — then click **Start**.
5. Download the dataset in JSON, CSV, Excel or via API.

### 💰 How much does it cost to scrape Welcome to the Jungle?

This Actor uses **pay per event** pricing: **$0.40 per 1,000 jobs**, full details included — plus **$0.0011 per run start** at the Actor's default memory (11 cents per 100 runs; the platform counts that event once per gigabyte, so a run you give more memory pays proportionally more). Higher Apify plans pay less per job: $0.38 (Bronze), $0.36 (Silver) and $0.34 (Gold and above) per 1,000. Example: 20,000 jobs ≈ $8; a daily monitor of 300 new jobs ≈ $0.12. The filters the site does not apply itself are checked by the Actor on every job it reads: a job you keep costs its normal price, a job a filter drops costs **$0.18 per 1,000** (`filter-check`: `postedAfter` / `postedBefore`, `excludeKeywords`, `excludeCompanies`, `onlyWithSalary`, `skipReposts`) or **$0.18 per 1,000** when a filter that needs the job page drops it (`detail-filter-check`: `onlyWithApplyUrl`, `onlyWithApplicationQuestions`) — $0.175 and $0.171 on Bronze, $0.17 and $0.162 on Silver, $0.165 and $0.153 on Gold. Filtering never costs more than taking every job. Platform usage (compute, proxy) is included in the price.

### ⚙️ Input

```json
{
    "query": "data engineer",
    "location": "Paris",
    "maxItems": 200,
    "includeDetails": true
}
```

Several searches, a cap per search, only recent jobs, only the ones not delivered before:

```json
{
    "searchQueries": ["data engineer", "data analyst"],
    "locations": ["Paris", "Lyon"],
    "maxItemsPerQuery": 50,
    "postedAfter": "7 days",
    "onlyNew": true,
    "stateKey": "data-fr"
}
```

Or with your own URLs:

```json
{
    "startUrls": [
        { "url": "https://www.welcometothejungle.com/fr/jobs?query=data&sortBy=date" },
        { "url": "https://www.welcometothejungle.com/fr/companies/rca/jobs/chef-de-projet" }
    ],
    "maxItems": 500
}
```

| Field | Notes |
| --- | --- |
| `query`, `searchQueries` | Free-text search, exactly as typed on the site; `searchQueries` adds more keywords (one search each). Ignored when `startUrls` is set. |
| `location`, `locations` | Office city, spelled as the site does (`Paris`, `London`); `locations` adds more cities. Every keyword is searched in every city (max 500 searches per run). |
| `startUrls` | Search results pages (pagination automatic; the filters of the input that the URL does not set are added; searched on the site of the URL's language — set `siteLanguage` to the same one) or single job pages. |
| `includeDetails` | Read each job page for the full description, the apply link, the skills, the ZIP code and the GPS of the office (default on, same price). |
| `includeCompanyDetails` | Read each company page for all its offices and the date its profile last changed, with or without `includeDetails` (off by default, same price, one more request per company of the run: 61 for the 100 first jobs of the pre-filled search, 28 for the 100 newest jobs of the site, measured on 2026-09-22). |
| `maxItems` | Stop after this many jobs for the whole run (0 = unlimited). |
| `maxItemsPerQuery` | Cap for EACH search (keyword × city, or search URL). 0 = no per-search cap. |
| `countryCodes`, `states`, `districts` | Office country (`FR`, `US`, `GB`), region (`Ile-de-France`) and department (`Hauts-de-Seine`), as the site spells them. |
| `contractTypes`, `remoteTypes`, `educationLevels` | `full_time`, `internship`, `apprenticeship`…; `fulltime`, `partial`, `punctual`, `no`; `bac_5`, `bac_3`… |
| `experienceMin`, `experienceMax` | Years of experience the job asks for. |
| `salaryMin`, `salaryMax`, `salaryCurrencies` | Yearly salary range stated by the employer, and its currency (`EUR`, `USD`, `GBP`). Only about a quarter of the jobs show one. |
| `sectors`, `professionCategories`, `professionSubCategories` | Company sector (`software-1`), job family (`tech-engineering-3NjUy`) and sub-family, by site reference or by name in any of the site languages (`Software` or `Logiciels`): a name is turned into its reference. |
| `companies`, `companyLabels`, `companyCommitments`, `companySizeMin`, `companySizeMax` | Only these employers (`carrefour`), with these labels (`happy-trainees`) or commitments (`social-commitment`), of this size. |
| `benefits`, `jobLanguages`, `postedWithinDays` | Benefits as the site words them in the **Site language** you pick (`RTT / Jour de repos` in French), language of the ad (`fr`, `en`), jobs published in the last N days. |
| `latitude`, `longitude`, `radiusKm` | Jobs whose office is within a radius of a point (the three go together). |
| `siteLanguage`, `sortBy`, `searchInTitleOnly` | Which country site is searched (`fr`, `en`, `es`, `cs`, `sk`), `relevance` or `date`, and whether the keyword matches the title only. |
| `postedAfter`, `postedBefore` | Publication date range: `2026-09-01`, or a period before now (`7 days`, `2 weeks`, `1 month`, `24 hours`). |
| `excludeKeywords`, `excludeCompanies` | Drop the jobs whose title or summary contains one of these words, or that belong to these companies. |
| `onlyWithSalary`, `onlyWithApplyUrl`, `onlyWithApplicationQuestions`, `skipReposts` | Keep only the jobs that state a salary, that link to the employer's own application system, that ask screening questions; drop the ads republished since they were delivered. |
| `onlyNew`, `stateKey`, `resetState` | Monitoring: only the jobs never delivered under this memory key (a job republished since comes back once, flagged as a repost); `resetState` forgets the memory. |
| `excludeEmptyFields` | Drop the keys that are null or empty from each item (the required ones stay). |
| Advanced | `proxyConfiguration` (Apify proxy by default, included in the price; the residential proxy is not available), `maxConcurrency`, `maxRequestsPerMinute` (a job page and its company page count as one request), `maxRequestRetries`, `debugLog`. |

### 📦 Output

One real item, shortened to its main fields (a run gives all 108):

```json
{
    "id": "f8ee62c7-2fa8-4d09-bc49-2393a8d1d202",
    "reference": "RCA_VA3z7px",
    "url": "https://www.welcometothejungle.com/fr/companies/rca/jobs/chef-de-projet-comptabilite-fiscalite-f-h_saint-herblain_RCA_VA3z7px",
    "title": "CHEF DE PROJET COMPTABILITÉ/FISCALITÉ F/H",
    "summary": "Rejoins RCA, une entreprise dynamique en pleine croissance.",
    "description": "Aujourd'hui, pour évoluer au sein de notre Direction Produit…",
    "keyMissions": [
        "Incarner et porter la vision du produit"
    ],
    "applyUrl": "https://jobs.smartrecruiters.com/RCA1/744000150295830",
    "contractType": "full_time",
    "remote": "partial",
    "experienceLevelMin": 4,
    "salaryMin": null,
    "hasSalary": false,
    "benefits": [
        "Horaires de travail flexibles",
        "Entre 3-4 jours de télétravail"
    ],
    "skillNames": [
        "Développement commercial",
        "Communication"
    ],
    "publishedAt": "2026-09-19T00:01:28.000Z",
    "sectors": [
        {
            "name": "Logiciels",
            "reference": "software-1",
            "parentName": "Tech"
        }
    ],
    "offices": [
        {
            "city": "Saint-Herblain",
            "countryCode": "FR",
            "district": "Loire-Atlantique",
            "state": "Loire Region",
            "address": "Rue Édith Piaf, 44800 Saint-Herblain, France",
            "zipCode": "44800",
            "latitude": 47.22967,
            "longitude": -1.6267
        }
    ],
    "officeCity": "Saint-Herblain",
    "officeZipCode": "44800",
    "latitude": 47.22967,
    "longitude": -1.6267,
    "companyName": "RCA",
    "companySize": 300,
    "companyEqualityIndex": 84,
    "companyJobsCount": 5,
    "contentHash": "2f5a1c9d4b6e8a0f1c3d5e7a9b1c3d5e7a9b1c3d",
    "detailFetched": true,
    "companyDetailFetched": false,
    "searchQuery": "data engineer",
    "scrapedAt": "2026-09-20T12:00:00.000Z"
}
```

You can download the dataset in various formats such as JSON, HTML, CSV or Excel.

#### All 108 fields

| Fields | What you get |
| --- | --- |
| `id`, `reference`, `slug`, `wkReference`, `url` | `f8ee62c7-…`, `RCA_VA3z7px`, `chef-de-projet…`, `RCA_VA3z7px`, `https://www.welcometothejungle.com/fr/companies/rca/jobs/chef-de-projet…` |
| `title`, `summary`, `language` | `Chef de projet comptabilité`, short pitch, `fr` |
| `description`, `descriptionHtml`, `profile`, `profileHtml`, `keyMissions` | full text and HTML of the ad, the required profile, the list of missions |
| `recruitmentProcess`, `recruitmentProcessHtml`, `applyUrl`, `ats` | hiring steps, the employer's own application link, `external` |
| `contractType`, `contractDurationMin`, `contractDurationMax` | `full_time`, `6`, `12` (months) |
| `remote`, `hasRemote` | `partial`, `true` |
| `experienceLevelMin`, `experienceLevel`, `educationLevel` | `4`, `4_TO_5_YEARS`, `bac_5` |
| `salaryMin`, `salaryMax`, `salaryCurrency`, `salaryPeriod`, `salaryYearlyMin`, `hasSalary` | `45000`, `55000`, `EUR`, `yearly`, `45000`, `true` |
| `benefits`, `skills`, `skillNames`, `tools` | `RTT / Jour de repos`, skills with their reference, `Développement commercial`, `Jira` |
| `applicationFields`, `atsQuestions` | what the application form asks, the employer's screening questions |
| `publishedAt`, `publishedAtDate`, `publishedAtTimestamp`, `updatedAt`, `startDate`, `status`, `archivedAt`, `isBoosted` | `2026-09-19T00:01:28.000Z`, `2026-09-19`, `1789776088`, `2026-09-19T00:01:35.109Z`, `2026-10-05` (the start day the employer picked), `published`, `null`, `false` |
| `professionCategory`, `professionCategoryReference`, `professionSubCategory`, `professionSubCategoryReference`, `professionPivot`, `professionPivotReference` | `Affaires et finance`, `business-finance-1MjQ4`, `Comptabilité`, `accounting-5Nzg0`, `Contrôleur de gestion`, `management-accountant-5MThl` |
| `sectors`, `sectorNames`, `sectorReferences` | sectors with their parent, `Logiciels`, `software-1` |
| `offices`, `officeCity`, `officeDistrict`, `officeState`, `officeCountry`, `officeCountryCode`, `officeAddress`, `officeZipCode`, `latitude`, `longitude` | every office (main one first, each with its own address and GPS), then that first one: `Saint-Herblain`, `Loire-Atlantique`, `Loire Region`, `France`, `FR`, `Rue Édith Piaf, 44800 Saint-Herblain, France`, `44800`, `47.22967`, `-1.6267` |
| `companyName`, `companySlug`, `companyUrl`, `companyReference`, `companySummary`, `companyDescription`, `companyDescriptionHtml` | `RCA`, `rca`, its page on the site, `5xRxGwr`, what the employer says about itself |
| `companySize`, `companyCreationYear`, `companyEqualityIndex`, `companyLabels`, `companyCommitments`, `companySectors` | `300`, `1999`, `84`, `professional-equality`, `environmental-impact`, `Software` |
| `companyProfileType`, `companyProfileRanking`, `companyScore`, `companyLogoUrl`, `companyCoverImageUrl`, `companyIndustry`, `companyJobsCount` | `standard`, `100`, `100`, logo and cover, `Software, SaaS / Cloud Services`, `5` |
| `companyWebsiteUrl`, `companyRevenue`, `companyTurnover`, `companyParityMen`, `companyParityWomen`, `companyAverageAge`, `companyHasExternalAts` | `https://jobs.rca.fr/`, `42M€`, `null`, `57`, `43`, `34`, `true` |
| `companyHeadquarterCity`, `companyHeadquarterAddress`, `companyHeadquarterCountryCode` | `Saint-Herblain`, `Rue Édith Piaf`, `FR` |
| `companyLinkedin`, `companyTwitter`, `companyFacebook`, `companyInstagram`, `companyYoutube` | the employer's account **name** on each network, as the site stores it: `rca-logiciels`, `RCA_logiciels`, `rca.nantes`, `rca_nantes`, `@RCA_logiciels` (handles, not links) |
| `companyOffices`, `companyUpdatedAt` | every office of the company with its ZIP code and GPS (company) |
| `contentHash`, `isRepost`, `detailFetched`, `companyDetailFetched` | `2f5a1c…` (same hash = nothing changed), `false`, `true`, `false` |
| `searchQuery`, `searchUrl`, `scrapedAt` | `data engineer`, the search page it was found on, ISO timestamp |

### 💡 Tips

#### How to get more results

Set `maxItems` to `0` and use a broad keyword — or no keyword at all with a country filter. A single search of the site stops at 1,000 results; the Actor gets past that by reading the rest of the search in pieces by publication date, so a country-wide run returns everything the site has. With **Sort by** on `date` the jobs still come newest first across the whole run, so a capped run gives you the newest ones, not a random slice.

#### How to reduce costs

The price is per job, so the levers are `maxItems`, `maxItemsPerQuery`, the filters (a filtered-out job costs only its filter check, $0.18 per 1,000 instead of $0.40; `postedWithinDays` is applied by the site and costs nothing) and `onlyNew` for recurring runs (you never pay twice for the same job; with `skipReposts`, not even when the employer republishes it). Turning `includeDetails` off makes runs faster but does not change the price.

#### Several searches in one run

Fill `searchQueries` and / or `locations`: the Actor runs one search per keyword × city (3 keywords × 4 cities = 12 searches, up to 500 per run). The single `query` and `location` fields still work and are added to the lists. A job found by several searches is saved — and charged — once. Set `maxItemsPerQuery` to give every search its own cap: without it the first searches can use up the whole `maxItems` budget. You can also paste several search URLs into `startUrls`: each one is a search of its own, with the same cap.

#### Monitoring: only the new jobs

Tick **Only new jobs** (`onlyNew`) and schedule the Actor. The first run returns everything; each later run skips the jobs already delivered: they are not saved, not charged, and their page is not even read. The memory lives in a named key-value store of your account (`welcometothejungle-jobs-scraper-seen`, up to 150,000 jobs per key) and is only updated with jobs that really reached the dataset, so a failed run never hides anything. Give each schedule its own `stateKey` (two schedules sharing a key would hide each other's jobs), and tick `resetState` once to start over. Searches are then sorted by newest, and a search stops once it meets 100 jobs in a row that you already have (boosted ads pinned at the top are not counted, and neither are ads republished since your last run — the site gives them a new publication date, which puts them back on top). Those republished ads come back once, with `isRepost: true`; tick `skipReposts` to drop them instead (their page is not read, and they are not saved; each new job checked costs the filter fee, $0.18 per 1,000).

#### Filter by publication date

`postedAfter` and `postedBefore` take a date (`2026-09-01`, the whole day is included, UTC) or a period before now (`7 days`, `2 weeks`, `1 month`; via the API also `24 hours` or a full ISO date-time). The filter reads `publishedAt`; a job without a publication date is dropped as soon as a date bound is set. Filtered-out jobs are not saved and do not count in `maxItems` (each job dropped costs the filter fee, see Pricing); the run summary tells how many were filtered. With `postedAfter`, searches are sorted by newest and stop at the first page that is entirely too old. `postedWithinDays` does the same job on the site's side: the older ads are never downloaded at all.

### 🔌 Integrations and API

Call the Actor via the Apify API, the JavaScript or Python clients, or connect it with integrations and webhooks (Make, Zapier, n8n, Google Sheets, Slack, Airtable…). The dataset can be fetched as JSON or CSV from any tool.

### 🤖 Use with AI agents (MCP)

AI agents (Claude, ChatGPT, Cursor…) can find and run this Actor through the [Apify MCP server](https://mcp.apify.com), billed to their Apify account like any run. It returns one item per Welcome to the Jungle job ad. Actor id: `nice_dev/welcometothejungle-jobs-scraper`; MCP server with this Actor only: `https://mcp.apify.com/?tools=fetch-actor-details,nice_dev/welcometothejungle-jobs-scraper`.

Smallest input, for a cheap first call:

```json
{
    "query": "data engineer",
    "maxItems": 10
}
```

Key output fields: `url`, `title`, `companyName`, `contractType`, `remote`, `salaryMin`, `officeCity`, `publishedAt`.

Cost: $0.40 per 1,000 jobs plus $0.0011 per run start at the default memory ($0.34 per 1,000 on the Gold plan); a job a filter drops costs $0.18 per 1,000, see the pricing section above. Cap each call with `maxItems` and, through the API, with the run option `maxTotalChargeUsd`.

### ❓ FAQ

#### Is it legal to scrape Welcome to the Jungle?

The Actor only reads what Welcome to the Jungle shows publicly to any anonymous visitor. It logs in to nothing. Job ads and company profiles are professional data; the Actor never returns the names of the people behind them. Results may still contain personal data, which is protected by GDPR: do not store it without a legitimate reason. You are responsible for using the data in compliance with Welcome to the Jungle's Terms of Use and applicable law. This Actor is not affiliated with Welcome to the Jungle.

#### Does it need a login or a proxy?

No login. The proxy is included in the price: leave the default setting (the residential proxy is not available). A request the site turns away is retried at once on a new proxy session (without a proxy, after a pause of 5 seconds, doubled at each retry up to 150 seconds).

#### Is the data safe to open in Excel or to show on a web page?

Titles and descriptions are the employers' own words, copied as they are. A text can begin with `-`, `+`, `=` or `@` (a title such as `-20% de turnover`): Excel and Google Sheets may read such a cell of a CSV file as a formula or as a number. The Actor leaves the text as it is, so that the JSON and the API give the real value: when you open a CSV, import these columns as text. Every URL field holds an http(s) URL or `null`, and the `…Html` fields hold the employer's own HTML, which the Actor does not sanitize: on a web page, escape every field like any text written by a stranger.

#### Known limitations

- The full description, `applyUrl`, `skills`, `tools`, `officeZipCode` and the company profile come from the job page: they are `null` when `includeDetails` is off.
- With `includeDetails` off, a job with **several offices** gets no `latitude` / `longitude`: the search results give one pair of coordinates without saying which office it belongs to, and `officeCity` is then the first office by name, not necessarily the main one. With the details on (default), every office carries its own address and GPS and the main one comes first.
- `professionPivot` / `professionPivotReference` (the standard job name the site maps the ad to) come from the search results: they are `null` for a job URL pasted into `startUrls`, as are `sectors`, `salaryYearlyMin`, `hasRemote`, `isBoosted` and `companyScore`.
- With `includeDetails` off, about a third of the ads give the required profile as Markdown instead of HTML: `profile` holds it as text and `profileHtml` is `null`.
- `companyOffices` and `companyUpdatedAt` need `includeCompanyDetails` (one more request per company of the run, with or without `includeDetails`).
- `onlyNew` remembers job ids, not their content: a job whose description changed is not returned again. `contentHash` lets you spot such a change on your side.
- Two runs sharing the same `stateKey` at the same time may both return the same new job.
- A salary is shown on about a quarter of the ads (26 % of the 8 767 jobs counted on the site on 2026-09-22, between 20 % and 41 % depending on the day); `salaryMin` / `salaryMax` are `null` on the others.

**A run the platform stops without warning** (out of memory, run timeout)

- Resurrect it: it goes on from where it stood at most a minute before the stop. What it had read since is read again, and the jobs already saved are skipped: none is delivered or charged twice, and `maxItems` still counts them.
- With `onlyNew`, the memory is saved once a minute: resurrect the stopped run and the jobs it had saved meanwhile join the memory; leave it stopped for good, and the next run may return up to a minute of them once more.

#### Something doesn't work?

The last line of the log counts the jobs saved, filtered out and no longer on Welcome to the Jungle (taken down while the run was reading them), and the requests that failed after every retry. Those requests and the removed jobs are listed, with the reason, in the `FAILED_REQUESTS` record of the run's key-value store. A run that saved nothing and had failed requests fails, and its last message gives the cause (a search that does not exist says so, instead of "run it again"). A run that saved some jobs fails too when at least as many requests failed for good as were read (page 1 read, the next pages or the job pages blocked): a green run with a short dataset would hide the outage. One failed request among many is only a warning.

If Welcome to the Jungle changes its pages, you are told instead of paying for blank rows. A results page that counts jobs but gives none the Actor can read is an error (listed in `FAILED_REQUESTS`), never a quiet "No jobs found". If the first 20 jobs read all lack their title, publication date, company name or — with `includeDetails` — description, the run saves nothing more, stops and fails, and its last message names the missing field: at most those first jobs are charged. A job that `postedAfter` / `postedBefore` drops because it has no date at all counts among those 20.

### 🛟 Support

Open an issue in the **Issues** tab with a link to your run: the run log and the `FAILED_REQUESTS` record of the key-value store show exactly which URLs failed and why.

# Actor input Schema

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

welcometothejungle.com search URLs (`https://www.welcometothejungle.com/fr/jobs?query=...`) or single job URLs (`https://www.welcometothejungle.com/fr/companies/<company>/jobs/<job>`). When this list is not empty, **Search keyword** and **City** below are ignored; the filters below are added to a search URL that does not set them itself, and the caps and the monitoring still apply. A search URL is searched on the site of its own language (`https://www.welcometothejungle.com/en/jobs?query=data` = the English site): set **Site language** to the same one. Max 1 000 URLs.

## `query` (type: `string`):

Free-text search, exactly as typed on the site (e.g. `data engineer`). Empty = every job.

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

Several keywords in one run: one search per keyword (times each city below). Added to **Search keyword**; jobs found by several searches are saved once.

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

Office city, spelled as the site does (e.g. `Paris`, `London`, `New York`). Empty = everywhere.

## `locations` (type: `array`):

Several cities in one run: every keyword is searched in every city (3 keywords × 4 cities = 12 searches, max 500). Added to **City**.

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

Maximum number of jobs to save for the whole run (after deduplication and filters). 0 = no limit (the whole index is 90 000+ jobs).

## `maxItemsPerQuery` (type: `integer`):

Cap for EACH search (keyword × city, or start URL), so that the first search cannot use up the whole **Max jobs** budget. 0 = no per-search cap.

## `includeDetails` (type: `boolean`):

Read each job page from the site API (1 extra request per job) for the full description, the apply link, the skills, the tools, the hiring process, the office address with ZIP code and GPS, and the application questions. Off = the search results alone: 68 of the 108 fields are still filled, for 1 request per 100 jobs — **same price**.

## `includeCompanyDetails` (type: `boolean`):

Read the company page for every office with its ZIP code and GPS, the headquarters flag and the date the company profile last changed. Works with or without **Extract job details**. Adds 1 request per company of the run, not per job: the jobs of a company share its page (measured on 2026-09-22: the 100 first jobs of the pre-filled search belong to 61 companies, the 100 newest jobs of the site to 28) — **same price**, a little slower. The company website, social accounts, open-job count, revenue and gender split already come with **Extract job details**.

## `countryCodes` (type: `array`):

ISO country codes of the office (e.g. `FR`, `US`, `GB`, `CA`, `DE`, `ES`). 249 values. Empty = every country.

## `states` (type: `array`):

Office region, spelled as the site does (e.g. `Ile-de-France`, `Auvergne-Rhone-Alpes`).

## `districts` (type: `array`):

Office department or district, spelled as the site does (e.g. `Hauts-de-Seine`, `Rhône`).

## `contractTypes` (type: `array`):

Contract type as the site codes it.

## `remoteTypes` (type: `array`):

How much remote work the job allows.

## `experienceMin` (type: `integer`):

Only jobs asking for at least this many years of experience (site field `experience_level_minimum`).

## `experienceMax` (type: `integer`):

Only jobs asking for at most this many years of experience.

## `educationLevels` (type: `array`):

Required diploma as the site codes it (French `bac` scale). Every value the site holds, read from its own counters.

## `salaryMin` (type: `integer`):

Only jobs whose stated yearly minimum salary is at least this amount (site field `salary_yearly_minimum`). Only about a quarter of the jobs show a salary (26 % counted on the site on 2026-09-22): setting this drops the others.

## `salaryMax` (type: `integer`):

Only jobs whose stated yearly minimum salary is at most this amount.

## `salaryCurrencies` (type: `array`):

Currency of the stated salary (`EUR`, `USD`, `GBP`).

## `sectors` (type: `array`):

Company sector, by site reference (e.g. `software-1`, `e-commerce-1`, `finance-2`) or by name in any of the site languages (`Software`, `Logiciels`): a name is turned into its reference, whatever **Site language** is. 112 values.

## `professionCategories` (type: `array`):

Job family, by site reference (e.g. `tech-engineering-3NjUy`, `sales-customer-service-5MzIw`) or by name in any of the site languages (`Tech & Engineering`, `Technologie et ingénierie`): a name is turned into its reference, whatever **Site language** is. 20 values.

## `professionSubCategories` (type: `array`):

Job sub-family, by site reference (e.g. `software-web-development-iMzA4`) or by name in any of the site languages (`Accounting`, `Comptabilité`): a name is turned into its reference, whatever **Site language** is. 179 values.

## `companies` (type: `array`):

Only these companies, by site slug (e.g. `carrefour`, `thales`) or by exact name. The slug is the part after `/companies/` in a job URL.

## `companyLabels` (type: `array`):

Employer labels as the site codes them (e.g. `happy-trainees`, `great-place-to-work`, `ecovadis-gold`, `qualiopi`). 52 values.

## `companyCommitments` (type: `array`):

Employer commitments as the site codes them.

## `companySizeMin` (type: `integer`):

Only companies with at least this many employees (site field `organization.nb_employees`).

## `companySizeMax` (type: `integer`):

Only companies with at most this many employees.

## `benefits` (type: `array`):

Benefits as the site words them IN THE SITE LANGUAGE chosen below: `RTT / Jour de repos` on the French site, `Reduction of working time (RTT)` on the English one. 83 values. A label written in another language matches no job at all.

## `jobLanguages` (type: `array`):

Language the job ad is written in (`fr`, `en`, `de`, `es`, `it`, `pt`…). 13 values. Different from **Site language** below.

## `postedWithinDays` (type: `integer`):

Only jobs published in the last N days (filter applied by the site, so nothing older is downloaded). Counted on the site on 2026-09-22: ≈ 2 900 jobs in the last 24 h and ≈ 16 700 in the last 7 days, out of ≈ 90 000 — a fresh day changes those numbers.

## `latitude` (type: `string`):

Decimal latitude of the centre, e.g. `48.8566` for Paris.

## `longitude` (type: `string`):

Decimal longitude of the centre, e.g. `2.3522` for Paris.

## `radiusKm` (type: `integer`):

Distance around the point. Counted on the site on 2026-09-22: 10 km around Paris ≈ 17 200 jobs.

## `siteLanguage` (type: `string`):

Which country site is searched — it sets the language of the site labels, not the language of the job ad (see **Job languages**). A search URL pasted in **Start URLs** is searched on the site of its own language.

## `sortBy` (type: `string`):

`Relevance` uses the site's own ranking (best with a keyword). `Publication date` returns the newest jobs first and lets the run stop as soon as it goes past **Posted after**.

## `searchInTitleOnly` (type: `boolean`):

Match the keyword against the job title only, instead of the whole ad (title, summary, profile, company).

## `postedAfter` (type: `string`):

Only jobs published on or after this date (a UTC day, as the **Published on (day)** column gives it): `2026-09-01`, or a period before now such as `7 days`, `2 weeks`, `1 month` (`24 hours` and full ISO date-times work too).

## `postedBefore` (type: `string`):

Only jobs published on or before this date (the whole UTC day is included), or older than a period such as `30 days`.

## `excludeKeywords` (type: `array`):

Drop the jobs whose title or summary contains one of these words (case and accents ignored).

## `excludeCompanies` (type: `array`):

Drop the jobs of these companies, by site slug or exact name (e.g. staffing agencies you do not want).

## `onlyWithSalary` (type: `boolean`):

Drop the jobs that do not state a salary. Only about a quarter of the jobs do (26 % counted on the site on 2026-09-22).

## `onlyWithApplyUrl` (type: `boolean`):

Keep only the jobs that point to the employer's own application system (ATS). Needs **Extract job details**.

## `onlyWithApplicationQuestions` (type: `boolean`):

Keep only the jobs whose application form asks extra questions (screening questions). Needs **Extract job details**.

## `onlyNew` (type: `boolean`):

Skip the jobs that a previous run (same **Memory key**) already delivered: they are not saved and not charged, and their job page is not even read. First run = everything is new. A job the employer republished since then comes back once, with `isRepost: true` (tick **Skip reposted jobs** to drop it).

## `skipReposts` (type: `boolean`):

Drop the jobs that were already delivered and have been republished since (employers republish to get back on top): without this box they come back once, with `isRepost: true`. Needs **Only new jobs** to be on, since the memory of the previous runs is what tells a repost.

## `stateKey` (type: `string`):

Name of the memory used by **Only new jobs**. Give each schedule / task its own key (e.g. `paris-data-jobs`) so that they do not share their memory. Letters, digits, `-` and `_`.

## `resetState` (type: `boolean`):

Forget everything remembered under this **Memory key** before the run: this run returns (and charges) every job again. Untick it afterwards.

## `excludeEmptyFields` (type: `boolean`):

Drop the keys whose value is null or an empty list from each item (the required keys stay). Smaller JSON, ragged columns in CSV.

## `proxyConfiguration` (type: `object`):

Apify Proxy or your own proxies. Keep the default: it is included in the price. The residential Apify proxy is not available in this Actor — it would cost you money without returning a single job more.

## `maxConcurrency` (type: `integer`):

Maximum number of requests processed in parallel.

## `maxRequestsPerMinute` (type: `integer`):

Global request rate. Lower it if the site answers HTTP 429 / 403 in the log. With **Extract job details**, a job page and its company page count as one request.

## `maxRequestRetries` (type: `integer`):

Retries per request before it is marked as failed.

## `debugLog` (type: `boolean`):

Include debug messages in the run log.

## Actor input object example

```json
{
  "startUrls": [],
  "query": "data engineer",
  "searchQueries": [],
  "locations": [],
  "maxItems": 100,
  "maxItemsPerQuery": 0,
  "includeDetails": true,
  "includeCompanyDetails": false,
  "countryCodes": [],
  "states": [],
  "districts": [],
  "contractTypes": [],
  "remoteTypes": [],
  "educationLevels": [],
  "salaryCurrencies": [],
  "sectors": [],
  "professionCategories": [],
  "professionSubCategories": [],
  "companies": [],
  "companyLabels": [],
  "companyCommitments": [],
  "benefits": [],
  "jobLanguages": [],
  "siteLanguage": "fr",
  "sortBy": "relevance",
  "searchInTitleOnly": false,
  "excludeKeywords": [],
  "excludeCompanies": [],
  "onlyWithSalary": false,
  "onlyWithApplyUrl": false,
  "onlyWithApplicationQuestions": false,
  "onlyNew": false,
  "skipReposts": false,
  "stateKey": "default",
  "resetState": false,
  "excludeEmptyFields": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "maxConcurrency": 5,
  "maxRequestsPerMinute": 240,
  "maxRequestRetries": 5,
  "debugLog": false
}
```

# Actor output Schema

## `results` (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 = {
    "query": "data engineer",
    "maxItems": 100,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("nice_dev/welcometothejungle-jobs-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 = {
    "query": "data engineer",
    "maxItems": 100,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("nice_dev/welcometothejungle-jobs-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 '{
  "query": "data engineer",
  "maxItems": 100,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call nice_dev/welcometothejungle-jobs-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,nice_dev/welcometothejungle-jobs-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/eekbvps8ksnUWivQb/builds/3fcmyLqwKoLgNExV2/openapi.json
