# Randstad Jobs Scraper (`nanoscrape/randstad-jobs-scraper`) Actor

Scrape job listings from Randstad Germany, Switzerland and the United Kingdom in one run. Title, company, location, hours, salary, categories and dates per job, plus full description and consultant contact with details. Pay-per-result.

- **URL**: https://apify.com/nanoscrape/randstad-jobs-scraper.md
- **Developed by:** [NanoScrape](https://apify.com/nanoscrape) (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

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?

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

## Randstad Jobs Scraper: Germany, Switzerland and the UK

Get the public job listings of Randstad in Germany (randstad.de), Switzerland (randstad.ch) and the United Kingdom (randstad.co.uk) as one clean dataset. Search by keyword, filter by location and posting date, and optionally add the full job description and the consultant contact. One row per job.

### What you can do with it

- Track the temporary and permanent jobs one of the largest staffing companies posts in three countries, in one run.
- Build a daily feed of new jobs per field, for example nursing, logistics or IT, with the posting date on every row.
- Compare pay, hours and contract types across Germany, Switzerland and the UK.
- Find which employers use Randstad for their hiring, from the end client name where Randstad shows it.
- Feed job boards, lead lists, market reports or alerts with structured data instead of web pages.

### What you get

One row per job. Fields that a country does not publish are `null`, lists are always `[]` when empty.

| Field | What it holds |
|---|---|
| `id`, `job_id`, `reference` | Row id (`randstad-<country>-<job id>`), Randstad's job id and the vacancy reference |
| `title` | Job title |
| `company_name`, `client_name` | Company on the job (Randstad when the end client is hidden) and the end client when Randstad shows it |
| `location`, `city`, `region`, `postal_code`, `country` | Place of the job, country as ISO code (`DE`, `CH`, `GB`) |
| `latitude`, `longitude`, `street` | With job details (all countries; Swiss jobs give coordinates and postal code, no street) |
| `employment_type`, `employment_type_normalized`, `hours`, `work_time`, `contract_duration` | Contract type as Randstad shows it, a normalized value, working hours and duration |
| `salary_min`, `salary_max`, `salary_currency`, `salary_period`, `salary_text`, `salary_note` | Pay as published (Germany and UK; Switzerland publishes no pay in its listing) |
| `description`, `description_html`, `description_truncated` | Short text in the listing, full text and HTML with job details |
| `responsibilities`, `requirements`, `benefits` | Lists from the job page and the benefit tags of the listing |
| `specialism`, `sub_specialism`, `industry`, `role`, `categories` | Field and category tags |
| `created_at`, `updated_at`, `expires_at`, `start_date`, `end_date` | Dates (ISO 8601) |
| `job_url`, `apply_url`, `external_url`, `alternate_urls` | Links to the job on Randstad, the application and the original posting; Swiss jobs list the language versions |
| `contact_name`, `contact_email`, `contact_phone`, `contact_office`, `contact_street`, `contact_city`, `contact_postcode` | The consultant and branch, with job details (Germany and UK; Switzerland gives the consultant name and email) |
| `language`, `detail_fetched`, `source_query`, `source_country`, `scraped_at` | Language of the text, whether the job page was read, the search that found the job, the country you asked for and the collection time |

Every job appears once per country, even when several searches or pages find it.

### Input

| Field | Type | Description |
|---|---|---|
| `countries` | string list | `DE`, `CH`, `UK`. Default `["DE"]`. Lower case, `GB`, a comma separated text and country names are accepted |
| `searchQueries` | string list | Keywords such as a job title or field. Each keyword runs in every country. Empty means all jobs |
| `location` | string list | Optional places, for example `Berlin`, `Zurich`, `Manchester` or a postcode start such as `80`. Only matching jobs are returned. In Germany and the UK, together with a keyword, the place is searched directly on Randstad, which is much faster |
| `postedWithinDays` | integer | Only jobs posted in the last N calendar days (UTC, today included). `0` means no limit |
| `maxResults` | integer | Total number of jobs over everything. Default `100`, `0` means no limit |
| `maxResultsPerQuery` | integer | Jobs per country and keyword. Default `50`, `0` means no limit |
| `includeDetails` | boolean | Read each job page for the extra fields listed above. Default `false` |
| `startUrls` | string list | Optional Randstad search URLs, each one runs as an extra search |

#### Example: nursing jobs in Germany and the UK

```json
{
  "countries": ["DE", "UK"],
  "searchQueries": ["pflege", "nurse"],
  "postedWithinDays": 7,
  "maxResultsPerQuery": 50,
  "maxResults": 200
}
```

#### Example: everything new in Switzerland

```json
{
  "countries": ["CH"],
  "postedWithinDays": 2,
  "maxResults": 0
}
```

#### Example: full descriptions and contacts in one city

```json
{
  "countries": ["DE"],
  "searchQueries": ["logistik"],
  "location": ["Hamburg"],
  "includeDetails": true,
  "maxResults": 40
}
```

#### Supported URLs

- `https://www.randstad.de/jobs/` and `https://www.randstad.de/jobs/q-pflege/`
- `https://www.randstad.ch/jobs/` and `https://www.randstad.ch/jobs/q-pflege/` (language prefixes such as `/de/jobs/` work too)
- `https://www.randstad.co.uk/jobs/` and `https://www.randstad.co.uk/jobs/q-nurse/`

Page numbers in a URL are ignored, the actor reads all pages up to your limits. URLs of single jobs are not search URLs and are skipped with a note in the log.

### Output

```json
{
  "id": "randstad-de-c01319492",
  "job_id": "C01319492",
  "title": "Pflegefachkraft (m/w/d)",
  "company_name": "Randstad Deutschland",
  "client_name": null,
  "location": "Berlin",
  "city": "Berlin",
  "postal_code": "10115",
  "country": "DE",
  "employment_type": "Zeitarbeit",
  "hours": "Vollzeit",
  "salary_currency": "EUR",
  "created_at": "2026-11-02T08:15:00Z",
  "job_url": "https://www.randstad.de/jobs/pflegefachkraft_berlin_c01319492/",
  "source_query": "pflege",
  "source_country": "DE",
  "scraped_at": "2026-11-04T09:00:00Z"
}
```

The example is shortened and the values are illustrative. Real rows hold all fields of the table above.

### Pricing

Pay per result, two events:

- `apify-actor-start`: $0.001 once per run.
- `job-result`: $0.001 per job in the dataset ($1.00 per 1,000 jobs).

Filtered-out and duplicate jobs are never charged. Set a maximum total charge on the run to stop at a budget; the run then ends cleanly with the message "Stopped at the max charge limit. N jobs delivered." The pricing box on the actor page always shows the current rates.

### Use with AI Agents (MCP)

Connect this actor to any MCP-compatible AI client, such as Claude Desktop, Claude.ai, Cursor, VS Code, LangChain, LlamaIndex, or custom agents.

**Example prompts once connected:**

> "Use `randstad-jobs-scraper` to find nursing jobs in Germany posted in the last 3 days and list title, city and hours."

> "Use `randstad-jobs-scraper` for DE, CH and UK with the keyword logistics and compare the salary ranges."

Configure the MCP server with the Randstad Jobs Scraper preconfigured at `mcp.apify.com?tools=nanoscrape/randstad-jobs-scraper`:

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=nanoscrape/randstad-jobs-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}
```

See the auto-generated API tab on the actor page for language-specific examples (cURL, JavaScript, Python, and more).

### FAQ

**What does one run cost?**
A start fee plus one result fee per job in the dataset. Use `maxResults` or a maximum total charge to cap it.

**Why is `client_name` often empty?**
Randstad names the end client only on some jobs. When it does not, `company_name` is Randstad and `client_name` is `null`.

**What does `includeDetails` add?**
It reads each job page and adds the full description as text and HTML, requirements, coordinates, consultant name and contact and the expiry date. Germany and the UK add more: responsibilities, street, update dates, the application link and the exact country of the job. Switzerland adds the postal code, number of openings, the task list and the offer list from the description, and the contract type.

**Why can a UK search return jobs in Ireland or Austria?**
The Randstad UK site also lists a few jobs of other countries. Without job details the row carries the country of the site; with `includeDetails` the exact country is filled.

**How does the location filter work?**
In Germany and the UK, a keyword plus a place such as `Hamburg`, `München` or `London` is searched on Randstad itself, so only the jobs of that place are read (a state such as `Bayern` works too). If Randstad does not know the place, or you give a postcode start, or you search without a keyword, the actor reads the keyword search and keeps the jobs whose city, region, location text or postal code start matches one of your places. This is also the way for Switzerland. Every row is checked against your place before it is delivered, so jobs elsewhere are never charged. The filtered search stops after 40 pages in a row without a match, so combine it with a keyword on large sites.

**Can I get only new jobs?**
Yes, set `postedWithinDays`. Without a keyword, the listing is newest first and the search stops at the first page that holds only older jobs.

**What if a search finds nothing?**
The run ends successfully with the message "0 jobs." and the reason, for example no hits or no job inside your filters.

### Limits

- At most 500 pages (15,000 jobs) are read per search. Use keywords to split larger searches.
- Pages are read one after the other with a short pause per site, so very large runs take a while.
- Swiss listings carry no pay, hours, start date or application link, and Swiss job pages do not add them: the page only says things like "attractive hourly pay" in the text, and its apply button leads to the general Randstad sign-up page. `apply_url` is `null` for Swiss jobs, use `job_url`. Coordinates, tasks, offer and the consultant come with `includeDetails`.
- Swiss pages occasionally come back without their result block. The actor reads such a page up to 2 more times and only skips it when all tries fail (noted in the log).
- Only public listing data is returned. Pay is shown as Randstad publishes it and can be an estimate range.
- You are responsible for using the data lawfully, including the GDPR/DSGVO, national data-protection laws and unfair-competition rules for marketing and outreach.

### Support

Questions, feature requests or a field you miss: <contact@nanoscrape.com>.

Built by NanoScrape. Not affiliated with Randstad.

# Changelog

This Actor's version history is a separate document: https://apify.com/nanoscrape/randstad-jobs-scraper/changelog.md

# Actor input Schema

## `countries` (type: `array`):

Which Randstad sites to search: DE (Germany), CH (Switzerland), UK (United Kingdom). Each country runs every search keyword.

## `searchQueries` (type: `array`):

One or more keywords such as a job title or field. Each keyword runs as its own search in every country. Leave empty to list all jobs. Jobs found by several searches are returned once.

## `location` (type: `array`):

Optional. Only jobs whose city, region, location text or postal code start matches one of these places are returned (for example Berlin, Zurich, Manchester or 80). In Germany and the UK, a keyword plus a place is searched directly on Randstad (fast). Otherwise the jobs found by the search are filtered, so combine it with a keyword to keep the search narrow.

## `postedWithinDays` (type: `integer`):

Only return jobs posted within the last N calendar days (UTC, today included). Older jobs are dropped before they are charged. 0 = no limit.

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

Maximum number of jobs in total over all countries and searches. 0 = no limit.

## `maxResultsPerQuery` (type: `integer`):

Maximum number of jobs per country and keyword. 0 = no limit.

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

Also read each job page. Adds the full description as text and HTML, requirements, coordinates, consultant contact and expiry date; Germany and the United Kingdom add responsibilities, street, update dates, apply link and the exact country. Slower.

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

Optional Randstad search URLs, for example https://www.randstad.de/jobs/pflege/ or https://www.randstad.co.uk/jobs/nurse/. Each URL runs as one more search; page numbers in the URL are ignored.

## Actor input object example

```json
{
  "countries": [
    "DE"
  ],
  "searchQueries": [
    "nurse"
  ],
  "location": [],
  "postedWithinDays": 0,
  "maxResults": 100,
  "maxResultsPerQuery": 50,
  "includeDetails": false,
  "startUrls": []
}
```

# Actor output Schema

## `jobListings` (type: `string`):

Dataset containing the scraped Randstad jobs.

# 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 = {
    "countries": [
        "DE"
    ],
    "searchQueries": [
        "nurse"
    ],
    "location": [],
    "startUrls": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("nanoscrape/randstad-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 = {
    "countries": ["DE"],
    "searchQueries": ["nurse"],
    "location": [],
    "startUrls": [],
}

# Run the Actor and wait for it to finish
run = client.actor("nanoscrape/randstad-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 '{
  "countries": [
    "DE"
  ],
  "searchQueries": [
    "nurse"
  ],
  "location": [],
  "startUrls": []
}' |
apify call nanoscrape/randstad-jobs-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,nanoscrape/randstad-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/L3wlUmKsDgZwKgB2t/builds/zGpEcdOp0evSJZy0G/openapi.json
