# StepStone Jobs Search Lookup: jobs scraper from $1.50/1k (`accountable_eel/stepstone-jobs-lookup`) Actor

StepStone jobs scraper by keyword and location — run a search on StepStone.de, the largest job board in Germany/Austria/Switzerland, and get one row per posting: title, company, location, home-office badge, link. No login required. Pay per posting returned; empty searches are free.

- **URL**: https://apify.com/accountable\_eel/stepstone-jobs-lookup.md
- **Developed by:** [Adrian Voss](https://apify.com/accountable_eel) (community)
- **Categories:** Jobs, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.14 / 1,000 job posting returneds

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?

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 Search Lookup: StepStone Jobs Scraper by Keyword & Location

You write job searches the way you'd type them into StepStone — `Software Engineer @ Berlin` —
and this actor runs each one against **StepStone.de**, one of the largest job boards in the
DACH region (Germany, Austria, Switzerland), and returns one clean row per posting: title,
company, location, home-office badge, how recently it was posted, and a permanent link.

No login. No API key. StepStone's search page is behind a connection-level bot wall, but the
page itself is server-rendered with real job data once that wall is cleared — see "Why this
one" below.

### Who it's for

The accountable\_eel catalogue sells company and hiring intelligence columns for outbound and
recruiting. Each actor takes a list of identifiers — domains, company slugs, and here, job
searches — and returns one flat, stably-named row per result: the shape a Clay table, an n8n
workflow, or an AI agent can consume without post-processing. Pricing is pay-per-event: a
fraction of a cent for a row you actually got, and nothing at all for a search that finds
nothing. No seat licence, no monthly minimum, no credit system to decode.

This actor is the DACH-region counterpart to `seek-jobs-search-lookup` and
`linkedin-jobs-search-lookup`: wide-net keyword + location search rather than a per-company
listing. The ATS lookups in the same family (`greenhouse-jobs-lookup`, `lever-jobs-lookup` and
friends) answer "what is *this company* hiring for" — you bring the company list. This one
answers "who is hiring for *this role*, in *this German-speaking city*" when you don't have the
list yet.

### Why this one

- **Reaches data a plain request can't.** A bare request to StepStone's search pages is blocked
  at the connection level — the server accepts the TLS handshake and then either resets the
  stream or never responds at all, before any page-level check even runs. This actor routes
  through a proxy tier built for exactly this kind of defense; the page itself, once reached, is
  ordinary server-rendered HTML with the job data already in it — no headless browser needed.
- **The filters are real.** A location segment genuinely narrows results — a Berlin search
  returned only Berlin-inclusive postings in verification, not a superset silently unfiltered —
  and pagination returns a genuinely disjoint set of postings per page, not a repeated cache.
- **You are never billed for the same posting twice.** Every posting is deduplicated by its
  StepStone job ID across the pages of one search and, by default, across every search in the
  run — an overlapping pair of searches returns, and bills, each posting once.
- **A generous depth ceiling, not a silent ceiling.** StepStone served a real page 182 pages deep
  into a 4,544-result search in verification — this actor still caps paging at a modest default
  (8 pages / ~200 postings per search) to keep proxy cost predictable, not because StepStone
  itself stops sooner. Raise "Most postings to return per search" if you need more.
- **Honest about proxy cost.** Reaching StepStone requires a paid proxy tier, unlike most of this
  catalogue's actors. That cost is priced into what you're charged per posting — see Pricing.

### What you get

One row per job posting by default. (Turn off "One row per job posting" in the Input tab to get
one row per *search* instead, with the whole posting list nested in `jobs`.) Every row carries
these fields:

| Field | Type / format | Description |
| --- | --- | --- |
| `query` | text | The search line you passed in, unchanged. |
| `found` | boolean | `true` if the search returned at least one posting. `false` rows are never charged. |
| `status` | text | `OK`, `NOT_FOUND` (no postings for that search), `BAD_FORMAT` (a blank line), or `BLOCKED`. |
| `searchKeywords` | text | The keywords actually sent to StepStone. |
| `searchLocation` | text | The location actually sent to StepStone. |
| `jobCount` | number | How many postings this search returned after your filters — this is exactly what you're charged for. |
| `totalAvailable` | number | StepStone's own total-match count for the search, before this actor's page/depth limits. |
| `truncated` | boolean | `true` if more postings were available than you asked for, or if paging stopped early. |
| `jobs` | array | The full posting list. Present in every row; it's what gets expanded into separate rows in "one row per posting" mode. |
| `jobId` | text | StepStone's own numeric posting ID — stable, and what deduplication keys on. |
| `title` | text | Job title. |
| `companyName` | text | Hiring company as StepStone names it. |
| `location` | text | Location(s) as StepStone prints them on the posting — some postings list several cities. |
| `remote` | boolean | `true` if the location, home-office badge, or title reads as remote. |
| `workFromHome` | boolean | `true` if StepStone shows its own "home office" badge on the card. |
| `postedAgo` | text | How recently it was posted, in StepStone's own words ("vor 1 Tag"). |
| `jobUrl` | link | Permanent link to the posting on stepstone.de. |
| `scrapedAt` | date (ISO) | When this actor fetched the row. |

A search that returns no postings comes back as a single `found: false` row with a
`status`/`message` explaining why, and is never charged. So does a blank line.

### Pricing

- **Job posting returned**: $1.5 per 1,000 job postings

Plus a $0.00005 start fee per run. Each event above is billed independently, only when it actually returns data — misses (`found:false`) are never charged.

You're charged **per posting returned**, not per search — a search that returns 12 postings
costs twelve, a search that returns none costs nothing, and a blank line costs nothing.

Because you pay per posting, "Most postings to return per search" is your budget control: leave
it at 50 and a ten-search run costs at most 500 postings' worth.

### How to use

1. **In the Apify Console.** Open the actor page and click **Start** — the `searches` field is already pre-filled with a working example. Results land in the run's dataset as soon as each item is found.
2. **Via the API.** Call it directly with a POST request — no Console needed once you have an API token:
   ```bash
   curl "https://api.apify.com/v2/acts/accountable_eel~stepstone-jobs-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \
     -X POST \
     -H "Content-Type: application/json" \
     -d '{"searches":["Software Engineer @ Berlin"]}'
   ```
3. **On a schedule.** Save this actor as an Apify **Task** with the input you want, then add a **Schedule** (hourly, daily, weekly) so it runs on its own — no server of your own required.

Paste one search per line:

```
Software Engineer @ Berlin
Bilanzbuchhalter
Pflegefachkraft @ Muenchen
```

A line with no `@ location` uses the location you set once in **🔍 Search settings** — leave
that blank too and the search covers all of Germany.

**🔍 Search settings** — these are sent to StepStone, so they narrow the search before results
are returned, which makes runs cheaper as well as more relevant:

| Input | What it does |
| --- | --- |
| `defaultLocation` | Location for any line that doesn't name one. Write it as StepStone does — "Berlin", "Muenchen", "Hamburg". |
| `maxJobsPerQuery` | Most postings to return per search. Default 50, capped at 200 (8 pages) by default. |

**🎯 Narrow the results** — applied here, to the postings after they arrive, so you can filter on
things StepStone's own search box doesn't expose. They combine with AND across fields and OR
within a field:

| Input | What it does |
| --- | --- |
| `titleKeywords` | Keep only titles containing one of these. |
| `excludeTitleKeywords` | Drop titles containing one of these — e.g. `["Praktikum","Werkstudent"]`. Applied after the include list. |
| `companies` | Keep only these companies — partial names match. |
| `excludeCompanies` | Drop these companies — useful for filtering out recruitment agencies you already know. |
| `locations` | Narrow a wide search to particular districts. |
| `remoteOnly` | Keep only roles whose location, home-office badge, or title reads as remote. |
| `skipDuplicateJobs` | On by default. Each posting is returned, and billed, once per run even if two searches overlap. |

### Input

```json
{
  "searches": [
    "Software Engineer @ Berlin"
  ]
}
```

One search per line. Write it as "keywords @ location", or just the keywords and set a location below. Accepted formats: Softwareentwickler @ Berlin, Bilanzbuchhalter, Pflegefachkraft @ Muenchen.

### Output

| query | found | status | searchKeywords | searchLocation | jobCount | totalAvailable | truncated | jobs | jobId | title | companyName | location | remote | workFromHome | postedAgo | jobUrl | scrapedAt |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| Software Engineer @ Berlin | true | OK | Software Engineer | Berlin | 50 | 589 | true | \<all postings found (full list)> | 13817131 | Software Engineer Public Sector (all genders) | msg systems ag | Berlin, Chemnitz, Dresden, Düsseldorf, Essen, Frankfurt am Main, Hamburg, Hannover | true | true | vor 1 Woche | https://www.stepstone.de/stellenangebote--Software-Engineer-Public-Sector-all-genders-Berlin-Chemnitz-Dresden-Duesseldorf-Essen-Frankfurt-am-Main-Hamburg-Hannover-msg-systems-ag--13817131-inline.html | 2026-08-31T07:00:50.030Z |

A miss comes back as a row with `"found": false` and is never charged.

### Use it from Clay, n8n, Make, or an AI agent

This actor runs synchronously over plain HTTP — call it directly from a script, a workflow tool, or an AI agent, no Apify Console needed once you have an API token.

```bash
curl "https://api.apify.com/v2/acts/accountable_eel~stepstone-jobs-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{"searches":["Software Engineer @ Berlin"]}'
```

**n8n.** Add an HTTP Request node: Method `POST`, URL `https://api.apify.com/v2/acts/accountable_eel~stepstone-jobs-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>`, Body Content Type `JSON`, JSON Body `{"searches":["Software Engineer @ Berlin"]}` (swap in an expression from an earlier node for a real value).

**Clay.** Add an "HTTP API" column: Method `POST`, URL `https://api.apify.com/v2/acts/accountable_eel~stepstone-jobs-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>`, Body `{"searches":["{{search}}"]}`, mapping the row's search into the `searches` array.

**MCP.** In Claude, Cursor, or any MCP client with the Apify MCP server, ask for "StepStone Jobs Scraper | Apify" — the agent will find and run this actor.

# Actor input Schema

## `searches` (type: `array`):

One search per line. Write it as "keywords @ location", or just the keywords and set a location below. Accepted formats: Softwareentwickler @ Berlin, Bilanzbuchhalter, Pflegefachkraft @ Muenchen. You're only charged for the ones we actually find — a miss costs nothing.

## `testRun` (type: `boolean`):

Turn this on to test your input on a small sample before running the full list. Turn it off to process everything.

## `onlyFound` (type: `boolean`):

Only keep rows where something was actually found. Misses are always free, whether or not you show them here.

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

Optional. Only keep results that mention at least one of these words (e.g. a job title, a city, a product name). Leave empty to keep everything.

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

Optional. Drop any result that mentions one of these words. Leave empty to skip nothing.

## `maxResults` (type: `integer`):

Optional. Stop the run once this many results have been found — useful for a quick, cheap sample. Leave blank for no limit.

## `defaultLocation` (type: `string`):

Used for any line that's just keywords. Write it the way StepStone does — "Berlin", "Muenchen", "Hamburg". Leave empty to search all of Germany.

## `maxJobsPerQuery` (type: `integer`):

StepStone shows 25 postings per page. This actor stops at 8 pages (200 postings) per search by default to keep proxy cost predictable — raise it if you need more. You pay per posting returned, so this is also your budget control.

## `titleKeywords` (type: `array`):

Optional. Keep only roles whose title contains at least one of these words. Case doesn't matter and partial words work. Leave empty to keep every role.

## `excludeTitleKeywords` (type: `array`):

Optional. Drop any role whose title contains one of these words — e.g. "Praktikum", "Werkstudent". Applied after the include list above.

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

Optional. Keep only roles at companies whose name contains one of these. Partial matches work.

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

Optional. Drop roles at companies whose name contains one of these — handy for filtering out recruitment agencies you already know.

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

Optional. Narrow a wide search down to particular districts — applied to the location StepStone prints on each posting.

## `remoteOnly` (type: `boolean`):

Keep only roles whose location, home-office badge, or title reads as remote. Applied to the postings after they arrive — treat it as a strong filter, not a guarantee.

## `skipDuplicateJobs` (type: `boolean`):

On by default. Overlapping searches routinely surface the same role — with this on, each posting is returned, and billed, exactly once per run.

## `columns` (type: `array`):

Choose which pieces of information to include in each result row. All are included by default.

## `expandRows` (type: `boolean`):

When on, each job posting found gets its own row instead of being grouped under its search. You're still only charged once per search, no matter how many rows it produces.

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

Parallel requests. Keep conservative — this target has no browser fallback, so getting blocked costs more than slow-and-steady.

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

Apify Proxy config. Residential recommended for anti-bot-sensitive targets.

## Actor input object example

```json
{
  "searches": [
    "Software Engineer @ Berlin"
  ],
  "testRun": false,
  "onlyFound": false,
  "includeKeywords": [],
  "excludeKeywords": [],
  "defaultLocation": "",
  "maxJobsPerQuery": 50,
  "titleKeywords": [],
  "excludeTitleKeywords": [],
  "companies": [],
  "excludeCompanies": [],
  "locations": [],
  "remoteOnly": false,
  "skipDuplicateJobs": true,
  "columns": [
    "searchKeywords",
    "searchLocation",
    "jobCount",
    "totalAvailable",
    "truncated",
    "jobs",
    "jobId",
    "title",
    "companyName",
    "location",
    "remote",
    "workFromHome",
    "postedAgo",
    "jobUrl"
  ],
  "expandRows": true,
  "maxConcurrency": 5,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "UNBLOCKER"
    ]
  }
}
```

# 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 = {
    "searches": [
        "Software Engineer @ Berlin"
    ],
    "includeKeywords": [],
    "excludeKeywords": [],
    "titleKeywords": [],
    "excludeTitleKeywords": [],
    "companies": [],
    "excludeCompanies": [],
    "locations": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("accountable_eel/stepstone-jobs-lookup").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 = {
    "searches": ["Software Engineer @ Berlin"],
    "includeKeywords": [],
    "excludeKeywords": [],
    "titleKeywords": [],
    "excludeTitleKeywords": [],
    "companies": [],
    "excludeCompanies": [],
    "locations": [],
}

# Run the Actor and wait for it to finish
run = client.actor("accountable_eel/stepstone-jobs-lookup").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 '{
  "searches": [
    "Software Engineer @ Berlin"
  ],
  "includeKeywords": [],
  "excludeKeywords": [],
  "titleKeywords": [],
  "excludeTitleKeywords": [],
  "companies": [],
  "excludeCompanies": [],
  "locations": []
}' |
apify call accountable_eel/stepstone-jobs-lookup --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,accountable_eel/stepstone-jobs-lookup"
        }
    }
}

```

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/IiN6dptbIkBtD36Br/builds/cJtFfJwBsOWyUUDqM/openapi.json
