# Arbeitsagentur Scraper - German Jobs & Employers (`scrapesage/arbeitsagentur-scraper`) Actor

Scrape the Arbeitsagentur Jobboerse, Germany’s federal job board with 700,000+ live positions: title, employer, address with coordinates, federal state, working-time model, contract duration, entry date and the full description. Filter by term, city, radius, offer type and more.

- **URL**: https://apify.com/scrapesage/arbeitsagentur-scraper.md
- **Developed by:** [Scrape Sage](https://apify.com/scrapesage) (community)
- **Categories:** Jobs, Lead generation, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.65 / 1,000 job listings

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

## Arbeitsagentur Scraper - German Jobs & Employers

Scrape the **[Arbeitsagentur Jobbörse](https://www.arbeitsagentur.de/jobsuche)** - the German Federal Employment Agency's official job board and **the largest job database in Germany, with over 700,000 live positions**. Every row is one job with the **employer**, the **address the employer published** (city and postcode on virtually every row, plus street and map coordinates wherever the employer gave one), the federal state, the **working-time model**, the **contract duration**, the **start date**, the publication date and the **complete job description**.

No login, no cookies, no browser, no API key. The Jobbörse renders server-side and ships its results as structured data, and each job page carries a schema.org `JobPosting`, so this actor reads real data structures rather than guessing at CSS classes.

### Why this Arbeitsagentur scraper?

- **Map coordinates wherever the employer published an address.** `latitude` / `longitude` plus a parsed `street`, `postalCode`, `city` and `region` - so the output drops straight into a map, a radius calculation or a territory plan. Coverage depends on the profession: measured 2026-08-31, engineering roles carried coordinates on 88% of results and retail roles on 8%, because retail is largely posted by central HR without a site address. `withCoordinatesOnly` keeps just the ones that have them, and city + postcode are ~100% either way.
- **The whole German market, not just white-collar.** The Jobbörse is where employers are legally encouraged to post, so it reaches trades, care, logistics and public-sector roles that never appear on commercial boards.
- **Apprenticeships and dual study places as a first-class filter.** `offerTypes` covers employment, **Ausbildung**, internships and self-employment - a distinct, high-demand market most scrapers ignore entirely.
- **Exclude staffing agencies with one switch.** `excludeTempAgencies` removes Zeitarbeit postings, which are roughly a third of the board - the single most requested filter for anyone doing employer research.
- **Employer profile and logo** linked back to the employer's own Jobbörse page, for every employer that has one (agency-posted roles do not).
- **Real pick-lists.** Offer type, working-time model, contract duration, publication window, radius, federal state and sort order are all dropdowns with the Jobbörse's own values, so a filter can never silently fail to apply.
- **Monitor mode** returns only jobs new since your last run.

### Use cases

- **Recruitment & staffing** - track which employers are hiring, in which towns, for which professions.
- **Labour-market research** - full-time vs part-time, permanent vs fixed-term, and hiring volume by federal state or profession field.
- **Lead generation** - an employer posting repeatedly is growing. Employer name, profile link and full address on every row.
- **Relocation & workforce planning** - `withCoordinatesOnly` plus latitude/longitude makes "everything within 30 km of this plant" a one-line filter.
- **Job aggregators & AI agents** - a structured, machine-readable feed of Germany's biggest job database.

### How to use

1. Enter one or more **search terms** (`entwickler`, `pflegefachkraft`, `elektroniker`...), **or** a **location** and radius, **or** a **profession field** to sweep a whole sector - **or** paste Jobbörse search URLs straight from your browser.
2. Narrow with the offer-type, working-time, contract, publication-window and federal-state pick-lists.
3. Set **Maximum jobs to return** and run.

### Input

```json
{
    "searchTerms": ["entwickler"],
    "location": "Berlin",
    "radiusKm": 25,
    "offerTypes": ["1"],
    "workingTimes": ["vz", "ho"],
    "excludeTempAgencies": true,
    "publishedWithinDays": 7,
    "maxResults": 200
}
```

Everything is optional and combines with **AND**:

- **searchTerms** - one term per line; each is searched separately, results merged and de-duplicated.
- **startUrls / urlsFromFile** - paste Jobbörse search URLs (all filters in them are kept) or single job URLs. Type them, upload a file, or link a remote `.txt` / `.csv` / Google Sheet.
- **location + radiusKm** - a city or postcode plus a radius from 0 to 200 km.
- **offerTypes** - Employment · **Apprenticeship & dual study** · Internship & trainee · Self-employment.
- **workingTimes** - Full time · Part time · **Home office / Telearbeit** · Shift, night or weekend · Mini-job.
- **contractType** - permanent or fixed term. **publishedWithinDays** - today, 1, 3, 7, 14 or 28 days.
- **excludeTempAgencies** - drop Zeitarbeit postings. **accessibleOnly** - only jobs open to applicants with a disability.
- **professionField** - a Jobbörse Berufsfeld such as `Informatik` or `Pflege`, to sweep a whole sector.
- **cities / postalCodePrefixes / regions / employerQuery / titleQuery / excludeKeywords / maxDaysOld / permanentOnly / withCoordinatesOnly** - tighten the result set after the search.
- **includeDescription** (on by default) + **maxDescriptionRecords**, **monitorMode / monitorKey**, **maxResults**, **proxyConfiguration**.

> **Tip:** run with no term, location or URL and you get a small 25-job sample rather than a sweep of the whole board, so an exploratory or agent-issued call is never an expensive surprise.

### Output

Every row is one job. The dataset ships four ready-made **views**: **Jobs**, **Employers**, **Map-ready** and **Newest first**.

```json
{
    "recordType": "job",
    "referenceNumber": "10001-1003564427-S",
    "title": "Entwickler (m/w/d)",
    "url": "https://www.arbeitsagentur.de/jobsuche/jobdetail/10001-1003564427-S",
    "employer": "Lopep GmbH",
    "employerProfileUrl": "https://web.arbeitsagentur.de/vermittlung/ag-darstellung-ui/anzeigen/HNZ...",
    "employerLogo": "https://rest.arbeitsagentur.de/vermittlung/ag-darstellung-service/ct/v1/arbeitgeberlogo/sharing/HNZ...",
    "offerType": "ARBEIT",
    "offerTypeEn": "job",
    "profession": "IoT-Entwickler/in",
    "allProfessions": ["IoT-Entwickler/in", "Softwareentwickler/in"],
    "street": "Varenseller Str. 77C",
    "postalCode": "33397",
    "city": "Rietberg",
    "region": "North Rhine-Westphalia",
    "regionCode": "NORDRHEIN_WESTFALEN",
    "latitude": 51.8545142,
    "longitude": 8.4467158,
    "fullAddress": "Varenseller Str. 77C, 33397 Rietberg",
    "fullTime": true,
    "partTime": true,
    "shiftNightWeekend": false,
    "workingTimeModels": ["Vollzeit", "Teilzeit"],
    "contractDuration": "BEFRISTET",
    "contractDurationEn": "fixed-term",
    "isPermanent": false,
    "temporaryUntil": "2026-09-01",
    "entryDate": "2026-08-21",
    "publishedAt": "2026-08-19",
    "daysSincePublished": 12,
    "description": "…the full job text…",
    "employmentTypes": ["FULL_TIME", "PART_TIME", "TEMPORARY"],
    "directApply": true,
    "immediateStart": true
}
```

German source values are kept exactly as published (`contractDuration`, `offerType`, `region`) **and** given an English companion (`contractDurationEn`, `offerTypeEn`, `region`), so the data is usable either way.

#### What to expect (field coverage)

Measured on a live run (search `entwickler`, 8 newest jobs with descriptions, 2026-08-31):

| Field group | Coverage |
|---|---|
| Title, URL, employer, employer profile & logo, reference number, offer type, working-time model, contract, entry date, published date | **100%** |
| Full description (with `includeDescription` on) | **100%** |
| City, postcode, region | ~100% |
| Latitude / longitude | **88% for engineering roles, 8% for retail** - it depends on whether the employer published an address |
| Street address | 48% for engineering roles, 0% for retail - same reason |
| Employer profile & logo | 100% for engineering roles, 0% for retail (agency-posted roles have no employer profile) |
| `temporaryUntil` | only on fixed-term jobs that name an end date |
| Salary | German job ads rarely publish pay: absent on ~100% of postings. Not a scraping gap - a market norm |

A blank street means the employer published only a town, and a blank `payInfo` means the Jobbörse literally recorded "keine Angaben" - returned as an honest `null` rather than a placeholder string. **Coverage of address-derived fields varies by profession, not by run quality**: it was verified on 2026-08-31 that this actor emits exactly what the source published, with a 0-point gap on every field.

### Monitor mode - only newly posted jobs

Turn on **`monitorMode`** and the actor remembers every job it has already returned and emits **only new ones** on the next run, each tagged `monitorEvent: "new"`. It is **orthogonal to [Apify Schedules](https://docs.apify.com/platform/schedules)**: the Schedule sets the cadence, monitor mode decides what is new. Use a distinct `monitorKey` per saved watch.

### A note on speed

The Jobbörse rate-limits bursts, so this actor deliberately paces its requests and backs off when the source pushes back. A throttled response is treated as transient - the run reports it and never fails because of it.

### Automate & schedule

[Schedules](https://docs.apify.com/platform/schedules) for cadence, [webhooks](https://docs.apify.com/platform/integrations/webhooks) to push each finished run onward, and one-click [integrations](https://docs.apify.com/platform/integrations) for Google Sheets, Slack, Airtable, Make and Zapier.

### Integrate with any app

Results are available as **JSON, CSV, Excel, XML and RSS** through the [Dataset API](https://docs.apify.com/api/v2#/reference/datasets/item-collection), and the actor can be started from the [API](https://docs.apify.com/api/v2#/reference/actors/run-collection/run-actor) or any [client library](https://docs.apify.com/api/client/js/).

### Use with AI assistants (MCP)

This actor is exposed through the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp), so Claude, ChatGPT, Cursor and any other MCP client can call it as a tool - "find permanent full-time nursing jobs within 25 km of Leipzig posted this week" becomes one tool call.

### Agent-ready: autonomous payments (x402 & Skyfire)

This actor is **agent-ready** — AI agents can discover it, run it, and **pay for it autonomously**, with no Apify account and no human in the loop. It uses [pay-per-event](https://docs.apify.com/platform/actors/publishing/monetize/pay-per-event) pricing and [limited permissions](https://docs.apify.com/platform/actors/development/permissions), so it qualifies for Apify's agentic-payment standards:

- **[x402](https://docs.apify.com/platform/integrations/x402)** — an open, HTTP-native payment protocol. Agents pay per run in USDC on the Base network directly through the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp) — no account, no API key.
- **[Skyfire](https://docs.apify.com/platform/integrations/skyfire)** — agent-to-service payments for fully autonomous AI-agent workflows.

Building an AI agent, MCP tool, or autonomous data pipeline? This scraper is ready to plug in and pay as it goes.

### More job & hiring scrapers from scrapesage

- [InfoJobs Scraper](https://apify.com/scrapesage) - Spain's largest job board, with structured salary ranges
- [ZipRecruiter Scraper](https://apify.com/scrapesage) - US job listings
- [Remote OK / We Work Remotely / Himalayas](https://apify.com/scrapesage) - remote-first boards
- Browse everything at [apify.com/scrapesage](https://apify.com/scrapesage)

### FAQ

**Do I need an account or API key?**
No. This reads only public search and job pages.

**How many jobs can I get?**
The Jobbörse holds more than 700,000 live positions. A single search is paged until `maxResults` is reached; for very large sweeps split the work by profession field, federal state or city.

**Is the description included?**
Yes, by default. Descriptions live on each job's own page, so fetching them costs one extra request and one extra event per job. Turn `includeDescription` off for a fast listing-only export.

**Are the results in German?**
Titles, employers and descriptions are in German exactly as published. Region names, offer types and contract durations also come back with English companions so you can filter without knowing German.

**What is Zeitarbeit and why would I exclude it?**
Zeitarbeit is temporary staffing. Agencies post a large share of the board, often re-posting the same role. `excludeTempAgencies` leaves only direct employers.

**Why do some jobs have no street address?**
Employers may publish only a town. The city, postcode and coordinates are still there in almost every case.

### Need help?

Open an issue on the actor's **Issues** tab with your input and the run ID and it will be looked at.

***

*This actor collects publicly available job postings for research, recruitment and labour-market analysis. It is not affiliated with, endorsed by, or sponsored by the Bundesagentur für Arbeit.*

# Actor input Schema

## `searchTerms` (type: `array`):

What to search for, one term per line - a job title, a profession or a skill (for example <code>entwickler</code>, <code>pflegefachkraft</code>, <code>elektroniker</code>). Each term is searched separately and the results are merged and de-duplicated. Leave it empty and set a location or profession field instead to sweep everything in that area.

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

Paste Jobboerse search URLs straight from your browser and every filter already in them is kept while the actor pages through the results. Single job URLs (.../jobsuche/jobdetail/...) also work. You can type them, upload a file, or link a remote text file.

## `urlsFromFile` (type: `string`):

A block of Jobboerse URLs, one per line - or a single link to a .txt / .csv file, a Google Sheet or a Drive file containing them. Blank lines, # comments, quotes and extra CSV columns are tolerated and duplicates are removed.

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

A German city or postcode to search around, for example <code>Berlin</code>, <code>Munchen</code> or <code>80331</code>. Combine with the radius below.

## `radiusKm` (type: `string`):

How far around the location to search, in kilometres. Only applies when a location is set. These are the radii the Jobboerse itself offers.

## `offerTypes` (type: `array`):

What kind of position to return. The Jobboerse carries far more than salaried jobs - apprenticeships and dual study places are a large, distinct market.

## `workingTimes` (type: `array`):

Filter by how the work is scheduled. <code>Home Office</code> is the Jobboerse's own remote/telework flag.

## `contractType` (type: `string`):

Fixed-term or permanent, as the employer declared it.

## `publishedWithinDays` (type: `string`):

Only jobs first published in the last N days. These are the windows the Jobboerse itself supports, so an unsupported value cannot be sent.

## `excludeTempAgencies` (type: `boolean`):

Staffing agencies post a large share of the board. Turn this on to see only direct employers - it typically removes about a third of results.

## `accessibleOnly` (type: `boolean`):

The Jobboerse flags positions explicitly open to applicants with a disability.

## `professionField` (type: `string`):

A Jobboerse profession field to sweep, for example <code>Informatik</code>, <code>Pflege</code>, <code>Elektrotechnik</code> or <code>Softwareentwicklung und Programmierung</code>. Use it instead of a search term to cover a whole sector.

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

Newest publication is best for monitoring and lead lists; relevance is best for a targeted search.

## `cities` (type: `array`):

Exact city names, applied after the search - useful when a radius pulls in neighbouring towns you do not want.

## `postalCodePrefixes` (type: `array`):

One or more leading digits of a German postcode, for example <code>10</code> for Berlin or <code>80</code> for Munich.

## `regions` (type: `array`):

Filter by Bundesland after the search. Every job record carries both the raw code and a readable English name.

## `employerQuery` (type: `string`):

Keep only jobs whose employer name contains this text (case-insensitive).

## `titleQuery` (type: `string`):

Keep only jobs whose title contains this text (case-insensitive) - handy for tightening a broad profession sweep.

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

Drop any job whose title, employer or description contains one of these terms.

## `maxDaysOld` (type: `integer`):

Drop jobs first published more than this many days ago. Finer than the Published-within windows and can be combined with them.

## `permanentOnly` (type: `boolean`):

Keep only jobs the employer declared unbefristet (permanent).

## `withCoordinatesOnly` (type: `boolean`):

Keep only jobs that carry latitude and longitude - useful when the output feeds a map or a distance calculation.

## `includeDescription` (type: `boolean`):

On by default. The search results carry everything except the description text, so each job's own page is fetched to add the full description plus employment types, contract length, direct-apply and immediate-start flags. Costs one extra request and one extra event per job - turn it off for a fast, cheap listing-only export.

## `maxDescriptionRecords` (type: `integer`):

A ceiling on how many job pages are fetched, so a large sweep cannot become a large bill by accident. Jobs beyond it are still returned, just without the description.

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

Stops the run once this many jobs have been collected. The Jobboerse holds more than 700,000 live positions, so leave room when sweeping a whole profession field.

## `monitorMode` (type: `boolean`):

Remembers every job already returned under this monitor key and emits only new ones, each tagged monitorEvent: "new". Pair it with an Apify Schedule for a clean feed of newly posted positions.

## `monitorKey` (type: `string`):

Names the memory used by monitor mode. Use a different key per saved watch (one per city, per profession, per client) so separate monitors never share state.

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

The Jobboerse serves fine through Apify's shared datacenter proxy, which is the default. It does rate-limit bursts, so the actor paces itself; switch to residential only if the run reports refused requests.

## Actor input object example

```json
{
  "searchTerms": [
    "entwickler"
  ],
  "radiusKm": "",
  "offerTypes": [
    "1"
  ],
  "contractType": "",
  "publishedWithinDays": "",
  "excludeTempAgencies": false,
  "accessibleOnly": false,
  "sortBy": "veroeffdatum",
  "permanentOnly": false,
  "withCoordinatesOnly": false,
  "includeDescription": true,
  "maxDescriptionRecords": 200,
  "maxResults": 100,
  "monitorMode": false,
  "monitorKey": "default",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

All scraped Arbeitsagentur job listings in the default dataset.

# 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 = {
    "searchTerms": [
        "entwickler"
    ],
    "offerTypes": [
        "1"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapesage/arbeitsagentur-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 = {
    "searchTerms": ["entwickler"],
    "offerTypes": ["1"],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("scrapesage/arbeitsagentur-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 '{
  "searchTerms": [
    "entwickler"
  ],
  "offerTypes": [
    "1"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call scrapesage/arbeitsagentur-scraper --silent --output-dataset

```

## MCP server setup

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