The Bundesagentur für Arbeit (BA) Jobbörse is Germany's largest job board, with around a million live Stellenangebote. It is backed by a public REST API, often called the Arbeitsagentur Jobsuche API, that the official website and app use.
This guide covers how that API works, where it gets awkward, and a ready-made way to get clean job data as JSON from JavaScript, Python or plain HTTP.
The raw Jobsuche API in 30 seconds
The community-documented endpoint (see the bundesAPI/jobsuche-api project on GitHub) looks like this:
curl -H "X-API-Key: jobboerse-jobsuche" \ "https://rest.arbeitsagentur.de/jobboerse/jobsuche-service/pc/v4/jobs?was=Softwareentwickler&wo=Berlin&umkreis=25&size=50&page=1" It's free and needs no account, which is great. In practice you'll run into a few issues:
- Pagination limits: deep pages are capped, so big searches need splitting.
- Duplicates: the same job appears under several reference numbers or reposts.
- Search results are thin: full descriptions need a separate request per job (and sometimes the HTML detail page).
- Filters are cryptic: employment types, contract types and
veroeffentlichtseituse codes. - Schema changes: fields move between versions, and a missing array silently looks like "0 results".
If you need this data regularly (for a job board, recruiting leads or research), you end up building a small scraper with retries, dedupe and normalization.
The shortcut: German Jobs Scraper on Apify
German Jobs Scraper is a hosted Apify Actor that wraps the Arbeitsagentur Jobsuche API and returns normalized records:
- Keyword(s), city + radius, full-time/part-time, contract type, Homeoffice/remote, "published in the last N days"
- Optional exclusion of temporary-staffing agencies (Zeitarbeit)
- Full job descriptions included at no extra cost
- Salary (min/max, period), postal code, region, latitude/longitude
- Deduplication across reposts, with distinct roles kept separate
- Optional StepStone source (off by default, see limitations)
Pricing: $0.0015 per unique job delivered on the Free plan (down to $0.0007 on higher plans) plus a tiny start fee. Duplicates, filtered jobs and errors are free. 1,000 jobs cost about $1.50 or less.
Step 1: Get an Apify token
Sign up at apify.com (free plan with monthly credits), then copy your token from Settings → API & Integrations:
export APIFY_TOKEN=your_token Step 2: JavaScript example
npm install apify-client import { ApifyClient } from 'apify-client'; const client = new ApifyClient({ token: process.env.APIFY_TOKEN }); const run = await client.actor('batuweb/german-jobs-scraper').call({ keywords: ['Pflegefachkraft'], location: 'Hamburg', radiusKm: 25, maxResults: 100, publishedSinceDays: 7, employmentTypes: ['fulltime'], excludeTemporaryAgencies: true, includeDescription: true, }); const { items } = await client.dataset(run.defaultDatasetId).listItems(); for (const job of items.filter((j) => j.type === 'job')) { console.log(job.title, '|', job.company, '|', job.postalCode, job.location, '|', job.salaryMin ?? '-'); } Step 3: Python example
pip install apify-client pandas import os import pandas as pd from apify_client import ApifyClient client = ApifyClient(os.environ["APIFY_TOKEN"]) run = client.actor("batuweb/german-jobs-scraper").call(run_input={ "keywords": ["Data Engineer", "Data Scientist"], "location": "München", "radiusKm": 50, "maxResults": 200, "remoteOnly": False, }) jobs = [i for i in client.dataset(run["defaultDatasetId"]).iterate_items() if i.get("type") == "job"] df = pd.DataFrame(jobs) print(df[["title", "company", "location", "salaryMin", "salaryMax", "publishedDate"]].head(20)) df.to_csv("jobs_muenchen.csv", index=False) Step 4: Plain HTTP
curl -X POST \ "https://api.apify.com/v2/acts/batuweb~german-jobs-scraper/run-sync-get-dataset-items?token=$APIFY_TOKEN" \ -H "Content-Type: application/json" \ -d '{"keywords":["Elektriker"],"location":"Köln","radiusKm":30,"maxResults":50}' Datasets can also be downloaded as CSV or Excel via .../datasets/{id}/items?format=csv.
Output example
{ "type": "job", "title": "Java/JEE-Entwickler Fullstack (m/w/d)", "company": "ANG GmbH", "location": "Berlin", "postalCode": "10115", "region": "BERLIN", "latitude": 52.531976, "longitude": 13.386737, "salaryMin": 36000, "salaryMax": 54000, "salaryCurrency": "EUR", "salaryPeriod": "year", "employmentTypes": ["fulltime"], "contractType": "UNBEFRISTET", "remote": false, "publishedDate": "2026-10-16", "url": "https://www.arbeitsagentur.de/jobsuche/jobdetail/10001-1003859193-S", "source": "arbeitsagentur", "description": "..." } Use cases
- Niche job boards (nursing, trades, IT in one region) refreshed daily via Apify schedules
- Recruiting and B2B sales leads: companies hiring for a role are often buying related services
- Labour-market and salary research by region or occupation
- Personal job alerts: schedule a daily run with
publishedSinceDays: 1and send new jobs to email or Slack via webhook/n8n
Honest limitations
- Salary data is only present when the employer provides it (or as a StepStone estimate, flagged
salaryIsEstimate). - The BA API is unofficial in the sense of having no SLA. If it changes, the Actor needs an update. It's checked weekly.
- StepStone is optional and off by default. Its terms likely restrict automated access, so enable it only if you've checked that your use is permitted.
- Job ads can contain contact persons' names, so make sure your use complies with GDPR (DSGVO).
FAQ
Is the Arbeitsagentur API free? Yes, the underlying API is free and public. The Actor charges for the hosting, dedupe, descriptions and normalization.
Can I search the whole of Germany? Leave location empty. Big searches are paginated automatically up to maxResults.
Does it include Ausbildung/apprenticeships? Use offerType to choose between jobs, apprenticeships and other offer types.
Related tools
- Google Trends Scraper: compare search interest for job titles and skills across German states.
- SEO Audit Toolkit: technical SEO audit for your job board or career site.
Disclosure: I built the German Jobs Scraper Actor.
