# StepStone Jobs Scraper (DE/AT/BE) · From $1.00/1K (`valev-lab/stepstone-jobs-scraper`) Actor

Scrape stepstone.de, .at and .be jobs to JSON/CSV. Keywords, cities, remote & salary filters, company pages or job URLs. Flat rows with predicted salaries, skills, optional full descriptions and monitoring. No login. From $1.00 per 1,000 jobs.

- **URL**: https://apify.com/valev-lab/stepstone-jobs-scraper.md
- **Developed by:** [Daniel Valev](https://apify.com/valev-lab) (community)
- **Categories:** Jobs, Lead generation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 job listings

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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 job scraper** for [stepstone.de](https://www.stepstone.de/), [stepstone.at](https://www.stepstone.at/) and [stepstone.be](https://www.stepstone.be/) — Germany, Austria and Belgium. Export public job listings to **JSON / CSV / Excel** with keywords, cities, remote & salary filters, company career pages or job URLs. Flat rows with predicted salaries, skills and optional full descriptions. Change monitoring. **No StepStone login.** From **$1.00 / 1,000** listing rows.

### What does StepStone Jobs Scraper do?

This Actor scrapes public StepStone search pages and job ads on **stepstone.de**, **stepstone.at** and **stepstone.be** (including `.be` locale paths `/jobs`, `/emplois`, `/vacatures`). You get **one flat row per job**: title, company, location, predicted or stated salary, labels, skills and provenance. Turn on **Include full details** to open each job page for description sections, company facts and contacts printed in the ad.

It does **not** cover UK StepStone brands (Totaljobs, CWJobs, Jobsite, Caterer), stepstone.nl, website email enrichment, or chat/Slack notifications.

### Why scrape StepStone jobs?

- **Recruiting & talent sourcing** — Role lists across DE / AT / BE without copying from the browser.
- **Job-market research** — Titles, cities, predicted salaries, remote mix, sponsored vs organic.
- **Lead generation** — Companies that are hiring, plus contacts that employers print in the public ad.
- **Job monitoring** — Schedule a search; pay only for **new** or **changed** ads.
- **Automation** — Apify schedules, API, webhooks, Zapier / Make / n8n; export JSON, CSV, Excel or HTML.

### What data can you extract?

#### Every row (listing)

| Field | Description |
| --- | --- |
| `id` / `globalId` / `url` / `title` | StepStone job id, harmonised id, clean URL, title |
| `companyId` / `companyName` / `companyUrl` / `companyLogoUrl` | Employer identifiers and links |
| `location` / `postCode` / `country` / `site` | Place and which StepStone site |
| `remote` / `workFromHomeCode` | Human remote flag when known; raw code when present |
| `datePosted` / `validThrough` | Publish window |
| `salaryMin` / `salaryMax` / `salaryCurrency` / `salaryPeriod` / `salaryIsEstimate` / `salaryYearlyMin` / `salaryYearlyMax` | Numeric salary; estimates marked separately |
| `labels` / `skills` / `snippet` / `benefits` | Labels, skills, plain-text snippet |
| `isSponsored` / `isTopJob` / `isHighlighted` / `isPartnerJob` / `resultSection` / `position` | Placement flags |
| `searchKeyword` / `searchLocation` / `searchUrl` / `scrapedAt` | Provenance |
| `detailsFetched` | Whether the job page was opened |

#### With `includeDetails: true`

| Field | Description |
| --- | --- |
| `description` / `descriptionHtml` / `descriptionMarkdown` | Per `descriptionFormat` |
| `descriptionIntro` / `descriptionTasks` / `descriptionRequirements` / `descriptionOffer` / `descriptionContact` | Ad sections |
| `contractType` / `workType` / `employmentType` / `industry` / `directApply` | Structured meta |
| Address / geo | `streetAddress`, `postalCode`, `addressLocality`, … |
| `contactEmails` / `contactPhones` | Only text printed in the ad (toggle `extractContacts`) |
| Company facts | Website, address, size, founded, industries, rating, review count, open jobs |
| `detailError` | e.g. `offline` / `blocked` when the detail page could not be charged as `job-detail` |

#### With monitoring (`onlyNewOrChanged`)

| Field | Description |
| --- | --- |
| `changeType` | `NEW` or `UPDATED` |
| `changedFields` / `previousValues` | What changed; previous values as a JSON string (flat rows) |
| `firstSeenAt` / `lastSeenAt` | First / last observation |

### How to scrape StepStone jobs

1. Open the Actor and click **Try for free**.
2. Set **Keywords** (e.g. `software engineer`) and optional **Locations** (e.g. `hamburg`), or paste **Start URLs** from the site (filters included).
3. Choose **Country** `de` / `at` / `be` when building from keywords.
4. Set **Max jobs** (start with 25).
5. Optionally turn on **Include full details**.
6. Click **Start**. Download JSON / CSV / Excel, or send the dataset to an integration.

#### Start URL types

| URL | Behaviour |
| --- | --- |
| Search / filtered listing | Paginated listing scrape |
| `/work/…` keyword URL | Same as search |
| `/cmp/…/jobs` company list | Only that employer’s jobs |
| `…--<id>-inline.html` job page | Single job (details fetched) |

When **Start URLs** are set, keyword / location builders are ignored.

#### Monitoring on a schedule

1. Set the same search input and a stable **Monitoring key** (or leave empty for an auto hash).
2. Set **Sort by** to **Date**.
3. Enable **Only new or changed**.
4. Create an Apify Schedule (e.g. daily). Unchanged jobs are **not** saved and **not** charged.

### How much does it cost?

Pricing is [pay-per-event](https://docs.apify.com/platform/actors/running/pay-per-event). Configure amounts in Console → Publishing → Monetization. The Actor emits these event names:

| Event | Suggested price | When |
| --- | ---: | --- |
| `job` (primary) | **$0.001** ($1.00 / 1,000) | Row saved **without** full details |
| `job-detail` | **$0.0018** ($1.80 / 1,000) | Row saved **with** full details — **instead of** `job`, never both |
| `apify-actor-start` | Platform synthetic | Run start (memory-based) |

**Worked examples**

- 250 listing jobs → `250 × $0.001 = $0.25`
- 1,000 jobs with details → `1,000 × $0.0018 = $1.80`
- Daily monitoring of a search with ~50 new jobs/day (listing only) → ≈ `50 × 30 × $0.001 ≈ $1.50 / month`

Do **not** enable `apify-default-dataset-item`, or every row is billed twice. Unchanged monitoring rows are free. Apify compute and proxy are billed separately by the platform.

Non-paying Apify users are capped at **20 rows per run** (clear log message).

### Input

See the **Input** tab for the full form. Highlights:

| Field | Default | Notes |
| --- | --- | --- |
| `startUrls` | — | Search, `/work/`, company `/cmp/…/jobs`, or job URLs |
| `keywords` / `locations` / `country` | `software engineer` / *(empty = whole country)* / `de` | Prefill location `hamburg`; cartesian product of searches |
| `radius` | — | km around the location |
| `postedWithin` | `any` | Verified: `1` (24h), `7`, `30` |
| `remote` | `any` | `remote` / `hybrid` / `onsite` via StepStone `wp=` facet |
| `minSalary` | — | Yearly filter (`us=`) |
| `applicationMethod` | `any` | Quick apply vs company website |
| `sortBy` | `relevance` | Or `date` |
| `includeDetails` | `false` | Opens job pages; charges `job-detail` |
| `descriptionFormat` | `text` | `text` / `html` / `markdown` / `all` |
| `maxItems` | `25` | Max saved (= max charged) rows |
| `onlyNewOrChanged` / `monitoringKey` | off | Monitoring mode |
| `proxyConfiguration` | Apify Proxy | Datacenter by default; escalates to residential DE after blocks |

Working hours, experience level, and job-language filters are **not** exposed until verified live — paste a filtered Start URL from the site instead.

### Output

You can download the dataset as **JSON, CSV, Excel, or HTML**. Rows are flat (arrays of strings only; no nested objects).

#### Listing example (from a real local run)

```json
{
  "id": "14693305",
  "globalId": "283655f7-1457-4f9f-9b7c-ed3ecc95e733",
  "url": "https://www.stepstone.de/stellenangebote--Hackers-wanted-Software-Engineers-Hamburg-freiheit-com-technologies--14693305-inline.html",
  "title": "Hackers wanted – Software Engineers",
  "companyName": "freiheit.com technologies",
  "location": "Hamburg",
  "country": "de",
  "site": "stepstone.de",
  "salaryMin": 50000,
  "salaryMax": 66000,
  "salaryCurrency": "EUR",
  "salaryPeriod": "year",
  "salaryIsEstimate": true,
  "datePosted": "2026-10-28T14:46:11.273Z",
  "resultSection": "main",
  "searchKeyword": "software engineer",
  "searchLocation": "hamburg",
  "detailsFetched": false
}
```

#### Detail fields example (same job, after `includeDetails`)

```json
{
  "id": "14693305",
  "detailsFetched": true,
  "contractType": "Feste Anstellung",
  "workType": "Homeoffice möglich, Vollzeit",
  "descriptionIntro": "We are an engineering organization…",
  "descriptionTasks": "Your responsibility: From day one…",
  "descriptionRequirements": "A degree: Bachelor's, Master's or PhD…",
  "companyAddress": "Budapester Straße 45, Hamburg",
  "companySize": "251-1000",
  "companyFounded": 1999,
  "companyOpenJobs": 6,
  "companyRating": 4.57,
  "companyReviewCount": 16,
  "contactPhones": ["+49 40/890584-0"]
}
```

#### Monitoring example

```json
{
  "id": "14693305",
  "changeType": "UPDATED",
  "changedFields": ["salaryMax", "validThrough"],
  "previousValues": "{\"salaryMax\":66000,\"validThrough\":\"2026-11-27T14:46:11.273Z\"}",
  "firstSeenAt": "2026-10-01T08:00:00.000Z",
  "lastSeenAt": "2026-10-30T08:00:00.000Z"
}
```

Dataset views in Console: **Overview**, **Details**, **Monitoring**.

### Limitations

- Broad searches may advertise a high `pageCount`; if pagination stops early, the run summary records it — split by location or date.
- Many salaries are **StepStone estimates** (`salaryIsEstimate: true`); employer-stated figures are kept apart when present.
- On some DE `/jobs/` listings `workFromHomeCode` is null; use the `remote` filter and/or detail `workType` text.
- `postCode` is often missing on listing rows.
- Region-restricted ads may keep listing data with `detailError` and charge `job` instead of `job-detail`.
- Public pages only. No login, CAPTCHA solving, or CV/candidate data.

### FAQ

#### Can I scrape Austria and Belgium?

Yes. Set `country` to `at` or `be`, or paste start URLs from those hosts. Belgium supports `/emplois/`, `/vacatures/`, and `/jobs/` paths.

#### Do you charge for failed detail pages?

If the ad is offline or blocked after retries, the Actor keeps listing fields, sets `detailError`, and charges **`job`**, not `job-detail`.

#### How do I keep costs down?

Use listing mode for discovery, lower `maxItems`, avoid residential unless needed, and use monitoring so unchanged jobs are free.

#### Is this affiliated with StepStone?

No. It is an independent Apify Actor for public pages.

### For AI agents

**Minimal input**

```json
{
  "keywords": ["software engineer"],
  "locations": ["hamburg"],
  "country": "de",
  "maxItems": 25
}
```

**Behaviours**

- Empty / schema-default runs probe a small DE Hamburg search.
- Deduplicates by job `id` across searches in one run.
- Drops `recommended` sections unless `includeRelatedJobs` is true.
- Monitoring state lives in named key-value store `stepstone-monitor`.
- Writes `RUN_SUMMARY` (and `MONITORING_SUMMARY` when monitoring).

**Billing**

- Exactly one of `job` or `job-detail` per saved row.
- Never emits `apify-default-dataset-item`.
- Respects `ACTOR_MAX_TOTAL_CHARGE_USD` / charge-limit stop.

### Disclaimer

This Actor is **not affiliated with StepStone**. It only accesses **public** pages. You are responsible for complying with StepStone’s terms of use, robots rules, and applicable law including GDPR. Contact emails and phones are taken only from text that employers publish in the job ad — not from crawled company websites. Do not scrape personal data unless you have a legitimate reason.

Use the **Issues** tab for bugs and feature requests. Use the **API** tab for programmatic runs.

# Actor input Schema

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

Search URLs (with any filters set on the site), `/work/…` URLs, company job lists `/cmp/…/jobs`, or single job URLs (`…--<id>-inline.html`). When non-empty, keywords/locations/country filters are ignored for URL building.

## `keywords` (type: `array`):

Each keyword is a separate search. Combined with Locations (cartesian product).

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

City, region or postcode. Empty = whole-country style search path for the selected country (deutschland / oesterreich / belgique).

## `country` (type: `string`):

Which StepStone site to use when building searches from keywords/locations.

## `radius` (type: `integer`):

Search radius in kilometres when a location is set. Empty = site default.

## `postedWithin` (type: `string`):

Verified age facet values: last 24h (`age_1`), 7 days (`age_7`), 30 days (`age_30`).

## `remote` (type: `string`):

Uses StepStone workPolicy facet (`wp=`): Remote, Hybrid, or On-site (Vor Ort).

## `minSalary` (type: `integer`):

StepStone unified salary filter (`us=`). Matches stated and predicted salaries.

## `applicationMethod` (type: `string`):

Quick apply on StepStone (`am=INTERNAL`) or apply on company website (`am=EXTERNAL`).

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

Relevance (site default) or newest first. Prefer date when using monitoring.

## `excludeSponsored` (type: `boolean`):

Drop rows where isSponsored is true (post-filter).

## `includeRelatedJobs` (type: `boolean`):

Keep StepStone `recommended` padding jobs. Off by default.

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

Open each job page for description sections, company facts and contacts. Charged as event `job-detail` ($0.0018) instead of `job` ($0.001).

## `descriptionFormat` (type: `string`):

Which description representation(s) to store when details are fetched.

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

Truncate description fields to this many characters. Empty = no truncation.

## `extractContacts` (type: `boolean`):

Collect emails and phones printed in the job ad only (no website crawling).

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

Maximum rows saved (= maximum charged rows).

## `maxItemsPerSearch` (type: `integer`):

Optional cap per keyword×location or start URL.

## `onlyNewOrChanged` (type: `boolean`):

Save only jobs that are new or changed since the previous run with the same monitoring key. Unchanged jobs are not charged. Prefer sortBy=date.

## `monitoringKey` (type: `string`):

Stable name for this watch. Default: hash of the normalised search input.

## `maxScanPerSearch` (type: `integer`):

How many listing rows to check per search in monitoring mode.

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

Default Apify datacenter proxy. Sessions escalate to RESIDENTIAL (DE) after blocks.

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

Parallel detail/listing workers.

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

Retries per request before giving up (includes proxy escalation attempts).

## Actor input object example

```json
{
  "startUrls": [],
  "keywords": [
    "software engineer"
  ],
  "locations": [
    "hamburg"
  ],
  "country": "de",
  "postedWithin": "any",
  "remote": "any",
  "applicationMethod": "any",
  "sortBy": "relevance",
  "excludeSponsored": false,
  "includeRelatedJobs": false,
  "includeDetails": false,
  "descriptionFormat": "text",
  "extractContacts": true,
  "maxItems": 25,
  "onlyNewOrChanged": false,
  "maxScanPerSearch": 1000,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "maxConcurrency": 10,
  "maxRequestRetries": 5
}
```

# Actor output Schema

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

No description

## `runSummary` (type: `string`):

No description

## `monitoringSummary` (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 = {
    "keywords": [
        "software engineer"
    ],
    "locations": [
        "hamburg"
    ],
    "maxItems": 25
};

// Run the Actor and wait for it to finish
const run = await client.actor("valev-lab/stepstone-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 = {
    "keywords": ["software engineer"],
    "locations": ["hamburg"],
    "maxItems": 25,
}

# Run the Actor and wait for it to finish
run = client.actor("valev-lab/stepstone-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 '{
  "keywords": [
    "software engineer"
  ],
  "locations": [
    "hamburg"
  ],
  "maxItems": 25
}' |
apify call valev-lab/stepstone-jobs-scraper --silent --output-dataset

```

## MCP server setup

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