Germany Jobs API - Arbeitsagentur Job Board Scraper avatar

Germany Jobs API - Arbeitsagentur Job Board Scraper

Pricing

from $1.00 / 1,000 job rows

Go to Apify Store
Germany Jobs API - Arbeitsagentur Job Board Scraper

Germany Jobs API - Arbeitsagentur Job Board Scraper

Search the German Federal Employment Agency (Arbeitsagentur) job board by keyword, city and radius: employer, location, start date, working time and the posting link. Official API, no key. dryRun prices a run before you pay; charged only for jobs delivered.

Pricing

from $1.00 / 1,000 job rows

Rating

0.0

(0)

Developer

Natalia Ivanova

Natalia Ivanova

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

12 days ago

Last modified

Share

Arbeitsagentur Jobs Scraper - Germany Jobs API

Get job openings from the largest job board in Germany — the public search of the Bundesagentur für Arbeit (the German Federal Employment Agency) — as one clean, structured dataset. Around two million postings are listed there at any time, from software engineering to nursing, logistics, crafts and apprenticeships, including thousands of jobs that never appear on commercial boards because employers publish them only through the agency.

One run gives you a normalized row per posting: title, employer, city, state, postal code, coordinates, home-office flag, working time, occupation, publication and start date, and the canonical link to the posting on arbeitsagentur.de. The field names match the other job Actors in this collection, so a job pipeline that already reads one of them reads this one without changes.

What you can do with the Arbeitsagentur jobs scraper

  • Job alerts for Germany — run it every morning with publishedSince: 1 and your keywords, then push new matches to Slack, email or a Google Sheet through Apify integrations.
  • Niche job boards and newsletters — fill a regional or sector site ("IT jobs in Bayern", "Pflege in Hamburg") with fresh listings that link back to the source.
  • Labour-market research — count openings per occupation, state or radius over time; the board covers the whole country, including rural areas no commercial board indexes well.
  • Recruiting and competitor research — see which employers are hiring for a given occupation right now, and which of them use temp agencies.
  • Relocation and salary research — coordinates and postal codes on every row make map and commute analysis straightforward.
  • AI agents — let an agent query the German labour market through the Apify MCP server (see below).

How this German job board is read

There is no documented public API for this source, and the Actor does not pretend otherwise. The agency's job search is an Angular application rendered on the server, and the server embeds the raw search response in the page as JSON. The Actor reads that JSON block rather than parsing rendered HTML, so it does not depend on the page layout, class names or wording:

StepEndpointWhat it gives
Searchwww.arbeitsagentur.de/jobsuche/suche?was=…&wo=…&umkreis=…&page=…25 postings per page with employer, location, dates, working time, occupation
Descriptionrest.arbeitsagentur.de/jobboerse/jobsuche-service/pc/v4/jobdetails/<id>the full posting text plus the temp-agency and private-recruiter flags

The description endpoint is the one the portal itself calls, and it is used only when includeDescription is on — one extra request per posting. No login, no browser, no cookies.

This is a public web source, not a contract: it can change or start refusing automated traffic at any time. The Actor is built for that — it pauses between requests, retries with backoff, handles HTTP 429 politely, and on an HTTP 403 switches to Apify Proxy when one is configured and says so in the log. The run summary reports how many requests were made and how many were refused.

Jobs in Germany scraper: input

FieldTypeDefaultDescription
keywordsarray["python"]Required. One search per term; results are merged and deduplicated. German terms match best.
locationstring—City, postal code or district (Berlin, 10247, München). Empty = all of Germany.
radiusKminteger25Radius around location, 0–200 km. Ignored without a location.
publishedSinceinteger—Published within this many days. Empty = no date filter.
employmentTypestringanyany, work, apprenticeship, internship, self_employment.
workingTimearray[]Any of full_time, part_time, shift_night_weekend, home_office, minor_employment.
temporaryWorkbooleantruefalse leaves out temp-agency (Zeitarbeit) postings.
includeDescriptionbooleanfalseFetch the full posting text — one extra request per posting.
maxItemsPerQueryinteger500Cap per keyword, so one broad term cannot consume the run.
maxItemsinteger1000Cap in total, after deduplication.
proxyConfigurationobjectoffEnable if the source starts refusing Apify's IPs.

keywords is required: an API or scheduler call that omits it fails fast instead of running an empty search.

Example — full-time developer jobs within 50 km of Munich, published in the last week, no temp agencies, with descriptions:

{
"keywords": ["softwareentwickler", "python"],
"location": "München",
"radiusKm": 50,
"publishedSince": 7,
"employmentType": "work",
"workingTime": ["full_time"],
"temporaryWork": false,
"includeDescription": true,
"maxItems": 500
}

Output

One dataset item per posting:

{
"id": "10001-1003841159-S",
"refnr": "10001-1003841159-S",
"title": "Junior Linux Systemadministrator (m/w/d)",
"company": "Heinlein Support GmbH",
"employer": "Heinlein Support GmbH",
"employerAnonymous": false,
"employerIdHash": "X8S-ikw6U8eE45OGfITtqg-HwamcRSqEAanh8aZsHeA=",
"location": "Berlin, Berlin, Germany",
"city": "Berlin",
"region": "Berlin",
"postalCode": "10119",
"country": "Germany",
"latitude": 52.533504,
"longitude": 13.410531,
"otherLocations": [],
"remote": true,
"homeOfficeType": "NACH_VEREINBARUNG",
"employmentType": "full_time",
"workingTime": ["full_time", "part_time_morning", "home_office"],
"offerType": "ARBEIT",
"offerTypeLabel": "Job",
"contractDuration": "UNBEFRISTET",
"contractMonths": null,
"contractUntil": null,
"occupation": "IT-Systemadministrator/in",
"occupationGroup": ["IT-Systemadministrator/in", "IT-Administrator/in"],
"publishedAt": "2026-10-12",
"firstPublishedAt": "2026-10-12",
"updatedAt": "2026-10-12T11:56:51Z",
"startDate": "2026-10-12",
"salaryMin": null,
"salaryMax": null,
"salaryCurrency": null,
"salaryPeriod": null,
"salaryInfo": null,
"isTemporaryWork": false,
"isPrivateRecruiter": false,
"suitableForCareerChangers": false,
"distanceKm": 3,
"url": "https://www.arbeitsagentur.de/jobsuche/jobdetail/10001-1003841159-S",
"externalUrl": "https://www.yourfirm.de/job/detail/YF-48852/",
"description": "Du suchst Abwechslung statt Alltagsroutine? …",
"source": "arbeitsagentur",
"sourceName": "Bundesagentur für Arbeit",
"attribution": "Originally published on arbeitsagentur.de: https://www.arbeitsagentur.de/jobsuche/jobdetail/10001-1003841159-S",
"scrapedAt": "2026-10-12T11:21:31Z"
}

employmentType uses the shared vocabulary of this collection (full_time, part_time, temporary, freelance, internship, other), while offerType keeps the board's own category. description, isTemporaryWork, isPrivateRecruiter and externalUrl are filled only when includeDescription is on; a null flag means the posting does not state it. Postings published under a Chiffre number have no employer name: employerAnonymous is true and company reads Confidential (Chiffre).

The OUTPUT record of the key-value store holds the run summary: jobs pushed and matched, duplicates dropped, requests made, refused responses, whether the proxy was used, and the per-keyword counts.

Personal data, attribution and affiliation

  • No personal data is collected. Postings sometimes name a contact person with a phone number or e-mail address. The Actor never reads or stores those: contact fields are stripped from every record before it is normalized, and only employer and vacancy data reaches the dataset. Nothing in the output identifies a private individual.
  • The data belongs to the Bundesagentur für Arbeit and the employers who published it. Every row keeps url, source, sourceName and an attribution line — please keep them when you display or republish the data.
  • This Actor is not affiliated with, endorsed by or operated by the Bundesagentur für Arbeit. It is an independent tool that reads a public job search, and the agency's terms of use for its portal apply to whatever you do with the results.

Use cases

Scrape Arbeitsagentur jobs by keyword

The plain case: a keyword, a city, a radius. Everything else defaults.

{ "keywords": ["python"], "location": "Berlin", "radiusKm": 25 }

Daily job alert for Germany

Yesterday's postings only, several occupations in one run, descriptions off so it stays fast.

{ "keywords": ["data engineer", "softwareentwickler"], "publishedSince": 1, "maxItems": 300 }

Apprenticeships and internships

The board is the main national source for Ausbildung and dual-study places.

{ "keywords": ["fachinformatiker"], "employmentType": "apprenticeship", "location": "Hamburg", "radiusKm": 100 }

Direct employers only, with full text

Temp-agency postings dominate some occupations; turn them off and read the full posting text.

{ "keywords": ["pflegefachkraft"], "temporaryWork": false, "includeDescription": true, "maxItems": 200 }

Pricing

Pay per event: a small fee per Actor start plus a fee per job returned. Searches, paging, deduplication and filtering are free — you pay only for the rows that end up in your dataset. A daily alert run with 20–50 new matches costs a few cents. Current prices are in the Pricing tab.

Limits and good practice

  • The search returns 25 postings per page and the portal ignores a larger page size, so a 1 000-job run is about 40 search requests. The Actor pauses between requests; keep schedules to a few runs a day rather than one a minute.
  • The date filter is applied by the portal in steps — today, 1, 7, 14 and 28 days. A publishedSince of 3 is sent as 7 and then narrowed by the Actor itself, and anything above 28 days is filtered by the Actor alone.
  • temporaryWork: false relies on the portal's own flag, which is coarse. With includeDescription on, the Actor additionally drops every posting whose detail record confirms it is temp agency.
  • Results are sorted newest-first, which lets a date filter stop paging early instead of walking the whole result set.
  • The board also carries some Austrian postings; country tells you which (Germany, Austria).
  • Keyword matching is the portal's own German-language search, not a substring match, so it handles compound words and inflections — but English terms find less than German ones.

Use with AI agents (MCP)

Add this Actor to the Apify MCP server and an agent (Claude, Cursor, any MCP client) can ask for "nursing jobs within 30 km of Leipzig posted this week" and get structured rows with links. Typical tool input:

{ "keywords": ["pflegefachkraft"], "location": "Leipzig", "radiusKm": 30, "publishedSince": 7, "maxItems": 50 }

FAQ

Why do I get fewer results than the website shows? maxItems and maxItemsPerQuery cap the run, and publishedSince is applied twice (coarsely by the portal, then exactly by the Actor). Raise the caps for a full sweep.

Is there an official API I should use instead? The agency publishes no documented public job-search API. This Actor reads the same public search a browser does, which is why it also accepts a proxy configuration.

Why is description empty? Descriptions need one extra request per posting, so they are off by default. Set includeDescription to true.

Why is there no salary on most rows? German postings rarely state pay. When an employer does publish it, salaryMin, salaryMax, salaryCurrency and salaryPeriod are filled.

Do you collect contact people? No — see the personal-data section above.

Something broke or a field is missing? Open an issue on the Actor's Issues tab; the source is a public portal and changes there are fixed quickly.

Cost control

Pay per event only works if you can see the bill before you pay it:

  • Set dryRun to count every result and print what a full run would cost - nothing is charged, and only a few sample rows are stored.
  • You are charged only for rows actually delivered to the dataset; a run that finds nothing costs nothing beyond the start event.
  • maxItems is respected exactly, and the run stops before the platform's Max total charge for the run.
  • Every run writes a COST record to the key-value store: chargeable events, charged events, the amount in USD and whether a limit was hit.