# Ausbildung.de Scraper (`nanoscrape/ausbildung-de-scraper`) Actor

Scrape apprenticeships, dual studies and training places from Ausbildung.de, Germany's largest apprenticeship board. Returns title, company, profession, address, start date, duration, required school degree, description and apply link. Pay-per-result.

- **URL**: https://apify.com/nanoscrape/ausbildung-de-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

## Ausbildung.de Scraper: Apprenticeships and Dual Studies in Germany

Get apprenticeship, dual study and training vacancies from [Ausbildung.de](https://www.ausbildung.de/) as clean dataset rows. Search by profession keyword, city or postcode and radius, or paste a search page, a company job list or a vacancy page from the site. Each row has the title, employer, profession, address, start, duration, required school degree, full description and the link to apply.

### What you can do with it

- Build a list of employers that train apprentices in a city, profession or region, for recruiting, sales or market research.
- Track the training market over time: run it on a schedule and watch new vacancies, start dates and employers.
- Feed a job board, school career portal or alert service with current apprenticeship places.
- Compare how many places exist per profession, federal state or type of training (dual training, dual study, school-based training).

### What you get

One dataset row per vacancy. Vacancies found by several searches are returned once, and duplicates are removed before anything is charged, so the number of rows is the number of results you pay for.

| Field | Description |
|-------|-------------|
| `id`, `vacancy_public_id`, `job_posting_public_id` | The vacancy ids. `id` equals `vacancy_public_id` and is the dedup key |
| `title` | Vacancy title |
| `profession_title`, `profession_id`, `profession_slug` | Profession the vacancy trains for (for example Fachinformatiker/in) |
| `company`, `company_public_id`, `company_slug`, `company_legal_name`, `company_description` | Employer name and profile |
| `company_website`, `company_url`, `company_logo_url` | Employer site, employer page on Ausbildung.de, logo |
| `company_street`, `company_postal_code`, `company_city` | Employer headquarters |
| `company_rating`, `company_review_count`, `company_recent_rating`, `company_recent_review_count`, `top_rated_employer` | Employer reviews on Ausbildung.de |
| `subsidiary_id`, `subsidiary_public_id`, `subsidiary_name`, `subsidiary_logo_url` | The branch that offers the vacancy |
| `location`, `street`, `postal_code`, `city`, `region`, `country` | Where the training takes place. `region` is the federal state, `country` is the ISO-2 code of the posting address (DE for the German site; read from the vacancy page, never guessed from the employer) |
| `latitude`, `longitude` | Coordinates of the training location |
| `apprenticeship_type`, `apprenticeship_type_slug` | Type of training: Klassische duale Berufsausbildung, Duales Studium, Schulische Ausbildung, Schulische und duale Ausbildung, Weiterbildung |
| `duration` | Length of the training as written on the listing (for example `3 Jahre`) |
| `expected_graduation`, `expected_graduation_slug` | School degree the employer expects (Hauptschulabschluss, Mittlere Reife, Fachabitur, Abitur, Keine Angabe) |
| `starts_at`, `starts_at_date` | Earliest start as written (`14.12.2026` or `Nach Absprache`) and as ISO date when it is a date |
| `application_deadline` | Application deadline as written (for example `Laufend`) |
| `employment_type`, `education_requirements`, `industry` | Employment type, education requirement and industry of the employer |
| `salary_min`, `salary_max`, `salary_currency`, `salary_period`, `salary_by_year` | Pay, only when the posting states it. `salary_by_year` lists the pay per training year (`year`, `amount`, `currency`, `period`) when the description publishes it; `salary_min`/`salary_max` then span those figures. Nothing is estimated, `period` is `month` only when the text says so. `salary_currency` is an ISO code and only set next to an amount |
| `description`, `description_text` | Full description as HTML and as plain text |
| `date_posted`, `created_at`, `updated_at` | Posting date and listing timestamps (ISO 8601) |
| `image_url`, `gallery_images`, `video_url` | Header image, gallery and video of the vacancy |
| `job_url` | Vacancy page on Ausbildung.de |
| `apply_url`, `apply_email`, `apply_postal_address`, `application_reference_number`, `application_options` | How to apply |
| `direct_application`, `vacancy_count`, `has_multiple_locations` | Direct application on Ausbildung.de, and how many identical vacancies the listing stands for |
| `salesforce_category`, `tags`, `ba_booking` | Listing flags the site publishes (`tags` only on company job lists) |
| `facts` | Every entry of the "Auf einen Blick" box |
| `details_fetched` | `true` when the vacancy page was read for the row |
| `source_query`, `scraped_at` | Keyword or URL that found the vacancy, scrape time (UTC) |

Fields marked "needs the vacancy page" in the dataset view are empty when `includeDetails` is off. Arrays are always arrays (`[]` when empty) and `facts` is always an object.

A listing that an employer offers at many branches (for example a retail chain) is returned once, for the branch the site shows first. `vacancy_count` tells how many branches offer it.

### Input

| Field | Type | Description |
|---|---|---|
| `searchQueries` | string\[] | Keywords or professions, one search each (default `Pflege`) |
| `location` | string | City or postcode, combined with every keyword. Empty = all of Germany |
| `radius` | integer | Radius in kilometres around the location, 1 to 200 (default 25) |
| `startUrls` | string\[] | Ausbildung.de search page URLs, company job list URLs, or single vacancy page URLs |
| `includeDetails` | boolean | Read each vacancy page for description, address, dates and apply link (default `true`) |
| `maxResults` | integer | Total cap across all searches (default 100, 0 = no limit) |
| `maxResultsPerQuery` | integer | Cap per keyword or start URL (default 100, 0 = no limit) |

#### Example: keyword and city

```json
{
  "searchQueries": ["Fachinformatiker", "Pflege"],
  "location": "Berlin",
  "radius": 50,
  "includeDetails": true,
  "maxResults": 200,
  "maxResultsPerQuery": 100
}
```

#### Example: a search page

```json
{
  "startUrls": [
    "https://www.ausbildung.de/suche/?search=pflege%7CBerlin&radius=50"
  ],
  "maxResults": 100
}
```

#### Supported URLs

- Search page: `https://www.ausbildung.de/suche/?search=<keyword>|<city>&radius=<km>`. Keyword, location and radius are used; other filters in the address are ignored.
- Vacancy page: `https://www.ausbildung.de/stellen/<vacancy>/`. The single vacancy is returned (useful to check if a vacancy is still online).
- Company job list: `https://www.ausbildung.de/unternehmen/<company>/stellen/`. Returns every open vacancy of the employer. Rows from a company list carry the list fields (title, profession, location, start, duration, school degree, application options, tags) and, with `includeDetails` on, the full vacancy page data.

### Sample output

```json
{
  "id": "0ca2f100-3375-4eec-9e39-08c593963750",
  "vacancy_public_id": "0ca2f100-3375-4eec-9e39-08c593963750",
  "job_posting_public_id": "c68a0cc2-9234-4768-8a1b-3d43648ed715",
  "title": "Duales Pflegestudium - Bachelor of Nursing (m/w/d) in Berlin-Pankow Oktober 2027",
  "profession_title": "Duales Studium Pflege",
  "company": "Caritas Gesundheit Berlin gGmbH",
  "company_website": "https://karriere.caritas-gesundheit.de/",
  "location": "13187 Berlin",
  "street": "Breite Str. 46/47",
  "postal_code": "13187",
  "city": "Berlin",
  "region": "Berlin",
  "country": "DE",
  "latitude": 52.5717807,
  "longitude": 13.4140338,
  "apprenticeship_type": "Duales Studium",
  "duration": "3–3,5 Jahre",
  "expected_graduation": "Abitur",
  "starts_at": "Nach Absprache",
  "application_deadline": "Laufend",
  "employment_type": "FULL_TIME",
  "industry": "Gesundheit",
  "description": "<p>Du willst Menschen pflegen und gleichzeitig studieren? ...</p>",
  "date_posted": "2026-10-26T14:39:09.295Z",
  "job_url": "https://www.ausbildung.de/stellen/duales-pflegestudium-bachelor-of-nursing-m-w-d-in-berlin-pankow-oktober-2027-bei-caritas-gesundheit-berlin-ggmbh-in-berlin-0ca2f100-3375-4eec-9e39-08c593963750/",
  "apply_url": "https://caritasgesundheitberlinggmbh.recruitee.com/o/duales-pflegestudium-bachelor-of-nursing-mwd-in-berlin-pankow-oktober-2027",
  "application_options": ["online"],
  "direct_application": true,
  "vacancy_count": 1,
  "gallery_images": ["https://www.ausbildung.de/uploads/image/69/694af318-b7e4-4534-9843-cd40cf351e7c/Motiv_2.jpg"],
  "details_fetched": true,
  "source_query": "pflege",
  "scraped_at": "2026-11-04T12:00:00Z"
}
```

The sample is shortened. Every row has all fields listed above.

You are responsible for using the data lawfully, including the GDPR/DSGVO, national data-protection laws and unfair-competition rules (e.g. UWG) for marketing and outreach.

### Pricing (Pay-per-event)

| Event | Description | When it fires |
|---|---|---|
| `apify-actor-start` | Run start fee | Once per run. $0.001 |
| `job-result` | One vacancy row in the dataset | Once per row pushed. $0.001 per result ($1.00 per 1,000 results) |

The start fee is charged on top of the results. Duplicates are never charged. Set a maximum total charge on the run to stop at a budget: the run ends cleanly when it is reached, and the status message says how many jobs were 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 `ausbildung-de-scraper` to find dual study places in Hamburg for IT and list company, start date and apply link."

> "Use `ausbildung-de-scraper` for the newest 50 Pflege apprenticeships within 30 km of Munich and summarise the employers."

Configure the MCP server with the Ausbildung.de Scraper preconfigured at `mcp.apify.com?tools=nanoscrape/ausbildung-de-scraper`:

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=nanoscrape/ausbildung-de-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

**How do I search a whole city?**
Set `location` to the city (or postcode) and add one or more keywords. A location without keyword is allowed, but it is not verified for every location: if it returns nothing, add a keyword.

**How many vacancies can one search return?**
Ausbildung.de reports at most 10,000 results per search. For larger sets split the work into narrower searches: one keyword per profession, or one search per city. The run status says when a search hit this limit.

**Why does a row have no description or street?**
Those fields come from the vacancy page. Turn `includeDetails` on (default). If a single vacancy page cannot be read, the row is still delivered with the search fields and `details_fetched` is `false`.

**Is the start date always a date?**
No. Many listings say `Nach Absprache` (by arrangement). `starts_at` keeps the text, `starts_at_date` is only set when the listing has a date.

**Why is one vacancy returned once when the employer has many branches?**
Retail and service chains publish the same vacancy for hundreds of branches. The result is returned once, with `vacancy_count` showing how many branches offer it.

### Limits

- Germany only (`country` is always `DE`).
- Up to 10,000 results per search (see FAQ).
- Searches use keyword, location and radius. Other filters of the site (type of training, school degree, sorting) are not applied.
- Results come 20 at a time, and a run with details reads one extra page per vacancy, so use `maxResults` to size the run and keep it fast.

### Related actors

- [Randstad Jobs Scraper](https://apify.com/nanoscrape/randstad-jobs-scraper): staffing and temp-job vacancies from Randstad, a complement to apprenticeship listings.

### Support

- **Questions or issues?** Email contact@nanoscrape.com, we typically reply within 6 hours, or open a ticket in the Issues tab on the actor page.
- **Need a scraper for a job board we don't cover yet?** Email contact@nanoscrape.com with the target site and your rough usage volume.

***

*Built by [NanoScrape](https://apify.com/nanoscrape). No affiliation with Ausbildung.de.*

# Changelog

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

# Actor input Schema

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

Professions or keywords, for example Pflege, Fachinformatiker or Koch. Each keyword runs as its own search; vacancies found by several searches are returned once. Leave empty to use a location or start URLs instead.

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

City or postcode, for example Berlin or 80331. Combined with every keyword. Leave empty to search all of Germany.

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

Search radius around the location in kilometres (1 to 200). Only used together with a location.

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

Ausbildung.de search page URLs (https://www.ausbildung.de/suche/?...; keyword, location and radius are used), company job list pages (https://www.ausbildung.de/unternehmen/<company>/stellen/) or single vacancy page URLs (https://www.ausbildung.de/stellen/...).

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

Read each vacancy page for the description, street address, coordinates, posting date, requirements, apply link and more. Turn off for a faster run with the search result fields only.

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

Maximum number of vacancies in the dataset across all searches. 0 = no limit.

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

Maximum number of vacancies per keyword or start URL. 0 = no limit. The site reports at most 10000 results per search; use narrower searches (city, keyword) beyond that.

## Actor input object example

```json
{
  "searchQueries": [
    "Pflege"
  ],
  "location": "Berlin",
  "radius": 25,
  "startUrls": [],
  "includeDetails": true,
  "maxResults": 100,
  "maxResultsPerQuery": 100
}
```

# Actor output Schema

## `vacancies` (type: `string`):

Dataset with the scraped Ausbildung.de vacancies.

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

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

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

```

## MCP server setup

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