# English-speaking Jobs — DE,CH,NL - to be deleted not working (`pillowy_scraper_2pv/english-jobs-germany-netherlands`) Actor

Aggregates English-speaking and remote/work-from-anywhere jobs for Germany and Switzerland from free APIs (Arbeitnow, Remotive, Jobicy, RemoteOK, We Work Remotely, Adzuna) plus optional LinkedIn/Indeed/Xing/StepStone Actors.

- **URL**: https://apify.com/pillowy\_scraper\_2pv/english-jobs-germany-netherlands.md
- **Developed by:** [akaji Banpu](https://apify.com/pillowy_scraper_2pv) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.01 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## English-speaking Jobs — Germany & Switzerland

An Apify Actor that aggregates English-speaking job listings focused on
Germany and Switzerland, plus remote/work-from-anywhere jobs regardless of
country (Netherlands is still supported via `countries`, just no longer the
primary focus).

### Sources

**Free, direct API calls** (no key needed, no separate Actor cost, no known
legal/ToS issue with this kind of use):

- [Arbeitnow](https://www.arbeitnow.com/api/job-board-api)
- [Remotive](https://remotive.com/api/remote-jobs)
- [Jobicy](https://jobicy.com/api/v2/remote-jobs) — supports Germany, Switzerland, Netherlands via `geo=`
- [RemoteOK](https://remoteok.com/api) — 100%-remote listings. **Its own API response embeds an explicit terms clause: "please link back... and mention Remote OK as a source, so we get traffic back from your site. If you do not we'll have to suspend API access."** If you build anything user-facing on top of this Actor's output, credit RemoteOK and link back to the job's URL on remoteok.com.
- [We Work Remotely](https://weworkremotely.com/remote-jobs.rss) — 100%-remote listings via the RSS feed the site itself publishes for external consumption.
- [Adzuna](https://developer.adzuna.com/) (optional — needs a free `adzunaAppId`/`adzunaAppKey`) — officially supports Germany (`de`), Switzerland (`ch`), and Netherlands (`nl`)
- [Himalayas](https://himalayas.app/jobs/api) — free public JSON API, no key, documented, supports a `country` filter
- [The Muse](https://www.themuse.com/api/public/jobs) — free without a key (500 req/hr); its own `location` query param didn't reliably narrow results in testing, so this fetches broadly and filters client-side like Arbeitnow
- [Jooble](https://jooble.org/api/about) (optional — needs a free `joobleApiKey` from a signup form) — legitimate free API meant for exactly this use, but the free tier is a **lifetime cap of 500 requests total** (not per-day), so it's opt-in and should be used sparingly

RemoteOK, We Work Remotely, Himalayas, and The Muse are all global,
English-by-default remote/job boards, so — unlike Arbeitnow, a German board
mixing German/English postings — their results are **not** run through the
English-language filter; nearly everything on them already qualifies, and
the filter would mostly produce false negatives.

One official option investigated but not added: **EURES**, the EU/EFTA's
own job mobility network (Switzerland is a full member via a bilateral
agreement, confirmed). It has no sanctioned public API — only a
reverse-engineered, community-documented endpoint exists, which could
change or break without notice since it isn't actually meant for third-party
use. Worth revisiting if you want the reach of an official government
jobs database and are OK with that fragility.

**Composed from existing Apify Store Actors** (each is a separate,
pay-per-result Actor run):

- LinkedIn — default `curious_coder/linkedin-jobs-scraper`
- Indeed — default `borderline/indeed-scraper`
- Xing — default `fatihtahta/xing-jobs-scraper`
- StepStone — default `trev0n/stepstone-scraper` — covers Germany, Austria, Netherlands, and Belgium (only DE/NL are in our `countries` list; Switzerland is skipped, StepStone doesn't operate there). Filters for English-language ads directly via its own `adLanguage` param, so this source's results are more precisely English than the others in this section. **Verified against a real run** — `title`/`location`/`url`/`companyName` all confirmed correct.
- Robert Half — default `studio-amba/roberthalf-scraper` — a staffing agency covering Germany, Netherlands, and Switzerland (via its English-language `CH-en` locale). **Unverified** — every test attempt (from two different Apify accounts) was rejected pre-flight with "exceeds remaining usage," seemingly due to this Actor's own Store pricing configuration rather than the caller's resource request. Verify before relying on it.
- Relocate.me (visa-sponsored jobs) — default `khadinakbar/visa-sponsored-jobs-scraper` — international tech roles with explicit visa sponsorship and a full relocation package (visa services, flights, housing, language courses); covers Germany and Netherlands, not Switzerland. Every listing from this source is inherently visa-sponsored, so it's tagged as `"Relocate.me (Visa Sponsored)"` in the `source` field rather than adding a dedicated schema field for one source. **Verified against a real run** — note there's no single `location` field, it's split into separate `city`/`country` fields (handled in code).

#### Why compose existing Actors instead of scraping these sites directly

LinkedIn, Indeed, Xing, StepStone, Robert Half, and Relocate.me all prohibit
automated scraping in their Terms of Service and run active anti-bot
defenses (LinkedIn in particular has pursued legal action against
scrapers). Rather than writing bespoke scraping logic against those
defenses in this repo, `src/sources/{linkedin,indeed,xing,stepstone,
robert_half,visa_sponsored}.py` each call an existing, actively-maintained
Actor from the Apify Store over the Apify API (`Actor.call(...)`) and read
its output dataset. That plumbing (proxies, anti-bot handling,
markup-change maintenance) already lives in those Actors. You are still
bound by Apify's own platform terms and by whatever terms the upstream
Actor's author has agreed to — this doesn't remove the underlying legal
risk, it just avoids re-implementing scraping logic that the target sites
explicitly forbid. **These six sources are excluded from the default
`sources` list precisely because of that residual legal exposure** — the
free/direct-API sources above are the default, ToS-clean set.

You can swap in a different Actor for any of these via the
`linkedinActorId` / `indeedActorId` / `xingActorId` / `stepstoneActorId` /
`robertHalfActorId` / `visaSponsoredActorId` input fields.

Robert Half and Relocate.me both have small user counts on Apify (2 and 81
respectively) — lower track record than the others in this list, so treat
their output field names as even less certain until you've run a real test.

Every composed-Actor call passes explicit `memory_mbytes=512`/
`timeout_secs=180` (see `src/sources/apify_actor.py`) so cost stays bounded
regardless of the called Actor's own resource defaults — found necessary
during testing when one Actor's default allocation alone made Apify's
pre-flight cost estimate exceed a small account's remaining monthly free
credit, even before any real per-result charges applied. Each source is
also isolated in `main.py` (`_safe()` wrapper) so one failing composed
Actor — insufficient balance, the Actor being down, a bad input field —
can never discard the other sources' already-collected results; a failure
is logged and that source just comes back empty.

Two boards considered but not added: **Glassdoor** and **Monster** both have
usable Apify Store Actors, but weren't wired in this round — Monster in
particular describes itself as US-focused, a weak fit for Germany/Switzerland.
Add them the same way (a new `src/sources/<site>.py` following the
`stepstone.py` pattern) if you want them later.

Four expat/English-specific job sites were also investigated
(iamexpat.nl, undutchables.nl, thelocal.de/jobs, berlinstartupjobs.com) as
candidates for a custom scraper. Only undutchables.nl came back clear (no
scraping clause in its ToS, permissive robots.txt); the other three either
explicitly prohibit scraping in their terms or have no real job-listing
surface left to scrape. None are implemented yet — see git history for the
full per-site findings if you want to revisit undutchables.nl.

**Known limitation:** the Actor-composed sources (LinkedIn/Indeed/Xing/
StepStone/Robert Half/Relocate.me) are not run through the English-language
filter, since the exact output fields of those third-party Actors aren't
verified yet — see the NOTE in each module. StepStone is the exception:
its `adLanguage: "en"` input param filters for English ads server-side.

### Input

See [`.actor/input_schema.json`](.actor/input_schema.json). Key fields:

| Field | Description |
|---|---|
| `keyword` | Job title/keyword filter, e.g. `"python developer"` |
| `countries` | `["germany", "switzerland"]` by default; `"netherlands"` also supported |
| `remoteAnywhere` | Default `true` — also include jobs tagged Worldwide/Anywhere/Europe/EMEA/Flexible regardless of `countries`, from arbeitnow/remotive/remoteok/weworkremotely/himalayas/themuse |
| `sources` | Which sources to query. Default is the free, ToS-clean set: `arbeitnow`, `remotive`, `jobicy`, `remoteok`, `weworkremotely`, `himalayas`, `themuse`. `jooble` is free but opt-in (signup + 500-request lifetime cap). `linkedin`/`indeed`/`xing`/`stepstone` are opt-in and cost money per run — see the legal note above before enabling |
| `maxItemsPerSource` | Cap on results per paid Actor — keep this low while testing |
| `adzunaAppId` / `adzunaAppKey` | Only needed if `adzuna` is in `sources` |
| `joobleApiKey` | Only needed if `jooble` is in `sources` |
| `linkedinActorId` / `indeedActorId` / `xingActorId` / `stepstoneActorId` / `robertHalfActorId` / `visaSponsoredActorId` | Override which Store Actor to call |

### Local development

```bash
pip install -r requirements.txt
apify run
```

`apify run` needs the [Apify CLI](https://docs.apify.com/cli/) installed and
an `APIFY_TOKEN` configured (`apify login`) so it can call the LinkedIn/
Indeed/Xing/StepStone/Robert Half/Relocate.me sub-Actors if those sources
are enabled. The free sources (the default) work without a token.

Before enabling `linkedin`/`indeed`/`xing`/`stepstone`/`roberthalf`/
`visasponsored`: verify the output field names assumed in the corresponding
`src/sources/*.py` module (`pick(item, "title", ...)` etc.) against a small
test run of each upstream Actor in the Apify Console — Store search doesn't
expose output schemas, only input schemas, so those were written
defensively but unverified against live output.

### Deploy to Apify

```bash
apify login
apify push
```

# Actor input Schema

## `keyword` (type: `string`):

Job title or keyword to search for, e.g. 'python developer'. Leave empty to fetch broadly.

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

Countries to search in. StepStone only supports germany (also Austria/Belgium, not modeled here); all others support all three.

## `remoteAnywhere` (type: `boolean`):

Also include jobs that don't name a specific country — tagged Worldwide/Anywhere/Europe/EMEA/Flexible — regardless of the `countries` filter above. Applies to arbeitnow, remotive, remoteok, weworkremotely, himalayas, and themuse.

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

Which job sources to query. Direct-API sources (arbeitnow/remotive/jobicy/adzuna/remoteok/weworkremotely/himalayas/themuse/jooble) are free, no legal/ToS exposure — each either has no restriction on this kind of use, or (RemoteOK) an explicit API-terms-of-service permitting it with attribution. jooble needs a free signup key (joobleApiKey) and has a lifetime cap of 500 requests on its free tier, so it's opt-in. linkedin/indeed/xing/stepstone/roberthalf/visasponsored each run a separate paid Apify Store Actor composed against a site whose own Terms of Service prohibit scraping directly — see README before enabling those.

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

Cap on how many results to request from each of linkedin/indeed/xing (these are pay-per-result Actors — keep this modest while testing).

## `adzunaAppId` (type: `string`):

Optional. Only needed if 'adzuna' is included in sources. Get a free key at https://developer.adzuna.com/

## `adzunaAppKey` (type: `string`):

Optional. Only needed if 'adzuna' is included in sources.

## `joobleApiKey` (type: `string`):

Optional. Only needed if 'jooble' is included in sources. Get a free key (500-request lifetime cap) at https://jooble.org/api/about

## `linkedinActorId` (type: `string`):

Apify Store Actor to call for LinkedIn. Default is a well-reviewed public Actor; override if you prefer another.

## `indeedActorId` (type: `string`):

Apify Store Actor to call for Indeed.

## `xingActorId` (type: `string`):

Apify Store Actor to call for Xing.

## `stepstoneActorId` (type: `string`):

Apify Store Actor to call for StepStone. Covers Germany, Austria, Netherlands, and Belgium (Austria/Belgium aren't in our countries list); Switzerland is skipped since StepStone doesn't operate there. Filters for English-language ads directly.

## `robertHalfActorId` (type: `string`):

Apify Store Actor to call for Robert Half. Covers Germany, Netherlands, and Switzerland (via its English-language CH-en locale).

## `visaSponsoredActorId` (type: `string`):

Apify Store Actor to call for Relocate.me visa-sponsored international tech jobs. Covers Germany and Netherlands; Relocate.me doesn't cover Switzerland.

## Actor input object example

```json
{
  "keyword": "",
  "countries": [
    "germany",
    "switzerland"
  ],
  "remoteAnywhere": true,
  "sources": [
    "arbeitnow",
    "remotive",
    "jobicy",
    "remoteok",
    "weworkremotely",
    "himalayas",
    "themuse"
  ],
  "maxItemsPerSource": 25,
  "linkedinActorId": "curious_coder/linkedin-jobs-scraper",
  "indeedActorId": "borderline/indeed-scraper",
  "xingActorId": "fatihtahta/xing-jobs-scraper",
  "stepstoneActorId": "trev0n/stepstone-scraper",
  "robertHalfActorId": "studio-amba/roberthalf-scraper",
  "visaSponsoredActorId": "khadinakbar/visa-sponsored-jobs-scraper"
}
```

# Actor output Schema

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

Job listings from every enabled source, deduplicated by URL.

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("pillowy_scraper_2pv/english-jobs-germany-netherlands").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("pillowy_scraper_2pv/english-jobs-germany-netherlands").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 '{}' |
apify call pillowy_scraper_2pv/english-jobs-germany-netherlands --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,pillowy_scraper_2pv/english-jobs-germany-netherlands"
        }
    }
}
```

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/D2bHdYKToz6nfWgHV/builds/XNWVHOKDAalXS5qwk/openapi.json
