# EU & Europe Jobs Scraper — EURES Vacancies (MCP) (`amygraphics/eu-jobs-monitor`) Actor

Live vacancies from EURES, the official EU job portal: 2.6M open positions across 32 countries (Germany, France, Spain, Italy…). Keywords, countries, full descriptions, employer, salary & contacts. Monitor mode returns only new or changed jobs. MCP-ready.

- **URL**: https://apify.com/amygraphics/eu-jobs-monitor.md
- **Developed by:** [Zakaria el bouami](https://apify.com/amygraphics) (community)
- **Categories:** Jobs, MCP servers
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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

## EU & Europe Jobs Scraper — EURES Vacancies, Search + Monitor 🇪🇺💼

Scrape **live job vacancies from EURES** — the official European employment portal run by the European Commission and the European Labour Authority. EURES aggregates the public employment services of **32 European countries** (EU-27 + Iceland, Liechtenstein, Norway, Switzerland): **~2.6 million open positions** online at any moment, refreshed continuously. The data comes from the official key-free EURES Search API — no browser, no proxy, no login.

This Actor does one job: turn the European job firehose into a **filtered, deduplicated feed of vacancies** — and, when scheduled, into an **automatic watch service that returns only what is new or changed**.

### Two modes

#### `search` — extract matching vacancies

Pick keywords (matched everywhere, in the job title, or in the employer name), countries, a publication window (`last 24 h / 7 days / 30 days`), work schedule (full-time / part-time), contract type (permanent / temporary) and the EURES flag. One clean JSON row per vacancy, newest first, deduplicated across all your queries.

#### `monitor` — only new & changed jobs

The Actor stores every job it has seen (vacancy id + fingerprint of modification date, title and number of posts) in the Apify key-value store. On the next run it returns:

- **`isNew: true`** — a vacancy never seen before,
- **`isUpdated: true`** — a known vacancy whose title, number of posts or content changed (re-posted and updated offers),
- **`changeType: "closed"`** — a vacancy you had already seen that **disappeared from the feed** (filled, withdrawn or expired): returned once, so your board never shows dead offers,
- and skips everything else — you only pay for what is new, changed or closed.

Closed-job detection only fires when the scan of your query was **complete and error-free** (it never guesses), and it is on/off with the `detectClosed` option.

Add a **daily schedule** and you have a private European job watch: "every morning, tell me the new nursing jobs in Germany and the Netherlands — and which ones are gone."

### What you get

| Field | Example |
|---|---|
| `id` / `euresUrl` | MTQ4OTIyOTIgMTI / direct link to the EURES vacancy |
| `title` / `description` | full vacancy text (clean text, HTML on request) |
| `translations` | every language version published by EURES (`includeTranslations`) |
| `employerName` / `employerLegalId` | INTER MARINE Sp. z o.o. / 5860006677 |
| `country` / `countryCodes` / `regionCodes` / `city` / `locations` | Germany / de / DE724 / Munich (NUTS regions) |
| `numberOfPosts` / `schedule` / `offering` | 20 / parttime / temporary |
| `euresFlag` | true = EURES-promoted job (relocation & language support) |
| `occupationUris` | ESCO occupation URIs |
| `languages` | languages the vacancy is published in |
| `publishedAt` / `modifiedAt` | 2026-10-05T… / 2026-10-07T… |
| **`salary`** (details) | minimum / maximum / currency / interval / type |
| **`remoteWorkAllowed`** (details) | true / false |
| **`lastApplicationDate`** (details) | apply-by deadline |
| **`contactNames` / `contactEmails` / `contactPhones`** (details) | recruiter contacts |
| `requiredEducationLevel` / `requiredYearsOfExperience` / `requiredDrivingLicenses` (details) | requirements |
| `employmentStart` / `employmentEnd` / `immediateStart` (details) | employment period |
| `isNew` / `isUpdated` / `changeType` / `firstSeenAt` / `closedAt` | monitor flags — changeType = new / updated / closed / unchanged |

**Note on salaries:** EURES only returns a salary package when the employer or the national service published one — the field is `null` otherwise. Nothing is invented.

### Who it is for

- **Recruitment agencies & job boards** — feed a European multi-country board, or a niche aggregator (healthcare, IT, logistics, construction, hospitality).
- **Relocation & immigration services** — EURES-flagged jobs come with relocation support.
- **Market research & salary benchmarking** — volumes by country, occupation, contract type, remote work.
- **AI agents & RAG pipelines** — a clean, structured, machine-readable feed of European vacancies (this Actor supports agentic payments through the Apify MCP server).
- **Job seekers with an edge** — be first on new vacancies in your field across 32 countries.

### Automation recipes (no code required)

- **Daily watch** — save this Actor as a **task** in Apify and add a **schedule** (e.g. every day 07:00). Set `mode: monitor` + `onlyChanges: true`: every run returns only new, updated and closed jobs, and you only pay for those.
- **One task per search** — "healthcare jobs in DE/NL", "IT jobs in PL", "hospitality in FR/ES": each task keeps its **own watch history** (state key derived from the query), so they never mix.
- **Push it anywhere** — add a **webhook** on the task (Apify Console → Integrations) to post each run's dataset to Slack, a Google Sheet, n8n, Make or Zapier, or to your own API endpoint for a live job board.
- **Agent-native** — an AI agent can run it directly through Apify's MCP server (below) and schedule itself.

### Use from AI agents (MCP)

This Actor is exposed as a tool through **Apify's MCP server** — any MCP client (Claude, Cursor, VS Code / Copilot, Codex, custom agents) can discover and run it without writing code:

```json
{
  "mcpServers": {
    "apify": { "url": "https://mcp.apify.com?tools=amygraphics/eu-jobs-monitor" }
  }
}
```

Then simply ask: *"Use amygraphics/eu-jobs-monitor to list the newest software engineering jobs in Germany and Spain published in the last 7 days."*

Agents can also pay autonomously — this Actor is **whitelisted for agentic payments** (Direct x402 / Apify AGI prepaid token), so a run does not require an Apify account.

### Pricing

Pay-per-event: a small start fee plus **$3 per 1 000 vacancies** extracted (one event per job). In monitor mode you only pay for jobs that are new or changed — a daily watch costs a few cents.

### Notes & limitations

- Source: official EURES Search API (European Commission / European Labour Authority). Data is reused as published.
- EURES caps every single keyword/country query at 10 000 results (API limit); combine more keywords or split by country to go deeper.
- One EURES vacancy can be advertised by several national services; use `id` to deduplicate.
- Descriptions are HTML in the source; the Actor returns cleaned text and keeps the raw HTML on request (`includeRawHtml`).

***

**Français —** Scrapez les **offres d'emploi EURES** (portail officiel européen, 32 pays, ~2,6 M de postes) : mots-clés, pays, date de publication, temps plein/partiel, CDI/CDD, drapeau EURES. Option : salaire, télétravail, date limite de candidature et **contacts (e-mail / téléphone)**. Le mode **monitor** ne renvoie que les offres nouvelles ou modifiées — parfait en tâche planifiée quotidienne. Tarif : **3 $ les 1 000 offres**, démarrage $0.001.

**Automatisation (mode monitor) :** programmez cet Actor (tâche + planning quotidien) : chaque exécution ne renvoie que les offres **nouvelles**, **modifiées** et **disparues du flux** (`changeType: "closed"` — poste pourvu, retiré ou expiré). La détection des offres fermées ne s'active que si le scan de la requête est complet et sans erreur ; option `detectClosed`. Webhook intégré (Slack, Google Sheets, n8n, Make, Zapier) pour pousser chaque exécution où vous voulez.

**Agents IA (MCP) :** cet Actor est exposé comme outil du serveur MCP d'Apify — branchez `https://mcp.apify.com?tools=amygraphics/eu-jobs-monitor` dans Claude, Cursor, VS Code ou Codex, puis demandez vos offres en langage naturel. Les agents peuvent payer seuls (x402 / AGI) : l'Actor est *whitelisted* pour les paiements agentiques.

# Actor input Schema

## `mode` (type: `string`):

search = return every matching vacancy. monitor = compare with the previous run (stored in the Apify key-value store) and return only jobs that are new or changed — ideal for a daily schedule.

## `searchQueries` (type: `array`):

One query per line. Each keyword is searched across all selected countries. Examples: 'software engineer', 'nurse', 'chef', 'warehouse'. Leave empty to take the newest vacancies of the selected countries without a keyword.

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

Two-letter EURES country codes. Leave empty for all 32 countries (EU-27 + Iceland, Liechtenstein, Norway, Switzerland). Examples: de, fr, es, it, nl, pl, pt, se.

## `keywordScope` (type: `string`):

EVERYWHERE = full text of the vacancy. TITLE = job title only. EMPLOYER = employer name only.

## `publishedWithin` (type: `string`):

Only return vacancies published in this window. 'LAST_DAY' is very fresh but thin; 'LAST_WEEK' is the best default for monitoring.

## `euresFlag` (type: `string`):

EURES-flagged jobs are the ones the EURES network actively promotes (relocation and language support, advisers in the destination country). WITH = only those; WITHOUT = exclude them.

## `positionSchedule` (type: `string`):

Filter by full-time or part-time positions.

## `positionOffering` (type: `string`):

Filter by permanent or temporary contracts (as declared by the employer).

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

Global cap on rows returned (and charged). The API itself caps each keyword/country query at 10 000 results.

## `fetchDetails` (type: `boolean`):

One extra API call per job: adds salary package, remote-work flag, employment period, required education/experience, application deadline, application instructions and contact e-mails/phones. Roughly doubles the run time.

## `includeTranslations` (type: `boolean`):

Adds a 'translations' object with every language version published by EURES for that vacancy.

## `includeRawHtml` (type: `boolean`):

Keeps the original HTML of the description in 'descriptionHtml' (otherwise only clean text is returned).

## `requestLanguage` (type: `string`):

Language requested from EURES for titles, descriptions and details when available.

## `onlyChanges` (type: `boolean`):

Monitor mode only. When enabled (recommended), jobs already seen and unchanged are not returned and not charged.

## `detectClosed` (type: `boolean`):

Monitor mode only. Marks jobs that disappeared from the feed as changeType='closed' (filled, withdrawn or expired) and returns them once, then forgets them. Only runs when the scan of your query was complete and error-free, so it never guesses. Disable if your query is so broad that the 10 000-result API cap truncates it.

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

Leave empty: the state key is derived automatically from the query, so each different search keeps its own watch history.

## Actor input object example

```json
{
  "mode": "search",
  "searchQueries": [
    "software engineer",
    "nurse"
  ],
  "countries": [
    "de",
    "fr",
    "es"
  ],
  "keywordScope": "EVERYWHERE",
  "publishedWithin": "any",
  "euresFlag": "any",
  "positionSchedule": "any",
  "positionOffering": "any",
  "maxResults": 30,
  "fetchDetails": false,
  "includeTranslations": false,
  "includeRawHtml": false,
  "requestLanguage": "en",
  "onlyChanges": true,
  "detectClosed": true,
  "stateKey": ""
}
```

# Actor output Schema

## `results` (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 = {
    "mode": "search",
    "searchQueries": [],
    "countries": [],
    "keywordScope": "EVERYWHERE",
    "publishedWithin": "any",
    "euresFlag": "any",
    "positionSchedule": "any",
    "positionOffering": "any",
    "maxResults": 30,
    "fetchDetails": false,
    "includeTranslations": false,
    "includeRawHtml": false,
    "requestLanguage": "en",
    "onlyChanges": true,
    "detectClosed": true,
    "stateKey": ""
};

// Run the Actor and wait for it to finish
const run = await client.actor("amygraphics/eu-jobs-monitor").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 = {
    "mode": "search",
    "searchQueries": [],
    "countries": [],
    "keywordScope": "EVERYWHERE",
    "publishedWithin": "any",
    "euresFlag": "any",
    "positionSchedule": "any",
    "positionOffering": "any",
    "maxResults": 30,
    "fetchDetails": False,
    "includeTranslations": False,
    "includeRawHtml": False,
    "requestLanguage": "en",
    "onlyChanges": True,
    "detectClosed": True,
    "stateKey": "",
}

# Run the Actor and wait for it to finish
run = client.actor("amygraphics/eu-jobs-monitor").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 '{
  "mode": "search",
  "searchQueries": [],
  "countries": [],
  "keywordScope": "EVERYWHERE",
  "publishedWithin": "any",
  "euresFlag": "any",
  "positionSchedule": "any",
  "positionOffering": "any",
  "maxResults": 30,
  "fetchDetails": false,
  "includeTranslations": false,
  "includeRawHtml": false,
  "requestLanguage": "en",
  "onlyChanges": true,
  "detectClosed": true,
  "stateKey": ""
}' |
apify call amygraphics/eu-jobs-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,amygraphics/eu-jobs-monitor"
        }
    }
}
```

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/iNy3GAYVRqvzJHXEf/builds/lUqIeI41TWuPgGDBp/openapi.json
