# StepStone Jobs Scraper (`s-r/stepstone-scraper`) Actor

- **URL**: https://apify.com/s-r/stepstone-scraper.md
- **Developed by:** [SR](https://apify.com/s-r) (community)
- **Categories:** Jobs, Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## StepStone Jobs Scraper

A StepStone scraper that returns job postings from Germany, Austria and Belgium with the job link resolved correctly, the employer profile, the location, the posting age and whether StepStone holds a salary estimate for the role.

No proxy, no browser, no impersonation. An access probe graded stepstone.de as an open target, so this actor spends nothing getting in.

### What you get

- **The job link, not the employer link.** A StepStone card contains two anchors and the obvious one points at the company profile. Reading `a[href]` gives you employer pages for every row, which is the single most common way a StepStone scraper produces a useless dataset
- **`salary_available_on_page`**, a flag for whether StepStone has a salary estimate behind its show-salary control. In a validation run, 46 of 50 jobs had one. No other jobs actor surfaces this
- **Employer profile URL** alongside the employer name, so postings group per company without a name match
- **Work-from-home badge**, contract type and work type where the card carries them
- **Three markets**: Germany, Austria and Belgium
- **Stable job ids** parsed from the card, so scheduled runs deduplicate exactly
- **25 jobs per page with real pagination**, up to 500 per run
- Charged per job, plus a flat $0,002 start fee

### Why this one is cheap to run

The second finding was where the data is not. StepStone assigns `window.__PRELOADED_STATE__` piecewise, and its only slices are the header, the footer, a modal and the search bar. There is no job list in any state envelope, so the rendered card is the surface, and `[data-at="job-item"]` is a test hook rather than a styling class, which is what makes it survive the CSS-hash churn that breaks class-based selectors.

### Input

| Field | Type | Required | Default | What it does |
|---|---|---|---|---|
| `query` | string | yes | `data engineer` | Job title or keyword |
| `location` | string | no | – | City or region, for example Berlin |
| `market` | select | no | `de` | Germany, Austria or Belgium |
| `url` | string | no | – | A StepStone search URL to read instead |
| `radius` | integer | no | – | Search radius in kilometres |
| `sort` | select | no | `relevance` | Relevance or most recent |
| `limit` | integer | no | `50` | Jobs to return, 1 to 500 |
| `retries` | integer | no | `3` | Retry attempts per page |

### Output

```json
{
  "position": 1,
  "job_id": "14439627",
  "title": "Requirements- & Data-Engineer*",
  "url": "https://www.stepstone.de/stellenangebote--Requirements-Data-Engineer-Koeln-...",
  "company": "BarmeniaGothaer",
  "company_url": "https://www.stepstone.de/cmp/de/barmeniagothaer-29978/jobs",
  "location": "Köln, Wuppertal",
  "posted_relative": "vor 21 Stunden",
  "work_from_home": true,
  "salary_available_on_page": true,
  "salary_text": null,
  "is_promoted": false,
  "market": "de",
  "query": "data engineer"
}
```

### Use cases

**Recruitment market mapping in DACH.** StepStone is the dominant board in Germany and Austria, where Indeed is weaker than it is in English-speaking markets. Run your competitor set as queries and your region as the location, group by `company_url`, and you see who is hiring hard.

**Feeding a job board or aggregator.** Job ids are stable and the URL points at the posting rather than the employer, so a scheduled run with `sort: date` deduplicates cleanly into an index.

**Finding roles with published pay.** Filter on `salary_available_on_page`. StepStone computes an estimate for most postings but hides it behind a control, and knowing which roles have one is the difference between a targeted follow-up and opening fifty tabs.

**Hiring-signal tracking.** A company that posts fifteen engineering roles in a month is expanding. Run their name as a query weekly, count rows, and you have an indicator that arrives before any announcement.

### How it compares

| | this actor | `cheap_scraper/indeed-job-scraper` | `borderline/indeed-scraper` |
|---|---|---|---|
| Per 1.000 jobs | **$1,10** | $1,00 | $5,00 |
| Covers StepStone | **yes** | no | no |
| Job link resolved correctly | **yes** | n/a | n/a |
| Salary-estimate flag | **yes** | no | no |
| Employer profile URL | **yes** | no | no |
| Needs a proxy or browser | **no** | – | – |

Honest about the other side: nothing on the Store covers StepStone, so the comparison is against Indeed scrapers on a different board. If your market is the US or UK, an Indeed actor will serve you better. StepStone is where the DACH market actually posts.

### Pricing

Two events. A flat `run_start` of $0,002 covers the run, and `job` costs $0,0011 per posting returned, which is $1,10 per 1.000. Beyond the start fee you only pay for jobs you actually receive.

### Limits and gotchas

- **The salary figure is not on the card.** StepStone shows a "Gehalt anzeigen" control and reveals the range behind a click. `salary_available_on_page` tells you an estimate exists; retrieving the number would be a second fetch per row and this actor does not make it.
- **`location` can list several cities** for one posting, exactly as StepStone renders it. Split it yourself if you need one city per row.
- **Promoted postings appear in results** and are flagged. Filter them out before counting a market.
- **500 jobs per run** across 20 pages of 25. Beyond that, narrow the query or add a location.
- **German search terms work better** on the German and Austrian sites than English ones, even for technical roles.
- **The route is a measurement, not a fact.** StepStone is open today. If that changes, the failure will be a short body rather than an error, which is why the actor checks the response size rather than the status code.

### FAQ

**Do I need a proxy for StepStone?**
No. It was measured as an open target: any client, any headers, any IP. Adding a proxy would cost money and buy nothing.

**Why do other StepStone scrapers return company pages instead of jobs?**
Because each card holds two anchors and the first is the employer profile. The job link lives on the title element specifically.

**Can I get the salary?**
You get whether StepStone has an estimate for the role. The figure itself sits behind a click on the job page, which is a second request per job.

**Which countries are covered?**
Germany, Austria and Belgium, which is StepStone's own footprint.

**How do I only get new postings?**
Set `sort` to `date` and deduplicate on `job_id` against your previous run.

### Related Actors

- [Indeed Jobs Scraper](https://apify.com/s-r/indeed-jobs-scraper) — 14 markets, with parsed salary ranges
- [LinkedIn Jobs Scraper](https://apify.com/s-r/linkedin-jobs-scraper) — full descriptions and applicant counts
- [Reddit Scraper](https://apify.com/s-r/reddit-scraper) — what people say about an employer

# Actor input Schema

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

Job title or keyword. German terms work best on the German and Austrian sites.

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

City or region, for example Berlin or Wien. Leave empty to search the whole market.

## `market` (type: `string`):

Which StepStone site to read.

## `url` (type: `string`):

A StepStone search URL to read instead of building one from the fields above. Paste it from your browser after applying filters.

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

Search radius around the location.

## `sort` (type: `string`):

Relevance is StepStone's default. Date puts the newest first.

## `limit` (type: `integer`):

How many jobs to return, 1 to 500. A page carries 25.

## `retries` (type: `integer`):

Retry attempts per page. StepStone was measured as an open target, so this rarely matters.

## Actor input object example

```json
{
  "query": "data engineer",
  "location": "Berlin",
  "market": "de",
  "url": "https://www.stepstone.de/jobs/data-engineer/in-berlin",
  "sort": "relevance",
  "limit": 50,
  "retries": 3
}
```

# Actor output Schema

## `results` (type: `string`):

One row per job posting.

## `summary` (type: `string`):

Pages served, jobs returned, and how salary information was available.

## `errors` (type: `string`):

Per-page failures with a code and a redacted message.

# 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",
    "limit": 50,
    "retries": 3
};

// Run the Actor and wait for it to finish
const run = await client.actor("s-r/stepstone-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",
    "limit": 50,
    "retries": 3,
}

# Run the Actor and wait for it to finish
run = client.actor("s-r/stepstone-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",
  "limit": 50,
  "retries": 3
}' |
apify call s-r/stepstone-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,s-r/stepstone-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/8NeRAX9KNnbFah9ZX/builds/YdGkrFSqNzIR4tjfh/openapi.json
