# StepStone Group Jobs API \[$1.5💰] — Totaljobs, CWJobs +12 (`memo23/stepstone-group-scraper`) Actor

One run, the whole StepStone Group: Totaljobs, CWJobs, Jobsite, Caterer, IrishJobs, Milkround, PNet + StepStone DE/AT/BE/NL. Unified JSON with a per-brand column — title, salary, employer, location, apply URL. Pure HTTP API, no browser. From $1.50/1,000 jobs.

- **URL**: https://apify.com/memo23/stepstone-group-scraper.md
- **Developed by:** [Muhamed Didovic](https://apify.com/memo23) (community)
- **Categories:** Jobs, Automation, Lead generation
- **Features:** Limited permissions
- **Stats:** 45 total users, 10 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $1.50 / 1,000 job scrapeds

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

## StepStone Group Jobs Scraper — Totaljobs, CWJobs, Caterer, IrishJobs, StepStone DE/AT/BE/NL, PNet

Scrape job postings across the **entire StepStone Group in a single run** — Totaljobs, CWJobs, Jobsite and Caterer in the UK, IrishJobs in Ireland, StepStone Germany, Austria, Belgium and the Netherlands, and PNet in South Africa — and get one clean, unified dataset back.

Pick the portals you want, type a keyword and a location, and every selected brand is searched at once. No juggling four different scrapers, four output shapes, or four billing lines.

![StepStone Group Jobs Scraper](https://muhamed-didovic.github.io/assets/stepstone-group-hero.png)

***

### Why Use This Scraper?

- **One run, the whole Group.** Fifteen portals across the UK, Ireland, Germany, Austria, Belgium, the Netherlands and South Africa — searched together, returned in one dataset.
- **One unified schema.** Every row has the same fields regardless of which brand it came from, plus a `brand` and `sourceSite` column so you always know the source.
- **Keyword + location, no URL building.** Multi-select the brands, type "chef" and "London", and go. Or paste raw brand URLs if you prefer.
- **Structured salary bands.** Min / max / currency / period parsed out wherever the employer disclosed a figure — including correct handling of continental European number formats.
- **Employer firmographics.** Company name, logo, internal company page and (where available) a company ID on every row.
- **Geo-coordinates.** Latitude/longitude on UK rows for mapping and territory analysis.
- **Built for scale.** Efficient JSON APIs under the hood — not fragile HTML scraping — with automatic pagination and per-brand result caps.
- **Change tracking across runs.** Turn on `incrementalMode` and every row is labelled `NEW`, `UPDATED`, `REAPPEARED` (or swept as `EXPIRED`) with a `firstSeenAt` stamp. Unchanged jobs aren't re-emitted — so a scheduled run returns only what actually moved, and you're not charged for repeats.
- **Alerts to your own channels.** Telegram, Slack, Discord, WhatsApp, or any webhook — an end-of-run summary with the top new jobs across every brand. Silent when nothing changed.
- **Filters that run before billing.** `includeKeywords` / `excludeKeywords` (matched against title, employer and description), date windows, and `maxAgeMinutes`. A filtered-out job is never pushed, so it never costs a row.
- **AI-agent friendly output.** `compact` core-field rows, `descriptionMaxLength` truncation, and `excludeEmptyFields` for lean MCP/LLM payloads.

***

### Overview

The StepStone Group Jobs Scraper is for recruiters, sourcers, market-intelligence teams, and data teams who need job-market data spanning multiple StepStone Group brands at once.

It returns a flat, one-row-per-job dataset. Each row is a single job posting normalized to a shared **JobPosting** shape: identity (job ID, URL, title), dates, employer, location, structured salary, employment type and apply flow. A `brand` column tags every row with its source portal (`totaljobs`, `caterer`, `irishjobs`, `stepstone-de`, …), so you can filter, group, or split by brand downstream.

Data comes from two shared StepStone Group backends:

- The **Totaljobs-Group** brands (Totaljobs, CWJobs, Jobsite, Caterer) share one mobile API.
- The **StepStone unified platform** brands (IrishJobs, StepStone DE/AT/BE/NL, PNet, Milkround, RetailChoice, CatererGlobal, CareerStructure, CityJobs) share the unified-resultlist API.

You don't need to know or care which is which — the actor routes each selected brand automatically.

***

### Supported Inputs

You can drive the actor two ways — use either, or mix both in one run.

#### 1. Brands + keyword + location (recommended)

Select one or more portals in the **StepStone Group portals** multi-select, then set a keyword and/or location. Every selected brand is searched.

Supported brands:

| Brand id | Portal | Market |
|---|---|---|
| `totaljobs` | Totaljobs | UK |
| `cwjobs` | CWJobs | UK (tech) |
| `jobsite` | Jobsite | UK |
| `caterer` | Caterer.com | UK (hospitality) |
| `irishjobs` | IrishJobs | Ireland |
| `stepstone-de` | StepStone | Germany |
| `stepstone-at` | StepStone | Austria |
| `stepstone-be` | StepStone | Belgium |
| `stepstone-nl` | StepStone | Netherlands |
| `pnet` | PNet | South Africa |
| `milkround` | Milkround | UK (graduate) |
| `retailchoice` | RetailChoice | UK (retail) |
| `catererglobal` | CatererGlobal | International (hospitality) |
| `careerstructure` | CareerStructure | UK (construction/eng) |
| `cityjobs` | CityJobs | UK (professional) |

#### 2. Start URLs

Paste raw listing or job-detail URLs from any supported brand — they are auto-routed to the correct backend by host. Mixing brands in one list is fine.

```json
{
  "startUrls": [
    "https://www.totaljobs.com/jobs/software-engineer/in-london",
    "https://www.caterer.com/jobs/chef",
    "https://www.irishjobs.ie/jobs/nurse/in-dublin"
  ]
}
```

Listing URLs auto-paginate until your `maxItems` budget is reached. Direct job-detail URLs emit one row each.

#### Not supported

- Sites outside the StepStone Group (any host not in the table above is skipped with a log note).
- Pasted **StepStone DE/AT/BE/NL job-detail** URLs (their detail URL format differs) — use the brand + keyword search for those markets instead. UK and Irish detail URLs are fully supported.
- Login-gated content, candidate CVs, or recruiter-only data.

***

### Use Cases

| Audience | What they get |
|---|---|
| Recruitment agencies | Cross-brand vacancy feeds for one keyword across UK/IE/DACH/BENELUX in a single pull |
| Talent sourcing | Employer + role lists filtered by location and posted date |
| Market intelligence | Volume, salary bands and hiring-company trends per brand and per country |
| Job boards / aggregators | A normalized multi-source jobs feed under one schema |
| Salary benchmarking | Structured min/max/currency/period bands grouped by title and market |
| Lead generation | Hiring companies (with logos and internal profiles) actively posting roles |

***

### How It Works

![How the StepStone Group Jobs Scraper works](https://muhamed-didovic.github.io/assets/stepstone-group-how-it-works.png)

1. **You pick brands + a keyword/location** (or paste brand URLs). The actor builds the right search URL for each selected portal.
2. **Each brand is routed to its shared backend** — the Totaljobs-Group mobile API or the StepStone unified-resultlist API — automatically, by host.
3. **Listings are paginated via JSON APIs**, collecting job items up to your per-brand `maxItems` cap.
4. **Each job's detail page is fetched and parsed** into the unified JobPosting row, merging the listing item's structured salary, coordinates and skills.
5. **Rows are streamed to the dataset** as they're ready, each tagged with its `brand` and `sourceSite`.

***

### Input Configuration

| Field | Type | Description |
|---|---|---|
| `brands` | array | StepStone Group portals to scrape (multi-select). Each selected brand is searched with the keyword & location below. |
| `searchKeyword` | string | Job title / keyword to search across every selected brand (e.g. `software engineer`, `nurse`, `chef`). Optional. |
| `searchLocation` | string | Location to search across every selected brand (e.g. `London`, `Dublin`, `Berlin`). Optional — blank searches the whole country per brand. |
| `startUrls` | array | Raw listing or job-detail URLs from any supported brand. Auto-routed by host. Optional. |
| `maxItems` | integer | Max job rows per (brand × listing URL). Default 100. Direct detail URLs always emit 1 row each. |
| `maxConcurrency` | integer | Parallel HTTP requests for detail fetches. Default 4. |
| `maxRequestRetries` | integer | Per-URL retry budget on transient errors. Default 6. |
| `proxy` | object | Advanced override. By default each brand uses its proven proxy automatically. |

#### Filters (applied before you're charged)

| Field | Type | Description |
|---|---|---|
| `includeKeywords` | array | Keep only jobs matching at least one keyword (searched across title, employer name and description). |
| `excludeKeywords` | array | Drop jobs matching any of these. |
| `fromDate` / `toDate` | string | Posted-date window, `YYYY-MM-DD` or ISO-8601. |
| `maxAgeMinutes` | integer | Only jobs posted in the last N minutes — pair with an hourly schedule for a near-real-time feed. |

#### Change tracking (for scheduled runs)

| Field | Type | Description |
|---|---|---|
| `incrementalMode` | boolean | Track jobs between runs. Rows gain `changeType` (`NEW` / `UPDATED` / `REAPPEARED`) and `firstSeenAt`; unchanged jobs are not re-emitted. |
| `stateKey` | string | Names the tracked universe. Leave blank to auto-derive one from your brand selection, keyword, location and URLs — so each saved task tracks separately. |
| `emitUnchanged` | boolean | Also emit jobs that haven't changed (default off). |
| `emitExpired` | boolean | Emit a marker row for jobs that disappeared from the source. |
| `skipReposts` | boolean | Suppress jobs that vanished and came back with identical content. |
| `resetIncrementalState` | boolean | Wipe the baseline and start fresh on this run. |

**Note on brand selection:** the auto-derived `stateKey` includes your `brands` list, so changing which portals you select starts a separate baseline instead of reporting the de-selected brands' jobs as expired.

#### Notifications

| Field | Type | Description |
|---|---|---|
| `telegramBotToken` + `telegramChatId` | string | Your own bot (from @BotFather) and target chat. |
| `slackWebhookUrl` / `discordWebhookUrl` | string | Incoming webhooks. |
| `whatsappAccessToken` + `whatsappPhoneNumberId` + `whatsappTo` | string | Meta Cloud API credentials. |
| `webhookUrl` (+ `webhookHeaders`) | string | Any HTTPS endpoint — receives `{ metadata, items }` for n8n / Make / Zapier / your backend. |
| `notificationLimit` | integer | Jobs listed per message. Default 5. |
| `notifyOnlyChanges` | boolean | Only count NEW / UPDATED / REAPPEARED toward notifications. |

All notification credentials are yours and are stored as secret input fields. A failing channel only logs a warning — it never fails a run that already produced data.

#### Example: three brands, one keyword

```json
{
  "brands": ["totaljobs", "caterer", "irishjobs"],
  "searchKeyword": "chef",
  "searchLocation": "London",
  "maxItems": 200
}
```

#### Example: continental StepStone markets

```json
{
  "brands": ["stepstone-de", "stepstone-at", "stepstone-be"],
  "searchKeyword": "engineer",
  "maxItems": 100
}
```

#### Example: paste URLs directly

```json
{
  "startUrls": [
    "https://www.totaljobs.com/jobs/in-manchester",
    "https://www.cwjobs.co.uk/jobs/devops"
  ],
  "maxItems": 50
}
```

***

### Output Overview

The dataset is flat: **one row per job posting**. Each row is normalized to the same JobPosting shape no matter which brand produced it. Provenance columns (`brand`, `backend`, `sourceSite`, `sourceSearchUrl`) let you trace every row back to its portal and the search that found it. Optional fields (structured salary, skills, coordinates, company ID) are populated when the source exposes them and are otherwise `null`.

***

### Output Samples

**From `brands: ["caterer"]`, keyword `chef` (Totaljobs-Group backend):**

```json
{
  "rowType": "job",
  "brand": "caterer",
  "backend": "naps",
  "sourceSite": "caterer.com",
  "sourceSearchUrl": "https://www.caterer.com/jobs/chef",
  "jobId": "107604788",
  "jobUrl": "https://www.caterer.com/job/chef/abbeywood-estate-job107604788",
  "title": "Chef",
  "employmentType": "FULL_TIME",
  "employer": {
    "name": "Abbeywood Estate",
    "url": "https://www.caterer.com/jobs/abbeywood-estate?cmpId=1460325",
    "logoUrl": "https://www.caterer.com/CompanyLogos/1460325.png",
    "id": 1460325
  },
  "location": {
    "text": "Northwich, Cheshire, CW8 2HW, GB",
    "locality": "Northwich",
    "region": "Cheshire",
    "postalCode": "CW8 2HW",
    "country": "GB",
    "lat": 53.22709,
    "lng": -2.60343
  },
  "salary": { "rawText": "£17 per hour", "min": 17, "max": null, "currency": "GBP", "period": "hour" },
  "applyType": "internal",
  "scrapedAt": "2026-07-13T16:00:00.000Z"
}
```

**From `brands: ["stepstone-at"]`, keyword `manager` (StepStone unified backend):**

```json
{
  "rowType": "job",
  "brand": "stepstone-at",
  "backend": "unified",
  "sourceSite": "stepstone.at",
  "jobId": "992653",
  "title": "Area Sales Manager (m/w/d) Nutzfahrzeuge",
  "employer": { "name": "WALTER LEASING GmbH", "id": null, "logoUrl": "https://..." },
  "location": { "text": "Wiener Neudorf, AT", "country": "AT", "lat": null, "lng": null },
  "salary": { "rawText": "€ 50.000", "min": 50000, "max": null, "currency": "EUR", "period": null },
  "applyType": "internal"
}
```

***

### Key Output Fields

**Provenance**

- `brand` — source portal id (`totaljobs`, `caterer`, `irishjobs`, `stepstone-de`, …)
- `backend` — which shared backend served it (`naps` or `unified`)
- `sourceSite` — host the job lives on
- `sourceSearchUrl` — the listing/search that discovered the row (null for pasted detail URLs)

**Job identity**

- `jobId`, `jobUrl`, `title`

**Job details**

- `description`, `datePosted`, `validThrough`, `employmentType`, `industry`, `directApply`, `jobLocationType`, `applicantLocationRequirements`, `workType`, `contractType`, `workFromHome`, `skills`

**Employer**

- `employer.name`, `employer.url`, `employer.logoUrl`, `employer.id`

**Location**

- `location.text`, `location.locality`, `location.region`, `location.postalCode`, `location.country`, `location.lat`, `location.lng`

**Salary**

- `salary.rawText`, `salary.min`, `salary.max`, `salary.currency`, `salary.period`

**Apply flow**

- `applyUrl`, `applyType` (`internal` / `external` / `unknown`)

***

### FAQ

**Which brands can I scrape in one run?** Any combination of the nine listed: Totaljobs, CWJobs, Jobsite, Caterer, IrishJobs, and StepStone DE/AT/BE/NL. Select as many as you want.

**How do I get only new jobs across the whole Group?** Turn on `incrementalMode` and schedule the run. The first run seeds a baseline (everything comes back as `NEW`); after that, each run emits only jobs that are new, changed, or reappeared — unchanged ones are skipped, so they cost nothing. Add a notification channel and you'll be pinged only when something actually moved. Verified on the platform: a repeat run over unchanged source emits zero rows.

**If I change which brands I've selected, will the old ones show as expired?** No. The auto-derived `stateKey` includes your brand selection, so each combination keeps its own baseline. Set `stateKey` yourself if you'd rather control the grouping — for example to keep one baseline while you vary the keyword.

**Do all brands return the same fields?** Yes — every row uses one unified schema. Some optional fields (structured company ID, skills, coordinates) are only present where a given brand exposes them; they're `null` otherwise.

**Does StepStone NL return many jobs?** The Netherlands portal has a small inventory (StepStone has wound it down), so expect low volumes there. It's included for completeness; the DE, AT and UK/IE portals carry the bulk of the roles.

**Can I paste a StepStone Germany job link?** For DE/AT/BE/NL, use the brand + keyword search rather than pasting individual job-detail URLs — those markets use a different detail-URL format. UK and Irish detail URLs work directly.

**How is salary handled?** Where the employer disclosed a figure, it's parsed into min/max/currency/period. Continental European "€ 50.000" formats are read correctly (50000, not 50). Undisclosed roles keep the raw text (e.g. "€ Not Disclosed") with null min/max.

**Does it scrape candidate data or recruiter-only content?** No — only publicly listed job postings.

**How does pagination and the item cap work?** `maxItems` is applied per brand per listing URL. Three brands with `maxItems: 100` yields up to 300 rows (100 each). Direct detail URLs always emit exactly one row.

***

### Support

Found a bug, a missing field, or a brand you'd like added? Open an issue on the actor's **Issues** tab and we'll take a look. We actively maintain this scraper and respond to reports.

***

### Additional Services

Need a tailored export, a scheduled cross-brand feed, additional StepStone Group portals, or enrichment (e.g. employer contact emails)? We build custom scraping and data pipelines — reach out via the Issues tab to discuss your requirements.

***

### Explore More Scrapers

Prefer a single-country scraper, or need deeper per-brand coverage? Explore our dedicated job-board actors:

- Totaljobs UK Scraper
- CWJobs Scraper
- Jobsite Scraper
- Caterer.com Scraper
- IrishJobs Scraper
- StepStone Scraper

Browse the full collection on our Apify Store profile.

***

### 🤖 For AI Agents & LLM Apps

Compact reference for AI agents calling this actor via the [Apify MCP server](https://mcp.apify.com) or the Apify API (actor: `memo23/stepstone-group-scraper`).

**Purpose:** Scrape job postings across the whole StepStone Group (Totaljobs, CWJobs, Jobsite, Caterer, IrishJobs, StepStone DE/AT/BE/NL, PNet and more) into one unified JobPosting schema in a single run.

**Minimal input:**

```json
{ "brands": ["caterer"], "searchKeyword": "chef", "searchLocation": "London", "maxItems": 20 }
```

Alternatively pass `startUrls` with raw listing or job-detail URLs from any supported brand instead of `brands` + keyword; the two modes can be mixed in one run.

**Output:** one dataset row per job — `rowType`, `brand`, `backend`, `sourceSite`, `sourceSearchUrl`, `jobId`, `jobUrl`, `title`, `employmentType`, `employer {name, url, logoUrl, id}`, `location {text, locality, region, postalCode, country, lat, lng}`, `salary {rawText, min, max, currency, period}`, `applyUrl`, `applyType`, `scrapedAt`.

**Behaviors an agent should know:**

- Always set `maxItems` — it caps rows per (brand × listing URL), so N brands at `maxItems: 100` can yield up to N×100 rows. Direct detail URLs emit exactly one row each.
- `startUrls` are auto-routed to the correct backend by host; hosts outside the Group are skipped. StepStone DE/AT/BE/NL detail URLs are not supported — use brand + keyword search for those markets.
- `incrementalMode` labels rows `NEW` / `UPDATED` / `REAPPEARED` and skips unchanged jobs across runs; `stateKey` names the tracked baseline.
- `includeKeywords` / `excludeKeywords`, `fromDate` / `toDate` and `maxAgeMinutes` filter before rows are pushed. `compact`, `excludeEmptyFields` and `descriptionMaxLength` trim payloads for LLM use.
- Billed per job row pushed to the dataset; filtered-out and unchanged rows are never pushed, so they are not charged. See the Pricing tab for the rate.
- Public job-posting data only — no candidate CVs, recruiter-only data, or login-gated content.

***

### ⚠️ Disclaimer

This actor is an independent tool and is **not affiliated with, endorsed by, or sponsored by** The Stepstone Group, Totaljobs Group Ltd, or any of their subsidiaries or brands (Totaljobs, CWJobs, Jobsite, Caterer.com, IrishJobs, StepStone). All product names, logos, and brands are the property of their respective owners and are used for identification purposes only.

This scraper collects **only publicly available job-posting data** — the same information any visitor can see without logging in. It does not access private, gated, or personal candidate data. Users are responsible for ensuring their use of the scraped data complies with the source sites' Terms of Service, applicable data-protection laws (including GDPR and CCPA), and any other relevant regulations. Use the data lawfully and responsibly.

***

### SEO Keywords

StepStone scraper, StepStone Group scraper, Totaljobs scraper, CWJobs scraper, Jobsite scraper, Caterer.com scraper, IrishJobs scraper, StepStone Germany scraper, job posting scraper, job board scraper, jobs data extraction, recruitment data scraper, vacancy scraper, salary data scraper, hiring companies scraper, UK jobs scraper, Ireland jobs scraper, DACH jobs scraper, employer data extraction, job market intelligence, multi-brand jobs scraper, StepStone Austria scraper, StepStone Belgium scraper, StepStone Netherlands scraper

# Actor input Schema

## `brands` (type: `array`):

Which Group portals to include. Each selected brand is searched with the keyword & location below. Leave empty if you're only pasting Start URLs.

## `searchKeyword` (type: `string`):

Job title / keyword to search across every selected brand, e.g. `software engineer`, `nurse`, `chef`. Leave blank to browse everything in the location.

## `searchLocation` (type: `string`):

Town / city / region to search across every selected brand, e.g. `London`, `Dublin`, `Berlin`. Leave blank to search the whole country for each brand.

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

Raw listing URLs (e.g. `https://www.totaljobs.com/jobs/in-london`) or direct job-detail URLs from any supported brand. Auto-routed to the right backend by host; mixed brands in one run are fine. Overrides the keyword/location search for brands they belong to.

## `includeKeywords` (type: `array`):

Only keep jobs whose title, company or description contains at least one of these (case-insensitive). Filtered rows are never charged.

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

Drop jobs whose title, company or description contains any of these (case-insensitive).

## `fromDate` (type: `string`):

Only keep jobs posted on or after this date (ISO 8601, e.g. 2026-08-01).

## `toDate` (type: `string`):

Only keep jobs posted on or before this date (ISO 8601). A date-only value means through the end of that day.

## `maxAgeMinutes` (type: `integer`):

Only keep jobs posted within the last N minutes. Empty or 0 = no age limit. Ideal for tight scheduled runs.

## `incrementalMode` (type: `boolean`):

Compare against the previous run and only emit jobs that are NEW, UPDATED, or REAPPEARED — you are only charged for what changed. The first run seeds the baseline. Each emitted row carries `changeType` and `firstSeenAt`.

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

Optional stable name for this tracked search (e.g. "nurse-toronto"). Leave empty to auto-derive one from the search inputs — same inputs, same baseline.

## `emitUnchanged` (type: `boolean`):

When incremental, also emit rows whose content did not change (charged as normal results).

## `emitExpired` (type: `boolean`):

When incremental, emit a small `changeType: EXPIRED` row for every previously-tracked job that vanished from the source.

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

When incremental, skip jobs whose content matches a previously-expired posting (employer re-listed the same ad under a new id).

## `resetIncrementalState` (type: `boolean`):

One-shot: drop the stored baseline for this state key at the start of the run.

## `compact` (type: `boolean`):

Emit only the core fields — small payloads for AI-agent / MCP / LLM pipelines.

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

Drop null / empty-string / empty-array fields from each record.

## `descriptionMaxLength` (type: `integer`):

Truncate description fields to this many characters (0 = full text). Controls LLM prompt cost and dataset size.

## `telegramBotToken` (type: `string`):

Your bot's token from @BotFather. With Chat ID set, a summary of new jobs is sent after each run.

## `telegramChatId` (type: `string`):

Chat or channel id the bot posts to (e.g. "-1001234567890").

## `slackWebhookUrl` (type: `string`):

Slack incoming-webhook URL (api.slack.com/messaging/webhooks).

## `discordWebhookUrl` (type: `string`):

Discord incoming-webhook URL (Server Settings → Integrations → Webhooks).

## `whatsappAccessToken` (type: `string`):

WhatsApp Cloud API token (Meta Business system user). The recipient must have messaged your business number within the last 24h — Cloud API rule.

## `whatsappPhoneNumberId` (type: `string`):

Your WhatsApp Business phone-number id (numeric, from the Meta dashboard).

## `whatsappTo` (type: `string`):

Recipient phone in E.164 without + (e.g. "14165551234").

## `webhookUrl` (type: `string`):

Receives one JSON POST with { metadata, items } after the run — the universal escape hatch for n8n / Make / Zapier / custom backends.

## `webhookHeaders` (type: `object`):

Optional custom headers for the generic webhook (e.g. {"Authorization": "Bearer …"}).

## `notificationLimit` (type: `integer`):

How many job lines each chat notification includes (1–20). The rest are summarised as a count.

## `notifyOnlyChanges` (type: `boolean`):

With Incremental mode on, notify only about NEW / UPDATED / REAPPEARED jobs.

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

Maximum job rows per (brand × listing URL). 3 brands × maxItems 100 → up to 300 rows. Direct detail URLs always emit 1 row each. Free-tier users are capped at 100 rows total across the run.

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

Parallel HTTP requests for detail-page fetches. 3-5 is the sweet spot.

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

Per-URL retry budget on network errors, proxy failures, and transient blocks. Each retry rotates the proxy session. Default 6.

## `proxy` (type: `object`):

Optional override. By default each brand uses its proven proxy: Apify Residential GB for the Totaljobs-Group brands (their detail pages are Akamai-locked) and residential rotation for the StepStone unified-platform brands. Only set this if you know what you're doing.

## Actor input object example

```json
{
  "brands": [
    "totaljobs",
    "caterer",
    "irishjobs"
  ],
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "skipReposts": false,
  "resetIncrementalState": false,
  "compact": false,
  "excludeEmptyFields": false,
  "notificationLimit": 5,
  "notifyOnlyChanges": false,
  "maxItems": 100,
  "maxConcurrency": 4,
  "maxRequestRetries": 6
}
```

# 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 = {
    "brands": [
        "totaljobs",
        "caterer",
        "irishjobs"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("memo23/stepstone-group-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 = { "brands": [
        "totaljobs",
        "caterer",
        "irishjobs",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("memo23/stepstone-group-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 '{
  "brands": [
    "totaljobs",
    "caterer",
    "irishjobs"
  ]
}' |
apify call memo23/stepstone-group-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,memo23/stepstone-group-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/Pai0iDvA8ltsYeGLL/builds/SGqLg9NvYYfewjYkg/openapi.json
