# Welcome to the Jungle Scraper (`mlg14/welcome-scraper`) Actor

Find public job listings by keywords and filters; export titles, salaries, locations, descriptions, application links and employer data.

- **URL**: https://apify.com/mlg14/welcome-scraper.md
- **Developed by:** [MLG Data](https://apify.com/mlg14) (community)
- **Categories:** Jobs, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$0.30 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## Welcome to the Jungle Scraper

Scrape Welcome to the Jungle jobs by role keywords, location, country, contract, remote policy, salary, and other published attributes. Export Welcome to the Jungle job data as JSON, CSV, or spreadsheet files, with titles, pay ranges, offices, descriptions, application links, and employer context. This actor provides a repeatable way to collect public job listings when you need more than a manual search page.

The actor discovers listings from the site's public job sitemaps and reads structured data embedded in each job page. A listing can have several offices, and salary and employer fields depend on what was published. Each result carries a source URL and a stable job ID so later exports can be compared without relying on title alone.

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

The default full-detail result has 65 fields. The table describes each output key; examples show the expected shape, while empty values remain `null`, `[]`, or `{}` according to the field. Text descriptions retain source HTML so lists and paragraphs are not flattened away.

| Field | Description | Example |
|---|---|---|
| `id` | Stable identifier for a listing | `fff60753-...` |
| `reference` | Employer-supplied job reference | `ENG-42` |
| `name` | Published job title | `Software Engineer (Senior)` |
| `url` | Direct source page | `https://www.welcometothejungle.com/...` |
| `contractType` | Contract classification | `full_time` |
| `remote` | Remote-work policy | `partial` |
| `language` | Job language code | `en` |
| `salaryMin` | Published minimum pay | `195000` |
| `salaryMax` | Published maximum pay | `250000` |
| `salaryCurrency` | Currency of pay values | `USD` |
| `salaryPeriod` | Pay period | `yearly` |
| `experienceLevel` | Published experience band | `5_TO_7_YEARS` |
| `educationLevel` | Education level, when given | `null` |
| `contractDurationMin` | Minimum contract duration | `null` |
| `contractDurationMax` | Maximum contract duration | `null` |
| `publishedAt` | Publication timestamp | `2026-08-29T00:05:33Z` |
| `updatedAt` | Last update timestamp | `2026-08-29T00:19:58Z` |
| `category` | Localized occupation category | `Tech & Engineering` |
| `subcategory` | Localized occupation subcategory | `Software & Web Development` |
| `categoryReference` | Stable occupation category reference | `tech-engineering-3NjUy` |
| `subcategoryReference` | Stable occupation subcategory reference | `software-web-development-iMzA4` |
| `sectors` | Employer sector labels | `["Software"]` |
| `summary` | Brief listing summary, if provided | `""` |
| `offices` | All published office objects | `[{"city":"New York"}]` |
| `city` | Primary office city | `New York` |
| `countryCode` | Primary office country | `US` |
| `organizationName` | Employer's published name | `[employer]` |
| `organizationSlug` | Employer's site URL segment | `[employer-slug]` |
| `organizationLogo` | Employer logo URL | `https://cdn-images...` |
| `organizationEmployees` | Published employee count | `165` |
| `organizationCreationYear` | Published founding year | `null` |
| `organizationLabels` | Employer labels | `[]` |
| `organizationCommitments` | Published commitments | `[]` |
| `organizationIndustry` | Published industry label | `Software` |
| `organizationJobsCount` | Employer's published job count | `33` |
| `description` | Full job description in HTML | `<ul><li>...</li></ul>` |
| `profile` | Candidate profile in HTML | `<ul><li>...</li></ul>` |
| `recruitmentProcess` | Hiring steps, if published | `null` |
| `applyUrl` | Direct application destination | `https://...` |
| `skills` | Published skills | `["Communication skills"]` |
| `tools` | Published technical tools | `[technology]` |
| `benefits` | Localized benefit names | `[]` |
| `benefitDetails` | Complete benefit groups | `{}` |
| `keyMissions` | Structured mission list | `[]` |
| `companySummary` | Short employer summary | `[summary]` |
| `team` | Team description | `null` |
| `startDate` | Planned start date | `null` |
| `atsQuestions` | Application questions | `[]` |
| `applicationFields` | Application form requirements | `[]` |
| `organizationDescription` | Employer description in HTML | `<p>...</p>` |
| `organizationCoverImage` | Employer cover image URL | `null` |
| `organizationRevenue` | Published revenue | `null` |
| `organizationTurnover` | Published turnover | `null` |
| `organizationGdprSetting` | Application privacy settings | `null` |
| `organizationHasExternalAts` | External application system flag | `true` |
| `organizationAutomaticEmail` | Automatic email flag | `true` |
| `organizationWebsite` | Employer website URL | `https://...` |
| `organizationLinkedin` | Published social profile | `[profile]` |
| `organizationTwitter` | Published social profile | `null` |
| `organizationFacebook` | Published social profile | `null` |
| `organizationInstagram` | Published social profile | `null` |
| `organizationAverageAge` | Published workforce average age | `null` |
| `organizationParityMen` | Published share of men | `null` |
| `organizationParityWomen` | Published share of women | `null` |
| `organizationHeadquarter` | Headquarters address object | `{"city":"San Francisco"}` |

The office array preserves every location in the source record. Use it when a job is open in several cities: `city` and `countryCode` represent only the primary office. Salary values are the numbers published with that job, so compare them only after checking `salaryCurrency` and `salaryPeriod`. When the source short summary is empty, the actor derives one from the first 500 characters of the published description.

### How to scrape Welcome to the Jungle

1. Enter role words in `query`, such as `software engineer`, or leave it blank to discover general listings.
2. Add a city or country and any contract, remote, experience, salary, or occupation filters that matter to the search.
3. Set `maxItems` to bound the number of saved jobs. Keep `includeDetails` on if descriptions, application destinations, and employer context are needed.
4. Run the actor and inspect the first few dataset items. Export JSON for nested office and application fields, or CSV for a flat report.

Jobs are saved as they are found. If a run stops before completing its candidate list, items already written to the dataset remain available. For a recurring workflow, save the input alongside each export and compare records by `id` or `url`.

### Input

| Name | Type | Default | Description |
|---|---|---|---|
| `maxItems` | integer | `100` | Stop after this many matching jobs; `0` has no result cap. |
| `includeDetails` | boolean | `true` | Add descriptions, application fields, and extended employer data. |
| `websiteCountry` | string | `en` | Select language for localized category and skill labels. |
| `query` | string | empty | Match role words in the job URL and page content. |
| `searchOnlyInJobTitle` | boolean | `false` | Restrict the final keyword check to the title. |
| `location` | string | empty | Match city, region, or address across published offices. |
| `countryCode` | string | empty | Match an office's two-letter country code. |
| `sortBy` | string | `relevance` | Use sitemap order or recently modified candidate pages. |
| `datePosted` | string | `any` | Keep jobs posted in the last 1, 3, 7, 14, or 30 days. |
| `contractType` | array | empty | Keep any selected contract type. |
| `remote` | array | empty | Keep any selected remote policy. |
| `experienceLevel` | string | empty | Minimum experience based on the published band's lower bound. |
| `salaryMin` | integer | empty | Keep jobs whose published maximum pay reaches this number. |
| `companySize` | string | empty | Filter by published employee-count band. |
| `language` | string | empty | Keep jobs written in the supplied language code. |
| `sector` | array | empty | Match keywords against published employer sector labels. |
| `occupation` | array | empty | Match occupation category references. |
| `proxyConfiguration` | object | enabled | Connection settings for public pages. |

A realistic input for an initial export:

```json
{
  "query": "software engineer",
  "countryCode": "US",
  "remote": ["partial", "fulltime"],
  "includeDetails": true,
  "maxItems": 50
}
```

This example is narrower than a title-only search because it checks both office country and remote policy. Start without optional filters if you are exploring which terms appear in the source. Add one filter at a time when a narrow combination returns too few results. For a job title that appears in several languages, a local-language `query` can expose listings that an English phrase does not.

### Output example

The following values come from one job in the successful 30-item validation run. The employer name, employer links, and long HTML text are redacted here; the dataset retains the complete public values.

```json
{
  "id": "fff60753-080e-430d-b9c1-e40217c4fbd6",
  "reference": null,
  "name": "Software Engineer (Senior)",
  "url": "[source job URL]",
  "contractType": "full_time",
  "remote": "unknown",
  "language": "en",
  "salaryMin": 195000,
  "salaryMax": 250000,
  "salaryCurrency": "USD",
  "salaryPeriod": "yearly",
  "experienceLevel": "5_TO_7_YEARS",
  "publishedAt": "2026-08-29T00:05:33Z",
  "category": "Tech & Engineering",
  "subcategory": "Software & Web Development",
  "city": "New York",
  "countryCode": "US",
  "organizationName": "[redacted]",
  "organizationEmployees": 165,
  "organizationJobsCount": 33,
  "description": "[HTML description redacted]",
  "applyUrl": "[application URL redacted]",
  "skills": ["Communication skills", "Collaboration and teamwork"],
  "organizationHasExternalAts": true
}
```

The validation run returned 30 of 30 requested jobs. Every record had an ID, title, source URL, employer name, publication date, description, and country code. In that sample, published pay bounds appeared in about 73% of records, a primary city in about 77%, and the source short summary was empty throughout, so the actor derived it from the description. Those percentages describe the sample, not a promise for another role or country.

### Use cases

- Recruitment analysts can compare published salary ranges and remote policies across roles without opening each listing individually.
- Job seekers can build a focused list of openings and keep the direct application link with the original job page.
- Labor-market researchers can track the mix of contract types, office countries, and occupations in repeated snapshots.
- Employer researchers can connect job demand with published employee counts, sectors, headquarters, and job totals.
- Operations teams can monitor a recurring keyword search and reconcile new or removed IDs between exports.
- Regional researchers can inspect multilingual listings while retaining each job's original language and salary currency.

The data is a snapshot of public pages at run time. A job can be edited, archived, or removed after collection. Keep the publication and update timestamps when comparing snapshots; use the source URL to verify details that affect a decision.

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

The current result price is **$0.30 per 1,000 saved jobs**, with platform usage included in the listed result price. At that rate, 100 jobs cost **$0.03**, 500 cost **$0.15**, and 1,000 cost **$0.30**. These are result-charge examples, not guarantees that the source exposes a requested number of matching jobs. A smaller `maxItems` makes a first run easier to inspect and limits the number of job pages fetched.

Job pages require residential proxy traffic in the tested environment. The actor obtains one page per candidate to evaluate filters, even when `includeDetails` is false. Narrow filters can require more page requests than the final result count suggests. If a large export is planned, first run a small sample with the same filters to estimate speed and coverage. The pay-per-result price can change; check the current run price before scheduling a large export.

### Tips for best results

Use role words that occur in public job URLs. The actor first checks the URL slug for every word in `query`, then checks the fetched page content; a role described only inside body text will not be discovered by that search. For more coverage, try a shorter query such as `engineer` and filter the resulting dataset downstream. A blank query considers all sitemap entries, but broad searches can take much longer because every candidate page must be checked.

Location checks all published office objects, so a multi-office job can match a city other than its primary `city` value. `countryCode` offers an exact country test. The location field matches text and does not apply a geographic radius. If a city name is ambiguous, add the country code. `websiteCountry` changes localized labels where available; it does not restrict jobs to a country. Use `language` for a job-language filter.

Contract and remote arrays use an any-of match within each field. Across different fields, all supplied conditions must hold. Published remote values include `no`, `partial`, `punctual`, `fulltime`, and `unknown`. Salary is checked against the listing's published maximum, with no currency conversion. For pay comparisons across countries, first filter or group by `salaryCurrency` and `salaryPeriod` in the exported data.

Use `sortBy: "date"` when recently modified candidate pages are preferred. That option reads all listed sitemap partitions before fetching jobs and sorts by sitemap modification time. Modification time can differ from `publishedAt`; `datePosted` checks the actual publication timestamp after the page is fetched. For routine monitoring, keep the query and filters stable between runs, then compare IDs and update timestamps.

### Limits

The public search endpoint returned 403 in probing, including a residential session. This actor uses public sitemap entries and page-embedded job data instead. The site supplied nine job sitemap partitions in the tested index, each with up to 10,000 URLs; that is the discoverable pool at run time, not a complete historical archive. Jobs absent from those sitemaps cannot be found by this actor. Sitemap URLs and page content can change independently, so a listed job may later be unavailable or archived.

The URL prefilter requires every query word to appear in a job slug. This keeps searches practical, but it can miss a relevant listing whose title or description contains a keyword that its URL omits. The `relevance` option follows sitemap order; it is not ranked search relevance. The `date` option uses sitemap modification time for candidate order, then reads the publication date for the record. Neither order guarantees all matching records are returned before `maxItems` is reached.

Detail pages were blocked on datacenter traffic during research and worked on residential traffic. Page availability may vary by region, session, or time. The actor skips a page when its structured job data is unavailable and continues with the next candidate. It does not infer a missing salary, benefit, social profile, or application field. Empty arrays and nulls should be treated as unpublished or unavailable values, not as negative answers.

`includeDetails: false` removes the extended columns from output but still fetches the job page to apply filters and create the core record. It should be selected for smaller exports, not as a speed or network-cost setting. Large broad searches, narrow filters, and runs with `maxItems: 0` can be slow because many candidate pages need to be read.

### Use with automated workflows

The actor accepts the same JSON input from a scheduled run or an integration. Give the workflow a bounded result limit and specify the fields needed for the next step. Two example requests are: “Collect up to 50 software engineer jobs in the US and group them by remote policy and salary currency,” and “Find recently posted design jobs in Paris and return source links, publication dates, and direct application destinations.” Review source links before acting on individual jobs.

For recurring monitoring, save the exact input with each dataset and record when it ran. Compare `id` values across snapshots to find newly observed jobs; compare `updatedAt` to spot edited records. A missing job in one short, capped run does not establish that it was removed from the site, because candidate order and reachability can vary.

### FAQ

**Is it legal to collect these listings?** The actor reads public job pages. Follow the source site's terms and applicable data-protection rules, and do not misuse personal data. Verify reuse rights before republishing job descriptions or employer media.

**Do I need to configure proxies?** No extra input is required for the tested setup. The actor uses a datacenter connection for public sitemaps and residential traffic for job pages. The source may change its blocking behavior, so a previously working connection is not a guarantee for future runs.

**How fast is a run?** Speed depends on the number of candidate pages, filters, and source response times. The 30-item validation run completed successfully, but a country or salary filter may require many more page reads to produce the same number of saved jobs. Test a small, representative input before increasing `maxItems`.

**Can I schedule and monitor collection?** Yes. Keep a fixed input and schedule repeated runs. Monitor each run's status and resulting dataset, then compare IDs and timestamps. A failed or partial run should not be used as evidence that a job disappeared.

**Can I export to a spreadsheet?** Yes. CSV and spreadsheet exports work for the scalar columns. JSON preserves nested offices, headquarters, benefits, and application fields. When importing CSV, keep IDs as text so they are not reformatted.

**What does an empty field mean?** The public page did not provide that value in the structured record. For example, many employers omit salary or benefits. The actor does not guess, convert currencies, or extract hidden application questions.

**Does the location setting support distance search?** No. It checks published office text; it does not calculate a radius. Enter a city with a country code to narrow an ambiguous location, or examine the office array after export.

### Integrations

Start a run through the platform API, read its default dataset, and send completed results to a database, spreadsheet, or internal report. Webhooks can notify a workflow when a run finishes, and schedules can produce regular snapshots. Store `id`, `url`, `publishedAt`, and `updatedAt` with downstream records to reconcile later exports. Use JSON when the downstream system needs all office locations or nested application data.

### Support

Open an issue on the Issues tab; we reply within 24h and add fields on request.

# Actor input Schema

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

Stop after this many jobs. Set 0 for all discoverable matches.

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

Include job description, application and extended employer fields. Pages are fetched in either mode.

## `websiteCountry` (type: `string`):

Language for localized category and skill labels when available.

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

Words to match in the job URL and page content. All words must be in the URL slug during discovery.

## `searchOnlyInJobTitle` (type: `boolean`):

Match the keywords against job titles after discovery.

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

City, region or address text matched against listed offices. Leave blank or use Worldwide for any location.

## `countryCode` (type: `string`):

Two-letter country code matched against job offices, such as FR or US.

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

Use sitemap order or sort candidate URLs by their last modified time.

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

Keep jobs posted in the last selected number of days.

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

Keep jobs with any selected contract type.

## `remote` (type: `array`):

Keep jobs with any selected remote work policy.

## `experienceLevel` (type: `string`):

Minimum years of experience based on the lower bound of the published experience band.

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

Keep jobs whose published maximum salary reaches this amount. Currency is not converted.

## `companySize` (type: `string`):

Keep employers in this employee-count range.

## `language` (type: `string`):

Keep jobs written in this language code, such as en or fr.

## `sector` (type: `array`):

Keep jobs whose employer sector labels contain any keyword.

## `occupation` (type: `array`):

Keep jobs whose occupation category reference matches one of these values.

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

Proxy connection used for sitemap and job pages.

## Actor input object example

```json
{
  "maxItems": 100,
  "includeDetails": true,
  "websiteCountry": "en",
  "query": "software engineer",
  "searchOnlyInJobTitle": false,
  "location": "Worldwide",
  "sortBy": "relevance",
  "datePosted": "any",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `jobs` (type: `string`):

Extracted jobs in the default dataset.

# 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": "software engineer",
    "location": "Worldwide"
};

// Run the Actor and wait for it to finish
const run = await client.actor("mlg14/welcome-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": "software engineer",
    "location": "Worldwide",
}

# Run the Actor and wait for it to finish
run = client.actor("mlg14/welcome-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": "software engineer",
  "location": "Worldwide"
}' |
apify call mlg14/welcome-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,mlg14/welcome-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/1hIwZ92jki7qSqhyj/builds/YOiIWPywTRqTC9PSt/openapi.json
