# Arbeitnow Jobs Scraper: Job Listings with Filters (`mrbridge/arbeitnow-jobs-scraper`) Actor

Scrape job listings from Arbeitnow with filters by keyword, location, tag, job type, visa sponsorship and posting date. One row per job with title, company, location, job types, tags, publication time, canonical URL and description as text and HTML. Export to JSON, CSV or Excel.

- **URL**: https://apify.com/mrbridge/arbeitnow-jobs-scraper.md
- **Developed by:** [MrBridge](https://apify.com/mrbridge) (community)
- **Categories:** Jobs, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.70 / 1,000 job listings

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

## Arbeitnow Jobs Scraper

Arbeitnow Jobs Scraper collects job listings from the Arbeitnow job board (https://www.arbeitnow.com). Filter by search term, location, tag, job type, remote flag and age, and get one clean row per job, ready for JSON, CSV or Excel. No API key is needed.

Set `maxPages` to 1 and `maxItems` to 10: the Actor reads the newest page of the feed and returns the 10 most recent listings with title, company, location, remote flag, tags, publication time, the canonical Arbeitnow URL and the job description as text and as HTML. Add the search term `python` to keep only the listings that mention it. E-mail addresses and phone numbers found in the descriptions are masked, links are kept.

### What is Arbeitnow Jobs Scraper?

Arbeitnow publishes a public job board feed. The Actor reads that feed through its public API, applies your filters, removes duplicates and returns structured rows. The feed holds the newest listings of every kind, about 300 per page, newest first. Most of them are on-site jobs, only about 3 in 100 are flagged remote, which is why `remoteOnly` is off by default. Many listings are in Germany, in German or in English.

#### What data can I extract with it?

One row per job: source, job ID (the Arbeitnow slug), title, company, location, remote flag, job types, tags, publication time, canonical Arbeitnow URL, description as text and as HTML, scrape time. Arbeitnow serves no salary, no company logo and no seniority level, so those columns are always empty.

#### How can I use the results?

- Feed a job board or a newsletter with fresh listings and a link back to each Arbeitnow page.
- Track which companies hire for a skill, a city or a language.
- Build a list of employers that sponsor a visa or relocation.
- Send new matches for a search term to a spreadsheet, a chatbot or a notification service.

### Data source and terms

All rows come from Arbeitnow (https://www.arbeitnow.com), through its public job board API. Arbeitnow asks anyone who reuses the data to follow these terms, and so does this Actor:

- **Credit Arbeitnow** with a direct link to https://www.arbeitnow.com wherever you republish the data.
- **Keep the canonical URL.** Every row carries it in the `url` field. Send applicants to that URL, never to a copy.
- **The feed is updated every hour.** A schedule faster than hourly returns the same rows.
- **3 requests per minute at most.** Arbeitnow limits its API to 3 requests per minute. The Actor waits 30 seconds between two requests and never retries, so a run reads a page every 30 seconds. `maxPages` defaults to 3 for that reason.
- **No salary, no logo.** Arbeitnow does not serve them.
- **Contacts are masked, links are kept.** E-mail addresses and phone numbers found in descriptions are replaced by neutral markers before the rows are returned. Links stay, with their `href` in `descriptionHtml`. A `mailto:` or `tel:` anchor, and a link that carries an e-mail address, is replaced by its text. A bare link of that kind in a text becomes `[link removed]`. Phone numbers are not looked for inside a URL.
- **Scope.** By default every listing of the pages read is returned, remote or not. Set `remoteOnly` to keep only the listings Arbeitnow flags as remote. The Actor reads the newest pages only, older listings are not covered.

The `OUTPUT` record of each run repeats the coverage note and the attribution to give to Arbeitnow.

### How to use it

1. Open the Actor in Apify Console.
2. Enter search terms if you want to narrow the feed, and set `maxPages` and `maxItems`.
3. Start the run. Rows appear in the dataset as they are delivered.

### Input

| Field | Meaning | Default |
|---|---|---|
| `searchTerms` | Up to 10 terms, 50 bytes at most each. A job is kept when one term appears in its title, tags or description | empty (all) |
| `locations` | Keep jobs whose location contains one of these texts, case-insensitive | empty (all) |
| `tags` | Keep jobs with one of these Arbeitnow tags (exact match, case-insensitive) | empty (all) |
| `jobTypes` | Keep jobs with one of these types as Arbeitnow writes them (exact match, case-insensitive) | empty (all) |
| `postedWithinDays` | Keep jobs published in the last N days (1 to 365) | empty |
| `remoteOnly` | Keep only the listings flagged remote | `false` |
| `visaSponsorship` | Ask Arbeitnow for the visa sponsoring listings only. The filter is applied by Arbeitnow | `false` |
| `maxPages` | Pages of the feed to read, 1 to 20 (one page every 30 seconds) | 3 |
| `maxItems` | Hard cap on jobs returned and billed, 1 to 10000 | 100 |
| `includeDescription` | Add the description as text and HTML | `true` |

The Console form pre-fills `searchTerms` with `python`, `maxPages` with 1 and `maxItems` with 10. Example input, used for the sample row below (the stored page holds no Python listing, so the example has no search term):

```json
{
  "maxPages": 1,
  "maxItems": 10
}
```

Terms, locations, tags and job types are lowercased, deduplicated and sorted. `searchTerms`, `locations`, `tags`, `jobTypes`, `remoteOnly` and `postedWithinDays` are applied by the Actor on the pages it reads, so a rare filter needs more pages. `visaSponsorship` is the only filter applied by Arbeitnow, which then returns a different list with a deeper time range. The `OUTPUT` record says how many pages were read.

Invalid input ends the run successfully with one `BAD_INPUT` row in the `errors` dataset and no job. It covers an empty or too long term, more than 10 terms, and a value of the wrong type. A page count or an item cap out of range never reaches the Actor: the platform refuses it before the run starts.

### Output

Jobs go to the default dataset, one row per job, in the order Arbeitnow gives them (newest first). Problems go to a separate `errors` dataset and are never billed. The `OUTPUT` record of the key-value store summarizes the run.

| Field | Type | Meaning |
|---|---|---|
| `source` | string | Always `Arbeitnow` |
| `jobId` | string | Arbeitnow slug of the job |
| `title` | string | Job title, spaces normalized (see below) |
| `company` | string | Company name |
| `companyLogo` | null | Always empty, Arbeitnow serves no logo |
| `location` | string or null | Location as Arbeitnow writes it |
| `remote` | boolean | The remote flag of Arbeitnow |
| `jobTypes` | array | Types as Arbeitnow writes them, in English, German or Dutch, sometimes mixed with seniority |
| `level` | null | Always empty |
| `categories` | array | Always empty |
| `tags` | array | Arbeitnow tags |
| `salaryMin`, `salaryMax` | null | Always empty, Arbeitnow serves no salary |
| `salaryCurrency`, `salaryPeriod`, `salaryText` | null | Always empty |
| `publishedAt` | string or null | Publication time, ISO 8601 in UTC |
| `url` | string | Canonical Arbeitnow page of the job |
| `descriptionText` | string or null | Description as text, line breaks kept |
| `descriptionHtml` | string or null | Description as HTML |
| `scrapedAt` | string | Time the row was built, ISO 8601 in UTC |

`descriptionText` and `descriptionHtml` are absent when `includeDescription` is `false`. The other fields are always present, with `null` when Arbeitnow gives no value.

`title`, `company` and `location` are cleaned the same way: HTML entities are decoded, tags are removed, and any run of spaces, tabs or line breaks becomes a single space, trimmed at both ends. So `Sales Manager  - Mid Market Paris` (two spaces) is returned as `Sales Manager - Mid Market Paris`. The title is therefore not byte for byte the one Arbeitnow serves. In `location` a comma is also followed by exactly one space, so `Canada,  USA` is returned as `Canada, USA`.

This is one real row from the example input above, built by the Actor from a stored Arbeitnow response:

```json
{
  "source": "Arbeitnow",
  "jobId": "senior-software-developer-dortmund-389547",
  "title": "(Senior) Software Developer (m/w/d)",
  "company": "Loopario",
  "companyLogo": null,
  "location": "Dortmund",
  "remote": false,
  "jobTypes": [],
  "level": null,
  "categories": [],
  "tags": [
    "Software Development"
  ],
  "salaryMin": null,
  "salaryMax": null,
  "salaryCurrency": null,
  "salaryPeriod": null,
  "salaryText": null,
  "publishedAt": "2026-10-06T13:01:02.000Z",
  "url": "https://www.arbeitnow.com/jobs/companies/loopario/senior-software-developer-dortmund-389547",
  "descriptionText": "Lust auf spannende Software-Projekte?\nDann bist du bei uns genau richtig! Wir suchen einen engagierten Senior Software Developer (m/w/d) mit Erfahrung in .NET / C#.\nW",
  "descriptionHtml": "<p><strong>Lust auf spannende Software-Projekte?</strong></p>\n<p>Dann bist du bei uns genau richtig! Wir suchen einen engagierten Senior Software Developer (m/w/d) mit Erfahrung in .NET / C#.</p>\n<p>W",
  "scrapedAt": "2026-10-26T18:53:00.000Z"
}
```

The example comes from a test fixture, so the description is cut after a few lines and the HTML stops in the middle of a tag pair. A real run returns the full description.

### Limits

- `maxItems` stops the run once that many jobs have been delivered. The default is 100.
- `maxPages` caps the pages read, 20 at most. A page holds about 300 listings, and the run stops earlier when Arbeitnow announces no next page. A run of 20 pages lasts at least 10 minutes.
- Requests are spaced by 30 seconds, retries and failed requests included, so a run never sends more than the 3 requests per minute that Arbeitnow allows.
- Each request is bounded at 110 seconds, answer included, so a stalled answer cannot hold the run. The later pages of the feed weigh about 3 MB and Arbeitnow serves them slowly, sometimes over a minute.
- A network error, a timeout, a truncated answer or a server error (5xx) is retried once: the retry starts after the longer of Retry-After (or the backoff) and the limiter interval, counted from the start of the failed attempt. If the retry fails too, the run writes one `FETCH_FAILED` row, keeps the jobs already delivered and ends with `stoppedBy` set to `fetchFailed`.
- A 429 is never retried, because a retry would use up the rate limit. If Arbeitnow answers 429, the run ends there with a `RATE_LIMITED` row.
- If Arbeitnow answers 400 to a request, the run stops with one `BAD_INPUT` row that carries the HTTP status and the message, and ends SUCCEEDED with `stoppedBy` set to `badInput`.
- After 3 blocked responses in a row the run stops cleanly and ends SUCCEEDED.
- Each run is a snapshot of the newest pages of the feed at that moment.

### Billing

One event is billed per delivered job. Errors, duplicates and filtered-out listings are never billed. The Pricing tab of the Actor shows the current prices.

When the platform moves a run to another server or restarts it while it works, the run picks up at the page it was reading. It saves its progress after every page, and on a restart it first reads the jobs already in its dataset: a job already returned is neither returned nor billed again, and the `OUTPUT` counters cover the whole run. In the rare case where the run stops between saving a job and billing it, that job is delivered once and never billed. The feed changes every hour, so a listing can slip from a page not yet read to a page already read during the move and be missed.

### Run summary and early stops

`stoppedBy` in the `OUTPUT` record says why a run ended early. It keeps the first reason only, so a `pageBudget` stop hides a `maxItems` or `chargeLimit` stop that would have come later.

- `null`: all the requested work was done.
- `maxItems`: the `maxItems` cap was reached.
- `chargeLimit`: the maximum charge set for the run was reached.
- `deadline`: the run reached its internal time limit, 90 seconds before the run timeout. The request in flight is abandoned and the jobs already delivered stay.
- `circuitOpen`: too many blocked responses in a row.
- `pageBudget`: the page budget of the run was spent.
- `badInput`: Arbeitnow answered 400 to a request.
- `fetchFailed`: a page could not be fetched, even after its retry. That page and the next ones were not read, and the `coverageNote` says "The walk ended on an error after page N".

Two fields show how far a run went: `pagesPlanned` is the number of pages the run planned to read (`maxPages`), and `lastPageRead` is the number of the last page read (1 is the newest page), or `null` when no page was read. When `lastPageRead` is below `pagesPlanned` and `stoppedBy` is `null`, check `errorsByKind`: if it is empty, the run reached the end of the feed. Otherwise the reason is there, for example `RATE_LIMITED` (Arbeitnow answered 429), `TARGET_BLOCKED` or `SCHEMA_CHANGED`, and the `errors` dataset has the row. A non-empty `errorsByKind` does not always mean that the run stopped: a `LISTING_INVALID` row skips one listing and stops nothing.

`pageTimings` lists the pages fetched, in reading order and 20 at most: `page` (its number, 1 is the newest), `attempts` (HTTP requests it took), `ms` (from the start of its first request to the end of the answer, a retry included) and `bytes` (size of the answer, 0 when the request failed). A page over a minute is slow on the Arbeitnow side. Expect a run to last about 30 seconds per page plus the emission of the jobs, longer when a page is slow. The default memory is 512 MB.

`pagesRereadAfterResume` counts the pages read a second time because the run was moved or restarted while it was applying them, usually 0 and one page at most per restart. These pages are also counted in `pagesRead`, and their jobs already returned in `duplicatesSkipped`.

Changes in 0.1.7: `OUTPUT` has the new field `pagesRereadAfterResume`. Input and rows are unchanged.

Changes in 0.1.5: `OUTPUT` has the new field `pageTimings`.

Changes in 0.1.2: `stoppedBy` can be `fetchFailed` (a run that ended on a failed page used to say `null`), and `OUTPUT` has the two new fields `lastPageRead` and `pagesPlanned`. The other fields keep their names and meaning.

Every business error is written to the `errors` dataset with a `kind`: `BAD_INPUT`, `TARGET_BLOCKED`, `RATE_LIMITED`, `FETCH_FAILED`, `UPSTREAM_ERROR`, `SCHEMA_CHANGED` or `LISTING_INVALID`. The `OUTPUT` record counts them in `errorsByKind`. The `OUTPUT` record is written at the end of every run, even one that hits its time limit. If the platform refuses to store a row, the run ends as FAILED, and the `OUTPUT` record still holds the counters reached until then.

### Frequently asked questions

#### Why do I get fewer jobs than `maxItems`?

The Actor reads at most `maxPages` pages, and your filters narrow what those pages hold. Only about 3 listings in 100 are remote, so `remoteOnly` needs more pages. When all the pages are read, the run ends with `stoppedBy` set to `null`.

#### Can I use Arbeitnow Jobs Scraper via MCP?

Yes. Any Apify Actor can be called from an MCP client through the Apify MCP server. See the [Apify MCP documentation](https://docs.apify.com/platform/integrations/mcp) for the setup. For a ready-made MCP server over remote job boards, use [Remote Jobs MCP](https://apify.com/mrbridge/remote-jobs-mcp).

#### Your feedback

We're always working on improving the performance of our Actors. So if you've got any technical feedback for Arbeitnow Jobs Scraper or simply found a bug, please create an issue on the Actor's [Issues tab](https://apify.com/mrbridge/arbeitnow-jobs-scraper/issues).

More free tools, scrapers, MCP servers and data studies from the same team: [mr-bridge.com](https://mr-bridge.com).

### Legal & responsible use

The data comes from Arbeitnow (https://www.arbeitnow.com). Link back to Arbeitnow and name it as the source when you republish the data, and keep the canonical Arbeitnow URL of each job. Arbeitnow has its own terms of use, and it may restrict automated access or commercial reuse. Before you rely on the output, in particular for commercial use, confirm that your use is permitted by those terms. You remain responsible for how you use the results and for complying with the source's terms and with the laws that apply to you.

# Changelog

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

# Actor input Schema

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

Up to 10 terms, 50 bytes at most each. Lowercased and deduplicated. A job is kept when one term appears in its title, tags or description. Leave empty to keep every job.

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

Keep only jobs whose location contains one of these texts, for example berlin or remote (case-insensitive). Leave empty to keep all.

## `tags` (type: `array`):

Keep only jobs with one of these Arbeitnow tags, for example it or software development (exact match, case-insensitive). Leave empty to keep all.

## `jobTypes` (type: `array`):

Keep only jobs with one of these types as written by Arbeitnow, for example berufserfahren or full time (exact match, case-insensitive). Leave empty to keep all.

## `postedWithinDays` (type: `integer`):

Keep only jobs published in the last N days (1 to 365). The feed is the newest listings first, so a short window needs few pages.

## `remoteOnly` (type: `boolean`):

Keep only listings flagged remote by Arbeitnow. About 3 in 100 listings are remote, so combine it with more pages. Off by default: every listing is returned.

## `visaSponsorship` (type: `boolean`):

Ask Arbeitnow for the visa sponsoring listings only (documented filter visa_sponsorship=true, applied by Arbeitnow, not by this Actor).

## `maxPages` (type: `integer`):

Pages of the feed to read, 1 to 20 (about 300 of the newest listings per page). One page is read every 30 seconds.

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

Hard cap on the number of jobs returned (and billed) in one run.

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

Add the job description as text and as HTML. E-mail addresses and phone numbers are masked, links are kept.

## Actor input object example

```json
{
  "searchTerms": [
    "python"
  ],
  "locations": [],
  "tags": [],
  "jobTypes": [],
  "remoteOnly": false,
  "visaSponsorship": false,
  "maxPages": 1,
  "maxItems": 10,
  "includeDescription": true
}
```

# Actor output Schema

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

Direct link to all dataset items produced by the run.

## `summary` (type: `string`):

Direct link to the OUTPUT record: requests made, jobs read, filtered out, returned and billed, errors by kind, the reason of an early stop and the Arbeitnow attribution.

# 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": [
        "python"
    ],
    "maxPages": 1,
    "maxItems": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("mrbridge/arbeitnow-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 = {
    "searchTerms": ["python"],
    "maxPages": 1,
    "maxItems": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("mrbridge/arbeitnow-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 '{
  "searchTerms": [
    "python"
  ],
  "maxPages": 1,
  "maxItems": 10
}' |
apify call mrbridge/arbeitnow-jobs-scraper --silent --output-dataset

```

## MCP server setup

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