# European Public Job Boards: France, Sweden & Norway (`gefese/european-public-jobs-scraper`) Actor

Public job postings from the national employment services of France (France Travail), Sweden (Platsbanken) and Norway (NAV Arbeidsplassen) in one schema. Unofficial, not affiliated with any of these agencies.

- **URL**: https://apify.com/gefese/european-public-jobs-scraper.md
- **Developed by:** [Gonzalo Fajardo](https://apify.com/gefese) (community)
- **Categories:** Jobs
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.84 / 1,000 result items

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## European Public Job Boards: France, Sweden & Norway

Unofficial, not affiliated with France Travail, Arbetsförmedlingen or NAV. This Actor reads public job postings from the national public employment services of three countries and returns them in **one common schema**, so you can compare or merge job markets without writing three parsers.

### What does it do and which fields does it return?

| Country | Source | Where the data comes from |
|---|---|---|
| France | France Travail (`candidat.francetravail.fr`) | The site's public offer search by place code and the public offer pages |
| Sweden | Arbetsförmedlingen Platsbanken (`arbetsformedlingen.se`) | The open JobTech JobSearch API, no key needed |
| Norway | NAV Arbeidsplassen (`arbeidsplassen.nav.no`) | NAV's official public job vacancy feed |

Every dataset item is one job posting with these fields (fields a source does not publish are `null`):

`source`, `country`, `jobId`, `url`, `title`, `employerName`, `location`, `municipality`, `region`, `regionCode`, `postalCode`, `latitude`, `longitude`, `contractType`, `workingHours`, `fullTime`, `salaryText`, `publishedAt`, `expiresAt`, `occupation`, `occupationCode`, `occupationScheme` (SSYK 2012 for Sweden, STYRK-08 for Norway), `occupationField`, `description`, `numberOfPositions`, `language`, `scrapedAt`.

No contact persons are returned: recruiter and contact-person records (names, e-mail addresses, phone numbers) are never read. In the description text, every line or sentence that holds an e-mail address, a social handle, a phone number or a "contact person" phrase is replaced by `[removed]`.

### How do I run it?

1. Open the Actor and press **Start**. The prefilled input needs no account details and no keys.
2. The prefill reads 10 jobs from each source: France Travail for department 75D (Paris), the newest Platsbanken ads for Sweden, and the newest NAV ads for Norway. In the local test run this produced 30 items in about 33 seconds.
3. Open the **Overview** table in the dataset, or export JSON, CSV or Excel.
4. To adapt it, change `franceLocations` (place codes such as `69D`, `52R` or `75117`), add a `query` for Sweden and Norway, set Swedish region or municipality codes or Norwegian municipality names, and raise `maxItemsPerSource`.

**Why does France not support keywords?** The robots.txt of France Travail disallows keyword search (`motsCles=`), so this Actor never sends one. France is searched by place code only. The `query` field is applied to Sweden (title, description and employer name) and Norway (every word must appear in the ad title or employer name).

### How much does it cost to scrape public job boards?

You pay **0.0012 USD per job** (one `result` event per dataset item) and nothing for starting a run. Example: 1,000 jobs = 1.20 USD. The prefill (30 jobs) costs 0.036 USD at that price. Apify platform usage is separate from the per-result price and depends on your plan.

### Input example

```json
{
  "sources": ["france_travail", "arbetsformedlingen", "nav"],
  "query": "",
  "franceLocations": ["75D"],
  "swedenRegions": [],
  "swedenMunicipalities": [],
  "norwayMunicipalities": [],
  "norwayLookbackDays": 1,
  "maxItemsPerSource": 10,
  "includeDescription": true,
  "franceFetchDetailPages": true
}
```

### Output example

Three real items from a local run on 2026-10-15, one per source. The Norwegian description is shortened here for the README; the Actor returns the full text.

```json
[
  {
    "source": "france_travail",
    "country": "FR",
    "jobId": "215GWKR",
    "url": "https://candidat.francetravail.fr/offres/recherche/detail/215GWKR",
    "title": "Assistant administratif (H/F)",
    "employerName": "MANPOWER FRANCE",
    "location": "75 - Paris 17e Arrondissement",
    "municipality": "Paris 17e Arrondissement",
    "region": null,
    "regionCode": "75",
    "postalCode": null,
    "latitude": null,
    "longitude": null,
    "contractType": "CDI",
    "workingHours": "Temps plein - 35H/semaine",
    "fullTime": true,
    "salaryText": "Annuel de 30500.0 Euros",
    "publishedAt": "2026-10-15",
    "expiresAt": null,
    "occupation": null,
    "occupationCode": null,
    "occupationScheme": null,
    "occupationField": null,
    "description": "Manpower CABINET EXPERTS CORPORATE PARIS recherche pour son client, un acteur du secteur Assurance, un Assistant administratif Polyvalent (H/F)\nNotre client est l'une des caisses de retraite...",
    "numberOfPositions": null,
    "language": "fr",
    "scrapedAt": "2026-10-15T12:29:39Z"
  },
  {
    "source": "arbetsformedlingen",
    "country": "SE",
    "jobId": "31601759",
    "url": "https://arbetsformedlingen.se/platsbanken/annonser/31601759",
    "title": "Sjuksköterska sökes till Salem!",
    "employerName": "Viva Bemanning AB",
    "location": "Salem, Stockholms län",
    "municipality": "Salem",
    "region": "Stockholms län",
    "regionCode": "01",
    "postalCode": null,
    "latitude": null,
    "longitude": null,
    "contractType": "Behovsanställning",
    "workingHours": null,
    "fullTime": null,
    "salaryText": null,
    "publishedAt": "2026-10-15T14:28:30+02:00",
    "expiresAt": "2026-10-29T23:59:59+01:00",
    "occupation": "Grundutbildade sjuksköterskor",
    "occupationCode": "2221",
    "occupationScheme": "SSYK 2012",
    "occupationField": "Hälso- och sjukvård",
    "description": "Sjuksköterska sökes till Salem!\nViva Bemanning söker legitimerad sjuksköterska under perioden: v.44-08.\nVi söker dig som:\när legitimerad sjuksköterska\nhar relevant erfarenhet inom hälso- och sjukvård\nhar goda kunskaper i svenska\nkan lämna referenser som styrker din kompetens\n\nDu är ansvarstagande, trygg i din yrkesroll och har ett professionellt bemötande.\n[removed]\nAnsökan\n[removed]\n[removed]\nHemsida: www.vivabemanning.se\nVälkommen med din ansökan!",
    "numberOfPositions": 1,
    "language": "sv",
    "scrapedAt": "2026-10-15T12:29:48Z"
  },
  {
    "source": "nav",
    "country": "NO",
    "jobId": "312edfed-0bef-4c36-b1e3-5ebe9c79fd41",
    "url": "https://arbeidsplassen.nav.no/stillinger/stilling/312edfed-0bef-4c36-b1e3-5ebe9c79fd41",
    "title": "Sport Outlet Hamar søker etter 20% butikkmedarbeider og flere ringevikarer",
    "employerName": "Sport Outlet Hamar",
    "location": "Hamar, Innlandet",
    "municipality": "Hamar",
    "region": "Innlandet",
    "regionCode": null,
    "postalCode": "2316",
    "latitude": null,
    "longitude": null,
    "contractType": "Annet",
    "workingHours": "Deltid",
    "fullTime": false,
    "salaryText": null,
    "publishedAt": "2026-10-15T00:00:00+02:00",
    "expiresAt": "2026-11-01T00:00:00+01:00",
    "occupation": "Butikkmedarbeidere",
    "occupationCode": "5223",
    "occupationScheme": "STYRK-08",
    "occupationField": "Salg og service",
    "description": "Sport Outlet Hamar søker 2 utadvente og engasjerte butikkmedarbeidere i 20% stilling. Vi har også behov for flere ringevikarer og håper du vil være med på laget.\n\nVi ser etter deg som:\n\n- Har et glimt i øye og et godt humør\n- Liker å få ansvarsoppgaver\n- Har sans for det visuelle\n- Er en dyktig selger og elsker kundebehandling\n- Er strukturert og ryddig\n- Er lidenskapelig opptatt av sport og friluftsliv.\n- Er over 18 … [shortened for this README]",
    "numberOfPositions": 3,
    "language": "nb",
    "scrapedAt": "2026-10-15T12:30:00Z"
  }
]
```

### Limitations and what a small run can do

- **France description is short.** France Travail fills in the full offer text with a browser script from a separate service that this Actor does not call. The `description` for France is the roughly 200-character snippet the site shows in its search list. Salary text and weekly hours come from the offer page. There is no occupation (ROME) code, region name, postal code, expiry date or position count for France in the data this Actor reads.
- **France dates are approximate.** The site shows "published today / yesterday / N days ago"; `publishedAt` is the calendar date computed from that text.
- **France needs place codes.** No keyword search (see above). Each page of 20 offers and each offer page is fetched at least one second apart, so a France run takes about one second per job when `franceFetchDetailPages` is on. Switch it off to skip salary and hours and the run needs far fewer requests.
- **Sweden:** the API returns at most about 2,000 ads per query; narrow the query or the place filter beyond that. Salary is only filled when the employer wrote a salary text.
- **Norway:** NAV's feed lists every change to every ad in time order and cannot filter by place or keyword. The Actor reads the feed from `norwayLookbackDays` back (up to 40 pages per run), keeps the newest still-active ads, and filters by municipality and keyword on the title and employer name. Use a longer window when you filter. The feed has no salary field, and the language is guessed from the text (`null` when unsure).
- **Snapshots:** each run is a snapshot taken at `scrapedAt`. NAV's terms for its feed require republished ads to be removed when they become inactive and updated when they change, so refresh data you keep or show to others.
- **Free plan:** the default input returns 30 jobs for 0.036 USD of result charges. Larger runs are limited by your plan's credit and by the maximum charge you set for the run; the Actor stops cleanly when that limit is reached.

### FAQ

**Is this official?** No. It is an independent Actor and is not affiliated with, endorsed by or sponsored by France Travail, Arbetsförmedlingen or NAV.

**Does it respect robots.txt?** Yes. At the start of each source the Actor reads the host's robots.txt and stops that source if the paths it needs are disallowed or the file cannot be read (for the two API hosts a 404 means no restrictions; France Travail's file must be readable). Requests to each host are sequential, with at least one second between them.

**Which licences apply?** The Swedish ads are published under CC0 according to the JobTech API. NAV publishes the feed under its own terms of use for the job-ad API. France Travail's general conditions say that the job offers it publishes may be reused for any purpose, except contact data. Check the terms of each source for your use case.

**Does it need a login or proxy?** No. It uses plain HTTP requests, no browser and no proxy.

**Something is wrong or missing?** Please report it in the Issues tab of this Actor on Apify.

# Actor input Schema

## `sources` (type: `array`):

Which public job boards to read: france_travail (France Travail), arbetsformedlingen (Sweden, Platsbanken) and nav (Norway, NAV Arbeidsplassen). Sources run one after another.

## `query` (type: `string`):

Free-text keyword. Sweden: searched in ad title, description and employer name by the JobTech API. Norway: every word must appear in the ad title or employer name. Not applied to France, because France Travail's robots.txt disallows keyword search; use the place codes below instead. Leave empty for the newest ads.

## `franceLocations` (type: `array`):

France Travail place codes as the site uses them in its search address: a department such as 75D (Paris) or 69D (Rhône), a region such as 52R (Pays de la Loire), or a commune code such as 75117 or 69123. No keywords.

## `swedenRegions` (type: `array`):

Two-digit county codes, for example 01 (Stockholm county) or 14 (Västra Götaland). Leave empty for all of Sweden.

## `swedenMunicipalities` (type: `array`):

Four-digit municipality codes, for example 0180 (Stockholm) or 1480 (Göteborg). Leave empty for all of Sweden.

## `norwayMunicipalities` (type: `array`):

Municipality names as NAV spells them, for example OSLO or BERGEN (case-insensitive). The feed cannot filter by place, so this is applied to the ads read from the feed. Leave empty for all of Norway.

## `norwayLookbackDays` (type: `integer`):

The NAV feed lists changes in time order. The actor reads the feed from this many days back and keeps the newest still-active ads. Use a longer window when you filter by keyword or municipality.

## `maxItemsPerSource` (type: `integer`):

Stops after this many jobs for each selected source. Each job is one billed result.

## `includeDescription` (type: `boolean`):

Adds the plain-text description. For France Travail this is the short text shown in the search list (about 200 characters); for Sweden and Norway it is the full ad text. E-mail addresses and phone numbers are replaced by \[removed].

## `franceFetchDetailPages` (type: `boolean`):

Reads one extra page per French job (at least 1 second apart) to add salary text and weekly hours. Turn off for faster runs.

## Actor input object example

```json
{
  "sources": [
    "france_travail",
    "arbetsformedlingen",
    "nav"
  ],
  "query": "",
  "franceLocations": [
    "75D"
  ],
  "swedenRegions": [],
  "swedenMunicipalities": [],
  "norwayMunicipalities": [],
  "norwayLookbackDays": 1,
  "maxItemsPerSource": 10,
  "includeDescription": true,
  "franceFetchDetailPages": true
}
```

# 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 = {
    "sources": [
        "france_travail",
        "arbetsformedlingen",
        "nav"
    ],
    "query": "",
    "franceLocations": [
        "75D"
    ],
    "swedenRegions": [],
    "swedenMunicipalities": [],
    "norwayMunicipalities": [],
    "norwayLookbackDays": 1,
    "maxItemsPerSource": 10,
    "includeDescription": true,
    "franceFetchDetailPages": true
};

// Run the Actor and wait for it to finish
const run = await client.actor("gefese/european-public-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 = {
    "sources": [
        "france_travail",
        "arbetsformedlingen",
        "nav",
    ],
    "query": "",
    "franceLocations": ["75D"],
    "swedenRegions": [],
    "swedenMunicipalities": [],
    "norwayMunicipalities": [],
    "norwayLookbackDays": 1,
    "maxItemsPerSource": 10,
    "includeDescription": True,
    "franceFetchDetailPages": True,
}

# Run the Actor and wait for it to finish
run = client.actor("gefese/european-public-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 '{
  "sources": [
    "france_travail",
    "arbetsformedlingen",
    "nav"
  ],
  "query": "",
  "franceLocations": [
    "75D"
  ],
  "swedenRegions": [],
  "swedenMunicipalities": [],
  "norwayMunicipalities": [],
  "norwayLookbackDays": 1,
  "maxItemsPerSource": 10,
  "includeDescription": true,
  "franceFetchDetailPages": true
}' |
apify call gefese/european-public-jobs-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,gefese/european-public-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/Dfs0IRZW0YyLC3Oeb/builds/e8OrBNkY6Vv2ab0y1/openapi.json
