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 veroeffentlichtseit use 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: 1 and 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

Disclosure: I built the German Jobs Scraper Actor.