# willhaben Jobs Scraper - Salary & Company VAT ID (`aidnurs/willhaben-jobs-scraper`) Actor

Scrape willhaben.at jobs (Austria) as an API: salary parsed into numbers, employment type, position, remote work, full description plus company VAT ID (UID), industry and size for B2B leads. Only new jobs mode for daily job alerts. Duplicates are free.

- **URL**: https://apify.com/aidnurs/willhaben-jobs-scraper.md
- **Developed by:** [Nursultan Imanov](https://apify.com/aidnurs) (community)
- **Categories:** Jobs, Lead generation
- **Stats:** 1 total users, 1 monthly users, 88.2% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 jobs

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

### willhaben jobs scraper and willhaben API

**willhaben Jobs Scraper** extracts job listings from [willhaben.at/jobs](https://www.willhaben.at/jobs), one of Austria's biggest job boards with 15,000+ open positions. Search by keyword, location and federal state, filter by job field, employment type, position level, employer type and posting date, and get clean, structured data: **title, company, salary (parsed into number + period), employment type, position, remote work flag, posting and expiry dates, apply link and the full job description**.

Every job also comes with **company data for B2B use**: Austrian VAT ID (UID), industry, company size, address, website and number of open jobs.

Use it as a **willhaben jobs API** for job market research, recruiting, salary benchmarking, lead generation for HR and staffing services, or to build your own job alerts.

### Why use this willhaben scraper?

- 🏢 **Company data included.** VAT ID (`ATU...`), industry, employee range, street address, website, direct employer vs. staffing agency.
- 💶 **Salary as a number.** `"ab 3.600 € monatlich"` becomes `salaryMin: 3600`, `salaryPeriod: "MONTH"`.
- 🔁 **No duplicates charged.** Every job appears once per run. willhaben's paid "top jobs", injected into every result page regardless of your search, are skipped.
- 🔔 **Only new jobs since the last run.** Turn on *Only new jobs* and schedule the Actor: each run returns just the postings you have not received yet. Perfect for daily job alerts.
- 🔎 **All website filters.** Federal states, job fields, employment types, position level, employer type, posted within 24 h / 3 days / week, or paste any willhaben search URL.
- 🛡️ **GDPR-friendly by default.** Phone numbers, personal e-mails and contact-person names are masked or left out (role mailboxes like `jobs@` stay). Turn on *Include personal data* only if you have a legal basis.
- ⚡ **Fast and reliable.** Pure HTTP, no browser. 50 jobs with full descriptions take about 5 seconds, 1,000 jobs about 7 minutes. If willhaben rate-limits the run, the Actor slows down and switches to an Austrian residential proxy by itself, at no extra cost to you.

### How much does it cost to scrape willhaben jobs?

You pay only for jobs saved to the dataset (pay per result). Empty searches cost nothing, and you can cap spending with *Max jobs* and the run's maximum cost.

### How to scrape willhaben jobs

1. Enter **Keywords** (e.g. `Data Engineer`, `Koch`, `Buchhaltung`) or leave empty for all jobs.
2. Optionally enter a **Location** (`Wien`, `Graz`, `Linz`) and/or pick **Federal states**.
3. Optionally choose job fields, employment types, position level, employer type or *Posted within*.
4. Set **Max jobs** and click **Start**.
5. Download the results as JSON, CSV, Excel or HTML, or get them via the Apify API.

Already have a search set up on willhaben? Paste its URL into **Search URLs** instead.

### Input example

```json
{
  "keywords": "python",
  "regions": ["Wien", "Steiermark"],
  "employmentTypes": ["Vollzeit"],
  "postedWithin": "last_week",
  "maxItems": 100,
  "includeDetails": true,
  "onlyNewJobs": false
}
```

### Output example

```json
{
  "id": "13277020",
  "url": "https://www.willhaben.at/jobs/job/it-projektmanager-m-w-d/13277020",
  "title": "IT Projektmanager (m/w/d)",
  "companyName": "HR Group Personaldienstleistung GmbH",
  "locations": ["Graz-Umgebung"],
  "employmentTypes": ["Vollzeit"],
  "position": "Projektmanagement",
  "salaryText": "ab 3.600 € monatlich",
  "salaryMin": 3600,
  "salaryCurrency": "EUR",
  "salaryPeriod": "MONTH",
  "overpayPossible": true,
  "startDate": "ab sofort",
  "datePosted": "2026-10-07T13:15:00.466",
  "validThrough": "2027-10-06",
  "city": "Graz-Umgebung",
  "region": "Steiermark",
  "country": "Österreich",
  "categories": ["IT EDV"],
  "subcategories": ["IT Projektmanagement Testing", "Softwareentwicklung"],
  "applyUrl": "https://karriere.hr-group.co.at/applicationForm.php?sid=3769&page_lang=de",
  "companyType": "Personaldienstleister",
  "companyVatId": "ATU67090701",
  "companyIndustry": "Personaldienstleistungen",
  "companyEmployeesFrom": 51,
  "companyEmployeesTo": 200,
  "companyCity": "Raaba-Grambach",
  "descriptionText": "IT Projektmanager (m/w/d) ...",
  "source": "willhaben.at",
  "scrapedAt": "2026-10-07T12:11:24Z"
}
```

### Fields you get

| Field | Description |
|---|---|
| `title`, `url`, `id`, `referenceId` | Job title, link, willhaben job ID and employer's own reference |
| `companyName`, `companyProfileUrl`, `companyLogo`, `companyWebsite` | Employer |
| `companyType`, `companyVatId`, `companyIndustry` | Direct employer / staffing agency, Austrian VAT ID (UID), industry |
| `companyEmployeesFrom`, `companyEmployeesTo`, `companyActiveJobs` | Company size and number of open jobs on willhaben |
| `companyStreet`, `companyPostalCode`, `companyCity`, `companyCountry` | Company address |
| `locations`, `city`, `region`, `country`, `jobLocationsFull` | Where the job is |
| `salaryText`, `salaryMin`, `salaryCurrency`, `salaryPeriod`, `overpayPossible` | Minimum gross salary as shown and parsed, willingness to pay above it |
| `employmentTypes`, `position`, `startDate` | Vollzeit / Teilzeit / ..., position level, start date |
| `categories`, `subcategories` | willhaben job field and sub-fields |
| `remoteWorkPossible`, `experienceRequired`, `languageSkills` | Remote work, professional experience, required languages |
| `datePosted`, `dateModified`, `dateRefreshed`, `validThrough`, `isNew` | Dates |
| `applyUrl`, `easyApply` | External application link, or apply directly on willhaben |
| `descriptionText` | Full job description |

Fields from the job page (description, company data, remote work, languages, apply link) require *Include full job description* (on by default).

### FAQ

**Is it legal to scrape willhaben?**
This Actor collects only publicly available job postings, the same pages any visitor sees. It never logs in, never uses accounts or cookies of real users, and never solves or bypasses CAPTCHAs.

**Personal data is off by default.** Phone numbers and personal e-mails inside job texts are masked (role mailboxes like `jobs@` or `office@` stay), and contact-person names, direct phones and e-mails are not saved. Names that an employer writes into the free text of a description can still appear as published.

If you turn on **Include personal data**, you get everything the site shows publicly, including the contact person's name, phone and e-mail where available. Personal data is protected by the GDPR in the EU and by similar laws elsewhere. Only enable it if you have a legal basis (for example, a documented legitimate interest), keep only what you need, and do not use it for unsolicited marketing. If you are unsure, consult a lawyer.

You are responsible for how you use the data and for complying with applicable laws and willhaben's terms. This Actor is not affiliated with or endorsed by willhaben. Read more: [Is web scraping legal?](https://blog.apify.com/is-web-scraping-legal/)

Found something that should not be here? Open an issue on the Actor's Issues tab and we will look at it promptly.

**Does it scrape other willhaben categories (real estate, cars, marketplace)?**
No, only the jobs section.

**Why is there only one salary number?**
willhaben shows the minimum gross salary required by Austrian law (often the collective agreement), e.g. "ab 3.600 € monatlich". `overpayPossible: true` means the employer is willing to pay more.

**Do I need a proxy?**
No. The Actor starts without a proxy and, if willhaben rate-limits it, switches to an Austrian residential proxy by itself. That proxy is included in the price. You can still add your own proxy URLs in the *Advanced* section.

**How do I get only new jobs every day?**
Turn on *Only new jobs since last run*, save the task and add a schedule. Use a different *Monitoring key* for each saved search.

### Known gaps

- **Searching for "C++" is unreliable.** willhaben drops the `+` characters, so `C++` returns ~700 loosely matching jobs (07.10.2026), most of them not about C++. Filter the results by `descriptionText` afterwards.
- **One salary number only.** willhaben publishes the minimum gross pay, not a range.
- **`remoteWorkPossible` and `experienceRequired` are rarely filled** by employers.
- **Search URLs with SEO paths** (`/jobs/suche/python/wien`) work, but willhaben ignores extra query filters on them; the Actor logs a warning. Use `https://www.willhaben.at/jobs/suche?...` URLs for filtered searches.

**Something broke or a field is missing?**
Open an issue on the Issues tab. We usually respond within 24 hours.

# Actor input Schema

## `keywords` (type: `string`):

What to search for, e.g. job title or skill ("Data Engineer", "Buchhaltung"). Leave empty for all jobs.

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

City or district in Austria, e.g. "Wien", "Graz", "Linz". Leave empty for all locations.

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

Limit to one or more federal states (Bundesländer). Leave empty for all of Austria (plus the few DE/CH jobs willhaben lists).

## `maxItems` (type: `integer`):

Maximum number of jobs to save. You only pay for jobs saved to the dataset.

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

Open every job page to add the full description, company VAT ID, industry, size and address, remote work flag, language skills, apply link and expiry date.

## `includePersonalData` (type: `boolean`):

Off by default: phone numbers and personal e-mails inside job texts are masked (\[phone removed], \[email removed]; role mailboxes like jobs@ or office@ stay), and contact-person names, direct phones and e-mails are not saved.<br><br>Turn it on to get everything the site shows publicly, including the contact person's name, phone and e-mail where the employer published them.<br><br>⚠️ This returns personal data. You're responsible for having a legal basis to collect and use it under privacy laws (e.g. GDPR).

## `jobFields` (type: `array`):

Filter by professional field (Berufsfeld/Bereich).

## `employmentTypes` (type: `array`):

Filter by employment type (Vollzeit, Teilzeit, ...).

## `positions` (type: `array`):

Filter by position (staff, team lead, management, internship, ...).

## `companyType` (type: `string`):

Only direct employers or only staffing agencies (Personaldienstleister).

## `postedWithin` (type: `string`):

Only jobs published recently.

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

Order of search results.

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

Paste willhaben job-search URLs (https://www.willhaben.at/jobs/suche?...) with filters already set on the website. When given, the search fields above are ignored.

## `onlyNewJobs` (type: `boolean`):

Remember job IDs between runs and return only jobs you have not received yet. Ideal for scheduled runs and job alerts. Already-seen jobs are never charged.

## `stateKey` (type: `string`):

Name of the memory used by 'Only new jobs'. Use a different key for each saved search.

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

Leave as is. The Actor runs without a proxy and switches to an Austrian residential proxy by itself if willhaben rate-limits it, at no extra cost to you. Only custom proxy URLs (your own proxies) are used from this field.

## Actor input object example

```json
{
  "keywords": "python",
  "maxItems": 20,
  "includeDetails": true,
  "includePersonalData": false,
  "companyType": "",
  "postedWithin": "",
  "sortBy": "newest",
  "onlyNewJobs": false,
  "stateKey": "default",
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `jobs` (type: `string`):

Scraped job listings (table view).

## `jobsFull` (type: `string`):

All fields including full description.

# 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 = {
    "keywords": "python",
    "maxItems": 20,
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("aidnurs/willhaben-jobs-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 = {
    "keywords": "python",
    "maxItems": 20,
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("aidnurs/willhaben-jobs-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 '{
  "keywords": "python",
  "maxItems": 20,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call aidnurs/willhaben-jobs-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,aidnurs/willhaben-jobs-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/u5SshDh7uVCT0ecs9/builds/vAn2OiTA0qf9EcGiF/openapi.json
