# Jobbird Scraper \[💰$0.90] - Netherlands, Germany & UK + Salary (`corvuslab/jobbird-scraper`) Actor

Scrape Jobbird jobs in the Netherlands, Germany and the UK by keyword, city + radius, category, hours and contract type. 53 fields: salary min/max/period, company website, region, distance in km, employer type, full description, emails and phones. Incremental monitoring and alerts.

- **URL**: https://apify.com/corvuslab/jobbird-scraper.md
- **Developed by:** [Corvuslab](https://apify.com/corvuslab) (community)
- **Stats:** 2 total users, 1 monthly users, 92.3% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.90 / 1,000 results

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

### What does Jobbird Jobs Scraper do?

Jobbird Jobs Scraper extracts job vacancies from **all three Jobbird country sites — the Netherlands, Germany and the United Kingdom** — into clean, structured records with **55 fields** each: salary range with currency and period, weekly hours, company website, region, distance from your chosen city, employer type (direct employer or agency), languages and the full job description. Search by keyword, city + radius, category, contract type, education level and hours, then schedule it with **incremental monitoring** so you only receive (and pay for) new and changed jobs. No code, no login — export to JSON, CSV or Excel, pull it from the API, or hand it to an AI agent.

Give it search terms and places — or paste Jobbird search, landing-page or job URLs — and it returns every matching job as a structured record, with the complete description in text, HTML and Markdown plus any emails, phone numbers and links written in the job text.

> New to Apify? Sign up free and use the included monthly platform credit to try this Actor — no credit card needed.

**Why this scraper**

- 🌍 **Three countries in one Actor** — the Dutch, German *and* UK Jobbird sites, together over 180,000 live jobs, in one run.
- 📍 **Location search that really filters** — only jobs inside your radius, each with its `distanceKm`.
- ⚡ **Fast & low-cost** — about 100 fully detailed jobs a minute, so large runs stay quick and cheap.
- 🧾 **Rich, typed records** — 55 structured fields per job, not raw HTML.
- ♻️ **Cheap to monitor** — incremental mode skips and doesn't charge unchanged jobs.
- 🔔 **Notifications built in** — Telegram, Slack, Discord or any webhook.
- 🤖 **AI- & API-ready** — compact output, MCP-friendly, one-click integrations.

***

### What data can you extract from Jobbird?

Every record carries **55 fields** grouped like this:

| Group | What you get |
|---|---|
| **Job** | Job ID, title, URL, apply link, category, contract types, languages, posting date, premium flag |
| **Location** | City, region / province, street address (mostly the Netherlands), country, Jobbird market, `distanceKm` from your search location |
| **Pay, hours & requirements** | `salaryMin`, `salaryMax`, `salaryCurrency`, `salaryPeriod`, `salaryText`, `hoursMin`, `hoursMax`, `hoursText`, work schedule, minimum and maximum education level, years of experience required (Netherlands, where the employer states it) |
| **Company** | Company name, company ID, logo, company website, company description, employer type (direct employer or recruitment agency) |
| **Contacts** | Emails, phone numbers and links written in the job text — useful for outreach when the employer or agency includes them |
| **Description & monitoring** | Full text as plain text, HTML and/or Markdown; change type (NEW / UPDATED / REAPPEARED / UNCHANGED / EXPIRED), first/last seen dates and repost detection |

#### What's filled in each country

Title, company, location, category, date, links and the full description are on virtually every
job. The rest depends on the country's site and on what the employer put in the ad:

| Field | 🇳🇱 Netherlands | 🇩🇪 Germany | 🇬🇧 United Kingdom |
|---|---|---|---|
| Salary | often — varies a lot by search | almost never | on most jobs |
| Weekly hours | sometimes | rarely | on most jobs |
| Contract types | often | rarely | always |
| Company website | often | sometimes | never |
| Street address | often | rarely | never |
| Employer type (direct / agency) | on most jobs | always | never |
| Languages | always | always | never |
| Education level | sometimes | never | never |
| Years of experience | occasionally | never | never |

A field the ad doesn't state is `null`, never a guess. Website and address need **Company
website & address** switched on. A few jobs, mainly internships abroad, have no city on Jobbird,
so their `location` is empty too.

***

### Key features

- 🌍 **Netherlands, Germany and United Kingdom** — pick one, two or all three Jobbird sites, over 180,000 jobs together. Most Jobbird tools only cover the Dutch site.
- 📍 **Radius search you can trust** — type a city, town or Dutch postcode and a radius, and you get only the jobs inside it, each with `distanceKm`. A place Jobbird doesn't recognise is skipped with a warning instead of quietly returning the whole country.
- 🎯 **No filler results** — a search term with no matches returns nothing, never a page of unrelated jobs you'd still pay for.
- 💶 **Structured salary** — `salaryMin`, `salaryMax`, `salaryCurrency` and `salaryPeriod` (hour / day / month / year) instead of a text blob, plus a filter for jobs that publish pay.
- 🏢 **Company intelligence** — company website, region, street address and employer type, with a **Direct employers only** filter to cut out agencies.
- 🔀 **Multi-search runs** — several keywords × several cities × several countries in one run, merged and de-duplicated, with an optional per-search cap so results come back balanced.
- 🎚️ **Every Jobbird filter** — category, contract type, education level, hours per week, how to apply and posted within — applied by Jobbird, so you never pay for rows you filtered out.
- ♻️ **Incremental monitoring** — schedule it and get only new, updated and reappeared jobs; unchanged jobs are not charged and reposts are flagged.
- 🔔 **Notifications** — Telegram, Slack, Discord or any webhook (n8n / Make / Zapier).
- 🤖 **AI-ready** — compact and drop-empty output modes keep payloads small for LLMs and MCP.

***

### 🚀 How to scrape Jobbird

1. Open the Actor, pick your **countries**, enter one or more **search terms** (e.g. `developer`, `verpleegkundige`) and, optionally, **locations** with a **radius** — or paste a Jobbird URL.
2. Add filters (category, contract type, hours, posted within…), set **Max results** and choose whether to fetch **full job details**.
3. (Optional) Turn on **incremental mode** and a **notification** channel, then **Schedule** it.
4. Click **Start** — jobs stream into the Output tab.
5. Download as **JSON, CSV or Excel**, or pull them from the **API**.

No login, no cookies, no setup — it runs out of the box. Start it with an empty input and it returns the 25 newest jobs in the Netherlands with full details.

#### Quick-start example input

```json
{
  "countries": ["nl"],
  "searchTerms": ["developer"],
  "locations": ["Amsterdam"],
  "radiusKm": 25,
  "postedWithin": "7",
  "maxResults": 100
}
```

This finds up to 100 developer jobs posted in the last week within 25 km of Amsterdam, each with its distance from the city centre, salary where the employer publishes it, the company website and the full description — a ready-made shortlist for recruiters or job seekers.

***

### Input

Configure it in the visual editor — no code needed — or pass JSON via the API.
**38 inputs** are available, grouped exactly as in the Actor's input form.

#### 🔎 Search

Every combination of country × search term × location runs as its own search; results are merged and de-duplicated.

| Field | Type | What it does |
|---|---|---|
| `searchTerms` | array | Job titles, skills or company names, e.g. `developer`, `verpleegkundige`, `logistiek medewerker`. Leave empty for every job matching your filters. |
| `countries` | array | `nl` (Netherlands), `de` (Germany), `uk` (United Kingdom). Defaults to the Netherlands. |
| `locations` | array | Cities, towns or Dutch postcodes, e.g. `Amsterdam`, `Den Haag`, `1012AB`, `Berlin`, `Manchester`. Empty = whole country. |
| `radiusKm` | integer | Distance around each location, 1–200 km (default 30). |
| `startUrls` | array | Jobbird search URLs (all their filters are kept), landing pages such as `/nl/vacatures/amsterdam/ict`, or single job URLs — from any country site. |
| `maxResults` | integer | Total jobs across all searches (default 25, 0 = no limit). |
| `maxResultsPerSearch` | integer | Cap each single search so a multi-keyword or multi-city run stays balanced (0 = no cap). |
| `sortBy` | enum | `auto` (default): newest first, but most relevant first in Germany (see FAQ). `newest` or `relevance` apply everywhere. |

#### 🎚️ Filters

Filters are applied by Jobbird itself. Categories, contract types and education levels differ per country — type them as that country's site shows them (Dutch on .nl, German on .de, English on .uk), by label or as in the site's URL.

| Field | Type | What it does |
|---|---|---|
| `postedWithin` | enum | `any`, `0` (today), `1` (last 2 days), `7` or `30` days. |
| `categories` | array | e.g. `ICT`, `Zorg en medisch`, `techniek` (NL), `IT`, `Logistik und Lager` (DE), `Retail` (UK). |
| `contractTypes` | array | e.g. `Fulltime`, `Parttime`, `Stage`, `Bijbaan`, `Freelance-zzp` (NL), `Vollzeit`, `Teilzeit`, `Minijob`, `Praktikum` (DE). |
| `educationLevels` | array | e.g. `MBO`, `HBO`, `WO` (NL). |
| `workingHours` | array | `0-8`, `9-16`, `17-24`, `25-32`, `33-36`, `37-*` hours per week, or `missing` (not stated). |
| `applicationMethods` | array | `on_site` (apply on Jobbird), `off_site` (employer's website), `whatsapp`, `is_motivation_mandatory` (no cover letter required). |
| `directEmployerOnly` | boolean | Only jobs posted by the employer itself, not an agency (Netherlands and Germany). |
| `salaryOnly` | boolean | Skip jobs without a published pay range — skipped jobs are not charged. |

#### 📄 Output

| Field | Type | What it does |
|---|---|---|
| `includeDetails` | boolean | Open every job for the full description, contract types, languages, employer type and contacts (default on). |
| `includeCompanyDetails` | boolean | Also add company website, region, street address and the employer's own apply link (default on). |
| `descriptionFormat` | enum | `all`, `text`, `html` or `markdown`. |
| `descriptionMaxLength` | integer | Cut descriptions to N characters (0 = full length). |
| `compact` | boolean | Core fields only — ideal for AI agents and MCP clients. |
| `excludeEmptyFields` | boolean | Drop null and empty fields for cleaner output. |

#### ♻️ Incremental monitoring

| Field | Type | What it does |
|---|---|---|
| `incrementalMode` | boolean | Tag every job NEW / UPDATED / REAPPEARED / UNCHANGED / EXPIRED and emit only what changed. |
| `stateKey` | string | Stable name for the tracked search (auto-derived from your settings if blank). |
| `emitUnchanged` / `emitExpired` | boolean | Also emit UNCHANGED / EXPIRED jobs. |
| `skipReposts` | boolean | Leave out jobs an employer took down and posted again under a new link — not charged. |

#### 🔔 Notifications

Credentials are secret inputs — encrypted at rest, masked in the UI and never written to the run log. Every channel is off unless you fill it in.

| Field | Type | What it does |
|---|---|---|
| `telegramToken` + `telegramChatId` | string | Telegram bot token + chat or channel ID. |
| `slackWebhookUrl` / `discordWebhookUrl` | string | Incoming webhook URL. |
| `webhookUrl` + `webhookHeaders` | string / object | POST the matched jobs as JSON anywhere — n8n, Make, Zapier. |
| `notificationLimit` | integer | Jobs per notification message (1–20, default 5). |
| `notifyOnlyChanges` | boolean | Alert only on new, updated and reappeared jobs. |

#### ⚙️ Advanced

| Field | Type | What it does |
|---|---|---|
| `proxyConfiguration` | object | On by default for faster large runs. Turn it off to use only the Actor's own connection (slower). |
| `ignoreUrlFailures` | boolean | Skip start URLs that can't be interpreted instead of failing the run (default on). |
| `maxRequestRetries` | integer | Retries per failed request (0–10, default 3). |

#### More example inputs

All three countries, IT jobs with a published salary, posted in the last 30 days:

```json
{
  "countries": ["nl", "de", "uk"],
  "searchTerms": ["software engineer", "data analyst"],
  "salaryOnly": true,
  "postedWithin": "30",
  "maxResultsPerSearch": 200,
  "maxResults": 0
}
```

Scrape a Jobbird landing page and a single job directly:

```json
{
  "startUrls": [
    { "url": "https://www.jobbird.com/nl/vacatures/amsterdam/ict" },
    { "url": "https://www.jobbird.com/nl/vacature/26009925-service-technicus-midden-zuid-nederland" }
  ],
  "includeDetails": true
}
```

Daily monitoring of nursing jobs from direct employers around Utrecht, with Slack alerts:

```json
{
  "countries": ["nl"],
  "searchTerms": ["verpleegkundige"],
  "locations": ["Utrecht"],
  "radiusKm": 40,
  "directEmployerOnly": true,
  "maxResults": 0,
  "incrementalMode": true,
  "slackWebhookUrl": "https://hooks.slack.com/services/...",
  "notifyOnlyChanges": true
}
```

***

### Output

Each dataset item is one Jobbird job. A real record (description shortened):

```json
{
  "id": "26009925",
  "title": "Service Technicus Midden/Zuid Nederland",
  "company": "Start People",
  "companyId": "30172",
  "url": "https://www.jobbird.com/nl/vacature/26009925-service-technicus-midden-zuid-nederland",
  "applyUrl": "https://www.jobbird.com/nl/volg/externe-sollicitatie?jobId=26009925&recruiterId=30172",
  "location": "Utrecht",
  "region": "Utrecht",
  "country": "NL",
  "market": "nl",
  "distanceKm": 0.0,
  "category": "Techniek",
  "contractTypes": ["Fulltime"],
  "languages": ["Dutch"],
  "hoursMin": 36,
  "hoursMax": 40,
  "hoursText": "36 - 40",
  "workSchedule": "Full-time",
  "salaryMin": 3300,
  "salaryMax": 4000,
  "salaryCurrency": "EUR",
  "salaryPeriod": "month",
  "salaryText": "3,300–4,000 EUR / month",
  "isPremium": false,
  "postedAt": "2026-10-09T19:05:02+00:00",
  "description": "Wat je gaat doen\nAls service technicus draag je bij aan het onderhouden en optimaliseren van medische meet- en weegapparatuur, waarmee zorgprofessionals dagelijks werken. …",
  "extractedEmails": ["apeldoorn@startpeople.nl"],
  "extractedPhones": ["055-5261112"],
  "source": "jobbird.com",
  "searchLocation": "Utrecht",
  "scrapedAt": "2026-10-09T21:19:14.543910+00:00",
  "detailFetched": true
}
```

The description also comes as `descriptionHtml` and `descriptionMarkdown`. Which
of the other fields are filled differs by country — see
[What's filled in each country](#whats-filled-in-each-country). In incremental mode each record also carries `changeType`, `firstSeenAt`,
`lastSeenAt` and `isRepost`.

#### Data fields

- **Core** — `id`, `title`, `company`, `url`, `applyUrl`, `location`, `region`, `country`, `market`, `distanceKm`, `postedAt`, `isPremium`, `source`, `scrapedAt`.
- **Pay & hours** — `salaryMin`, `salaryMax`, `salaryCurrency`, `salaryPeriod`, `salaryText`, `hoursMin`, `hoursMax`, `hoursText`, `workSchedule`, `experienceText`, `experienceYearsMin`, `experienceYearsMax`.
- **Details** (with `includeDetails`) — `category`, `contractTypes`, `languages`, `educationMin`, `educationMax`, `employerType`, `description`, `descriptionHtml`, `descriptionMarkdown`.
- **Contacts** — `extractedEmails`, `extractedPhones`, `extractedUrls`.
- **Company** — `companyId`, `companyLogo`, `companyWebsite`, `companyDescription`, `streetAddress`.
- **Search & monitoring** — `searchKeyword`, `searchLocation`, `detailFetched`, `contentHash`, `changeType`, `firstSeenAt`, `lastSeenAt`, `previousSeenAt`, `expiredAt`, `isRepost`, `repostOfId`, `repostDetectedAt`.

Every field is present in standard mode (missing values are `null`); **compact mode** returns
18 core fields for lean AI/MCP payloads; **`excludeEmptyFields`** drops empties. The Output tab
has a ready-made **Overview** table.

***

### ♻️ How to monitor Jobbird with incremental mode

Schedule the Actor and turn on **incremental mode**: each run compares against the last and
emits only **NEW / UPDATED / REAPPEARED** jobs, plus **EXPIRED** ones if you ask. Unchanged jobs
are recognised from the search results *before* their job page is opened, so you skip both the
work and the cost. Jobs that an employer takes down and re-posts under a new link are flagged
with `isRepost` (or left out entirely with `skipReposts`).

| | First run (baseline) | Next run (nothing changed) |
|---|---|---|
| Jobs found | all | all |
| Job pages opened | all | **none** |
| Records emitted & billed | all | **none** |

Your cost after the baseline scales with how much Jobbird changes, not with how much you watch:

| Daily churn | of 1,000 tracked | billable records | you save |
|---|---|---|---|
| 5 % | 1,000 | 50 | **95 %** |
| 15 % | 1,000 | 150 | **85 %** |
| 30 % | 1,000 | 300 | **70 %** |

#### Setting up scheduled monitoring

1. Configure your countries, search terms, locations and filters.
2. Turn on **Incremental mode** and set **Max results** to 0 (or above the number of matches) so expired jobs can be detected.
3. Run it once — this seeds the baseline.
4. Open the **Schedules** tab and set a recurring run (e.g. hourly or daily).
5. Add a notification channel with **Alert only on changes** on.

***

### 🔔 How to set up Jobbird alerts

Get pinged the moment a matching Jobbird job appears on Telegram, Slack, Discord or any webhook (n8n / Make / Zapier) — fill in only the channels you want (see **Notifications** under Input).

Every channel fires independently — a broken channel can't stop the scrape or the others.
Pair **incremental mode** with **Alert only on changes** to hear only about genuinely new jobs,
with no duplicates across runs. `notificationLimit` sets how many jobs each message lists.

***

### 💡 What can you do with Jobbird data?

#### Find hiring companies for recruitment and staffing sales

Turn on **Direct employers only** to see which companies are hiring themselves, with their website,
region and open roles. That is a ready-made prospect list for agencies and HR-tech vendors.

#### Benchmark salaries in the Netherlands and the UK

Collect `salaryMin`, `salaryMax` and `salaryPeriod` by category, region and hours. Use
**Only jobs with a salary** to build a clean pay dataset without empty rows.

#### Compare labour markets across three countries

Run the same search on the Dutch, German and UK sites in one go and compare volumes, categories,
contract types and hours side by side. Ideal for expansion planning and cross-border hiring.

#### Build a commute-based job feed

Search around a city or Dutch postcode and sort by `distanceKm` to show candidates only the jobs
they can actually reach. Schedule it to keep a job board or newsletter fresh every day.

#### Market & competitive research

Track which companies, agencies and categories post the most jobs by region over time. Export the
history to build trend dashboards in Google Sheets, Looker Studio or Power BI.

#### Lead generation from job ads

Combine company website, employer type and the emails and phone numbers written in each job ad to
reach the right recruiter or hiring manager directly.

#### Feed AI agents and LLM pipelines

Send compact JSON straight into an LLM context, an MCP tool or a vector store. `excludeEmptyFields`
and compact mode keep token costs low.

***

### 💰 How much does it cost to scrape Jobbird?

This Actor uses Apify's **pay-per-event** model: a small fee when a run starts, plus a fee for
each job saved to your dataset; the **Pricing** tab shows the current numbers. Your Apify plan's platform usage is billed
separately and stays low, because the Actor is lightweight by design.

Three things cut your bill further:

- **Jobbird-side filters** — category, hours, contract type, posted within and the others are
  applied by Jobbird, so rows you filtered out are never fetched and never billed. Jobs skipped by
  **Only jobs with a salary** or **Skip re-posted jobs** are not charged either.
- **Incremental mode** — after the baseline you pay only for what changed (the table above).
- **Lean runs** — turn off **Company website & address** or **Full job details** when the
  search-result fields are enough.

***

### 🔌 Integrations & export

Export to **JSON, CSV, Excel** or an HTML table, or pull from the **REST API** and the
**JavaScript / Python** clients. Runs on a **schedule**, connects to **Google Sheets, Slack,
Make, Zapier and n8n**, and works as an **MCP tool** for AI agents — compact mode keeps token
usage small.

Also covering the UK and German job markets: [Reed.co.uk Jobs Scraper](https://apify.com/corvuslab/reed-scraper), [Totaljobs Scraper](https://apify.com/corvuslab/totaljobs-scraper) and [StepStone Job Scraper](https://apify.com/corvuslab/stepstone-jobs-scraper).

***

### 🔗 Using the API

Run this Actor from your own code. Example with the Apify Python client:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_API_TOKEN>")

run_input = {
    "countries": ["nl", "uk"],
    "searchTerms": ["developer"],
    "locations": ["Amsterdam", "London"],
    "radiusKm": 30,
    "maxResults": 50,
}

run = client.actor("corvuslab/jobbird-scraper").call(run_input=run_input)

for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["title"], item["company"], item["distanceKm"], item["salaryText"])
```

It also works with the JavaScript/TypeScript client, the Apify CLI and the REST API.

***

### ❓ FAQ

**Which Jobbird countries does it cover?** All three Jobbird sites: the Netherlands (around 77,000 jobs), the United Kingdom (around 86,000) and Germany (around 22,000), as of October 2026. Each site has its own jobs; pick any combination in `countries` and every record says which `market` it came from.

**How many jobs can I get per run?** Jobbird shows up to 7,500 jobs per search. To collect more, add search terms, locations or categories — each combination is its own search, and the results are merged and de-duplicated. Set `maxResults` to 0 for no overall limit.

**How long does a run take?** About 100 jobs a minute with full details, so 1,000 jobs in around 10 minutes and a full 7,500-job search in a little over an hour. Turning off **Full job details** makes runs much faster still.

**Why are German results sorted by relevance?** On the German site one large job platform refreshes its ads daily, so newest-first pages are almost all theirs. By default (`auto`) German searches are sorted by relevance for a broad mix of employers; Dutch and UK searches stay newest first. Set `sortBy` to `newest` to override. To monitor German jobs, use incremental mode with `maxResults` 0 so every job is read whatever the order.

**Why is the salary empty on some jobs?** Because the employer didn't publish one. See [what's filled in each country](#whats-filled-in-each-country); use **Only jobs with a salary** to skip the rest (they are not charged). Jobbird publishes the amount but not whether it is hourly, daily, monthly or yearly, so `salaryPeriod` is estimated from the amount — a weekly rate, for example, may read as daily. `salaryText` keeps the readable form.

**What happens if Jobbird doesn't recognise my location?** It is skipped with a warning in the log, and your other locations still run — you never get the whole country back by mistake. Cities, towns and Dutch postcodes (e.g. `1012AB`) work; provinces are not supported, so search a city with a larger radius instead.

**What if my search term has no matches?** You get nothing for that search and a warning — never a list of unrelated "you might also like" jobs that you would pay for.

**In which language do I type categories and contract types?** As each country's site shows them: Dutch on the Netherlands site (`Zorg en medisch`, `Parttime`), German on the German site (`Logistik und Lager`, `Vollzeit`) and English on the UK site (`Retail`). The label or the URL form both work; a value that doesn't exist on a site is skipped with a warning, and that country is skipped if none of your values exist there.

**Can I get only new jobs on a schedule?** Yes — turn on incremental mode and schedule it; see the monitoring and alerts sections above.

***

### ⚖️ Is it legal to scrape Jobbird?

This Actor collects only **publicly available** job ads. You're responsible for how you use the
data — in particular any personal information, such as a recruiter's email or phone number in a
job description — and for complying with Jobbird's terms and applicable law, including the GDPR
in the Netherlands, Germany and the UK. Not affiliated with, endorsed by or sponsored by Jobbird.

***

**Keywords:** jobbird scraper · jobbird.com scraper · jobbird api · jobbird jobs · jobbird vacatures · Netherlands job scraper · Dutch job listings · Germany job scraper · UK job scraper · vacatures scraper · Stellenangebote scraper · salary data Netherlands · job monitoring · recruitment lead generation · export jobs to CSV · export to Excel · no-code scraper · MCP tool for AI agents

# Changelog

This Actor's version history is a separate document: https://apify.com/corvuslab/jobbird-scraper/changelog.md

# Actor input Schema

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

Job titles, skills or company names, e.g. "developer", "verpleegkundige", "logistiek medewerker". Leave empty to get every job matching your other filters. A term with no matches returns nothing — never unrelated jobs.

## `countries` (type: `array`):

Which Jobbird country sites to search — each has its own jobs. Defaults to the Netherlands.

## `locations` (type: `array`):

Cities, towns or Dutch postcodes, e.g. "Amsterdam", "Den Haag", "1012AB", "Berlin", "Manchester". Only jobs within the radius below are returned. A place the site does not recognise is skipped with a warning rather than returning jobs from the whole country. Provinces are not supported. Leave empty to search the whole country.

## `radiusKm` (type: `integer`):

How far around each location to search.

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

Optional. Paste Jobbird search URLs (e.g. https://www.jobbird.com/nl/vacature?s=developer — every filter in the URL is kept) or job URLs, from any country site. When set, these replace the search form above.

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

Maximum number of jobs to return across all searches. Set 0 for no limit. Jobbird shows up to 7,500 jobs per search — add search terms, locations or categories to collect more.

## `maxResultsPerSearch` (type: `integer`):

Optional. Cap each single search (one country × search term × location) so a multi-keyword or multi-city run comes back balanced. 0 = no per-search cap.

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

Order of the results. Automatic sorts newest first, except on the German site: there one job platform refreshes its ads every day and would fill the newest-first pages, so German results come most relevant first. Pick Newest first to get the newest jobs first in every country.

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

Only jobs posted or refreshed recently.

## `categories` (type: `array`):

Only jobs in these Jobbird categories, by name or as in the site's URL, e.g. "ICT", "Zorg en medisch", "techniek" (Netherlands), "IT", "Logistik und Lager" (Germany), "Retail" (United Kingdom).

## `contractTypes` (type: `array`):

Only jobs with these contract types, e.g. "Fulltime", "Parttime", "Stage", "Bijbaan", "Freelance-zzp" (Netherlands), "Vollzeit", "Teilzeit", "Minijob", "Praktikum" (Germany). Many jobs do not state one.

## `educationLevels` (type: `array`):

Only jobs asking for these education levels, e.g. "MBO", "HBO", "WO" (Netherlands).

## `workingHours` (type: `array`):

Only jobs in these weekly-hours ranges.

## `applicationMethods` (type: `array`):

Only jobs with these application options.

## `directEmployerOnly` (type: `boolean`):

Only jobs posted by the employer itself, not by an agency. Netherlands and Germany only.

## `salaryOnly` (type: `boolean`):

Skip jobs that do not publish a pay range. Skipped jobs are not charged.

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

Open every job to add its full description (text, HTML and Markdown), category, contract types, languages, employer type and any emails, phone numbers and links written in it. Turn off for the fastest runs with search-result fields only.

## `includeCompanyDetails` (type: `boolean`):

Also add the employer's website, region, street address and the employer's own apply link (needs Full job details). Turn off for faster runs.

## `descriptionFormat` (type: `string`):

Which representation(s) of the job description to include.

## `descriptionMaxLength` (type: `integer`):

Cut descriptions to this many characters. 0 = full length.

## `compact` (type: `boolean`):

Emit only the core fields (title, company, location, category, hours, salary, links, date). Ideal for AI agents and MCP clients.

## `excludeEmptyFields` (type: `boolean`):

Remove null, empty-string and empty-list fields from each record.

## `incrementalMode` (type: `boolean`):

Track jobs between runs and tag every record NEW / UPDATED / UNCHANGED / EXPIRED. Unchanged jobs are skipped and not charged.

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

Stable name for the tracked search. Leave empty to derive one automatically from your search settings.

## `emitUnchanged` (type: `boolean`):

Also emit jobs that have not changed since the previous run.

## `emitExpired` (type: `boolean`):

Emit jobs that were open before but are gone now. Needs a run that reads the whole search (Max results 0 or above the number of matches).

## `skipReposts` (type: `boolean`):

Leave out jobs that an employer took down and posted again under a new link (same title, company and location as a job already tracked). They are not charged. Every new job still carries isRepost either way.

## `telegramToken` (type: `string`):

Bot token from @BotFather.

## `telegramChatId` (type: `string`):

Chat or channel ID, e.g. "-100123456789" or "@yourchannel".

## `slackWebhookUrl` (type: `string`):

Slack incoming-webhook URL.

## `discordWebhookUrl` (type: `string`):

Discord incoming-webhook URL.

## `webhookUrl` (type: `string`):

Any HTTPS endpoint. Receives a JSON POST with the matched jobs — works with n8n, Make and Zapier.

## `webhookHeaders` (type: `object`):

Extra headers for the webhook request, e.g. {"Authorization": "Bearer xyz"}.

## `notificationLimit` (type: `integer`):

How many jobs to include in each notification message.

## `notifyOnlyChanges` (type: `boolean`):

In incremental mode, alerts list only new, updated and reappeared jobs, even when Include unchanged jobs is on.

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

On by default: Apify Proxy runs alongside the Actor's own connection, so large runs finish several times faster. Turn it off to use only the Actor's own connection — slower, and Apify Proxy is still brought in by itself if the site starts refusing.

## `ignoreUrlFailures` (type: `boolean`):

Skip start URLs that cannot be interpreted instead of failing the whole run.

## `maxRequestRetries` (type: `integer`):

How many times to retry a failed request before giving up on it.

## Actor input object example

```json
{
  "searchTerms": [
    "developer"
  ],
  "countries": [
    "nl"
  ],
  "radiusKm": 30,
  "maxResults": 25,
  "maxResultsPerSearch": 0,
  "sortBy": "auto",
  "postedWithin": "any",
  "directEmployerOnly": false,
  "salaryOnly": false,
  "includeDetails": true,
  "includeCompanyDetails": true,
  "descriptionFormat": "all",
  "descriptionMaxLength": 0,
  "compact": false,
  "excludeEmptyFields": false,
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "skipReposts": false,
  "notificationLimit": 5,
  "notifyOnlyChanges": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "ignoreUrlFailures": true,
  "maxRequestRetries": 3
}
```

# Actor output Schema

## `overview` (type: `string`):

No description

## `allItems` (type: `string`):

No 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 = {
    "searchTerms": [
        "developer"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("corvuslab/jobbird-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": ["developer"] }

# Run the Actor and wait for it to finish
run = client.actor("corvuslab/jobbird-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": [
    "developer"
  ]
}' |
apify call corvuslab/jobbird-scraper --silent --output-dataset

```

## MCP server setup

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