# Nofluffjobs $1💰 URL Search | Keyword Filtering | Rich Output (`abotapi/nofluffjobs-scraper`) Actor

From $1/1K. Extract IT jobs from NoFluffJobs.com across Poland, Czechia, Slovakia, Hungary, and the Netherlands. Search by filters or use NoFluffJobs URLs. Returns salary ranges, skills, full descriptions, tasks, benefits, hiring steps, company size, GPS coordinates, and merged office locations.

- **URL**: https://apify.com/abotapi/nofluffjobs-scraper.md
- **Developed by:** [Abot API](https://apify.com/abotapi) (community)
- **Categories:** Jobs, Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 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.
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?

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

## NoFluffJobs Scraper

Extract IT job listings from NoFluffJobs across five markets: Poland, Czechia, Slovakia, Hungary and the Netherlands. Every record comes with the disclosed salary range, required and nice-to-have skills, the full job description, daily tasks, benefits, recruitment steps, company size and exact GPS coordinates. Search with structured filters (category, seniority, salary, city, technology, remote) or paste NoFluffJobs URLs. You get one clean, deduplicated record per job, with all of its office locations merged into a single array.

### Why this scraper

- High data coverage: 40+ fields per job, including disclosed salary ranges, per-skill requirements, hourly and monthly salary breakdowns, daily tasks, equipment and office perks.
- Five markets in one run: scrape Poland, Czechia, Slovakia, Hungary and the Netherlands, individually or all at once.
- Structured search: filter by category, seniority, employment type, posting language, city, technology, remote-only and salary range, no need to hand-build URLs.
- Clean output: one record per job, deduplicated across its location variants, with every city and its GPS in a locations array.
- Fast and low cost: a direct data path with no heavy page rendering, so it runs on the cheap Datacenter connection and finishes quickly.
- Cost controls built in: a configurable residential usage cap, a residential traffic budget, and automatic switching to a cheaper connection when possible.
- Walks the whole catalogue by default: `maxPages` defaults to `0` (unlimited). The run walks every search-result page, stopping on its own once the region reports no more pages, a page comes back empty, or a page contributes no new jobs (repeat-page guard). `maxListings` remains the primary cap.
- Resumable runs: set `resumeFromRunId` to a previous run or dataset id to collect only the jobs that run doesn't already have (a delta). Long unlimited runs also checkpoint their progress to the key-value store, so an Apify platform migration or a Resurrect of a failed run picks up where it left off instead of starting over.

### Data you get

> Sample shape: values are illustrative placeholders, not from a live listing.

| Field | Example |
| --- | --- |
| id | `senior-java-developer-sample-company-Warszawa` |
| title | `Senior Java Developer` |
| url | `https://nofluffjobs.com/pl/job/senior-java-developer-sample-company-warszawa` |
| companyName | `Sample Company` |
| companySize | `500 - 999` |
| category | `backend` |
| seniority | `["senior"]` |
| technology | `Java` |
| salaryFrom | `21000` |
| salaryTo | `28000` |
| salaryCurrency | `PLN` |
| salaryType | `b2b` |
| remote | `true` |
| cities | `["Remote", "Warszawa"]` |
| locations | `[{ "city": "Warszawa", "country": "Poland", "latitude": 52.0000, "longitude": 21.0000 }]` |
| description | `Full job description text appears here when fetchDetails is on.` |
| requirementsMust | `[{ "name": "Java", "type": "main" }, { "name": "Spring", "type": "main" }]` |
| requirementsNice | `[{ "name": "Kafka", "type": "nice" }]` |
| dailyTasks | `["Design backend services", "Build REST APIs"]` |
| benefits | `["Private healthcare", "Sport card"]` |
| equipment | `{ "computer": "Notebook", "operatingSystem": "Linux" }` |
| salaryRanges | `[{ "type": "b2b", "from": 130, "to": 140, "currency": "PLN", "period": "Hour" }]` |
| recruitmentSteps | `["Phone screen", "Technical interview"]` |
| onlineInterviewAvailable | `true` |
| region | `pl` |

When `fetchDetails` is off, only the list-level fields are populated (title, company, salary, category, seniority, cities, technology, remote, url); the detail-only fields stay `null`.

### How to use

Basic search in Poland:

```json
{
  "mode": "search",
  "region": "pl",
  "maxListings": 100
}
```

Senior backend Java jobs paying at least 20000, fully remote:

```json
{
  "mode": "search",
  "region": "pl",
  "category": "backend",
  "seniority": "senior",
  "technology": "java",
  "remoteOnly": true,
  "minSalary": 20000,
  "maxListings": 100
}
```

All five markets at once:

```json
{
  "mode": "search",
  "region": "all",
  "maxListings": 500
}
```

Paste URLs (a search page and a single job page):

```json
{
  "mode": "url",
  "urls": [
    "https://nofluffjobs.com/pl/devops",
    "https://nofluffjobs.com/cz/backend?criteria=seniority%3Dsenior"
  ],
  "maxListings": 200
}
```

Resume a previous run, collecting only jobs it doesn't already have:

```json
{
  "mode": "search",
  "region": "pl",
  "maxListings": 0,
  "resumeFromRunId": "<a previous run id or dataset id from this account>"
}
```

### Input parameters

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| mode | string | `search` | `search` builds a query from the filters; `url` fetches the URLs you paste. |
| region | string | `pl` | `pl`, `cz`, `sk`, `hu`, `nl`, or `all`. |
| keyword | string | (empty) | Free-text search across roles, technologies and companies. |
| category | string | (any) | Job category, e.g. backend, frontend, devops, testing, data. |
| seniority | string | (any) | trainee, junior, mid, senior, expert. |
| employmentType | string | (any) | b2b, permanent, mandate-contract, internship. |
| jobLanguage | string | (any) | Language the posting is written in. |
| city | string | (any) | Restrict to one city. |
| technology | string | (any) | Require a specific skill, e.g. react, kubernetes. |
| sortBy | string | relevance | Result order: relevance, newest, salary high to low, salary low to high. Applied server-side. |
| remoteOnly | boolean | `false` | Only jobs that offer remote work. |
| minSalary | integer | (none) | Minimum monthly gross, region currency. Applies in both search mode and URL mode. Hourly-quoted postings are converted to a monthly figure first, so an hourly job is compared on the same basis. A posting that publishes no salary is skipped while a bound is set, and the run log names each one it skipped. |
| maxSalary | integer | (none) | Maximum monthly gross, region currency. Applies in both search mode and URL mode, on the same basis as minSalary. |
| urls | array | (empty) | NoFluffJobs URLs to fetch in URL mode. |
| fetchDetails | boolean | `true` | Add the full detail fields per job. |
| maxListings | integer | `20` | Main limit: max jobs to return. 0 means unlimited (then maxPages caps). |
| maxPages | integer | `0` | Optional bound on pages walked per query/region (about 20 jobs per page). `0` = walk the whole catalogue: stops on its own at the region's reported page total, an empty page, or a page with no new jobs, never an artificial page cap. Does not cap job count; use maxListings for that. |
| resumeFromRunId | string | | Optional id of a previous run (or dataset) of this actor. Jobs already in that dataset are skipped, so this run returns only new jobs (a delta). |
| proxy | object | Apify Datacenter | Connection settings. |
| residentialRequestCap | integer | `0` | Switch to Datacenter after N residential requests. 0 disables. |
| residentialBudgetMb | integer | `0` | Switch to Datacenter after N MB over residential. 0 disables. |

### Send results into your apps (MCP connectors)

Optionally pipe the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. This is an extra delivery step **after** the scrape — the Apify dataset is never changed.

**What gets written to the connector:** a condensed, human-readable **summary** of each record — not the full JSON. Each item becomes one entry with a **title** and its key fields flattened to plain text. The **complete record always stays in the Apify dataset**.

1. Authorize a connector once under **Apify → Settings → Integrations** (Notion, Linear, Airtable, or Apify).
2. Select it in the **"Pipe results into your apps"** input field. (If the picker is empty, you haven't authorized a connector yet.)
3. For **Notion**, also set `notionParentPageUrl` to the page where items should be created.

The connection is mediated by Apify's MCP proxy, so this actor never sees your third-party credentials. Leave the field empty to skip.

### Output example

> Sample shape: values are illustrative placeholders, not from a live listing.

```json
{
  "id": "senior-java-developer-sample-company-Warszawa",
  "reference": "REF-0001",
  "title": "Senior Java Developer",
  "url": "https://nofluffjobs.com/pl/job/senior-java-developer-sample-company-warszawa",
  "region": "pl",
  "companyName": "Sample Company",
  "companySize": "500 - 999",
  "companyProfileUrl": "https://nofluffjobs.com/company/sample-company-00000000",
  "category": "backend",
  "seniority": ["senior"],
  "technology": "Java",
  "remote": true,
  "fullyRemote": true,
  "remotePercent": 100,
  "cities": ["Remote", "Warszawa"],
  "locations": [
    { "city": "Warszawa", "country": "Poland", "countryCode": "POL", "latitude": 52.0000, "longitude": 21.0000 }
  ],
  "salaryFrom": 21000,
  "salaryTo": 28000,
  "salaryCurrency": "PLN",
  "salaryType": "b2b",
  "salaryRanges": [
    { "type": "b2b", "from": 130, "to": 140, "currency": "PLN", "period": "Hour" }
  ],
  "description": "Full job description text appears here when fetchDetails is on.",
  "dailyTasks": ["Design backend services", "Build REST APIs"],
  "requirementsMust": [
    { "name": "Java", "type": "main" },
    { "name": "Spring", "type": "main" }
  ],
  "requirementsNice": [{ "name": "Kafka", "type": "nice" }],
  "requirementLanguages": ["en"],
  "benefits": ["Private healthcare", "Sport card"],
  "officePerks": ["Free coffee"],
  "equipment": { "computer": "Notebook", "operatingSystem": "Linux" },
  "recruitmentSteps": ["Phone screen", "Technical interview"],
  "recruitmentLanguages": ["pl", "en"],
  "onlineInterviewAvailable": true,
  "expiresAt": "2026-01-01T00:00:00.000Z"
}
```

### Plan requirement

This actor runs on the Apify Datacenter connection by default, which is included on every plan, including the free plan. No residential connection is required.

Residential is offered as an option for users who want geo-diverse exit IPs, but it is not needed for this site. If you do select a Residential connection, use the residential request cap and traffic budget to keep costs predictable; the actor will automatically switch back to the cheaper Datacenter connection once a cap is reached.

# Actor input Schema

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

How to find jobs. 'search' builds a query from the filters below. 'url' fetches the NoFluffJobs URLs you paste (search pages or individual job pages).

## `region` (type: `string`):

Which NoFluffJobs market to search. 'All regions' scrapes every market.

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

Free-text search, e.g. a role, technology or company name. Leave empty to list everything in the region.

## `category` (type: `string`):

Job category. Leave empty for all categories.

## `seniority` (type: `string`):

Experience level. Leave empty for any.

## `employmentType` (type: `string`):

Contract type. Leave empty for any.

## `jobLanguage` (type: `string`):

Language the posting is written in. Leave empty for any.

## `city` (type: `string`):

Filter to a single city (e.g. warszawa, krakow, praha). Leave empty for all cities.

## `technology` (type: `string`):

Require a specific skill or technology (e.g. java, react, kubernetes).

## `urls` (type: `array`):

Paste NoFluffJobs URLs (one per line). Search/listing pages are walked with pagination; individual job pages are fetched directly. The search-mode fields are ignored in URL mode. The fields in the Filters and sort section still apply.

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

Only return jobs that offer remote work. The detail fields remotePercent and fullyRemote tell you whether a job is fully remote or hybrid. Applies in BOTH search mode and URL mode.

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

Result ordering. Default is the site's relevance ranking. Salary sorts use the disclosed range; newest sorts by publish date. Applied server-side, so it orders the whole result set, not just the page. Applies in BOTH search mode and URL mode.

## `minSalary` (type: `integer`):

Only jobs whose disclosed salary range reaches at least this amount (monthly gross, in the region currency). Jobs with no disclosed salary are excluded when a salary filter is set. Applies in BOTH search mode and URL mode.

## `maxSalary` (type: `integer`):

Only jobs whose disclosed salary range starts at or below this amount (monthly gross, in the region currency). Applies in BOTH search mode and URL mode.

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

Open each job's detail page to add full description, required/nice-to-have skills, daily tasks, benefits, equipment, recruitment steps, company size, salary breakdown and GPS. Turn off for a faster, lighter run with list-level fields only.

## `maxListings` (type: `integer`):

The main limit: maximum number of jobs to return. 0 means unlimited (then Max pages becomes the cap).

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

Safety bound on how many search-result pages to walk per query/region (about 20 jobs per page). 0 means unlimited (walk until the storefront reports no more pages). Leave at the default; Max jobs is the primary limit. Only lower this if you set Max jobs to 0 (unlimited) and want pages to be the cap.

## `resumeFromRunId` (type: `string`):

Continue ONE interrupted crawl. ID of a previous run of this actor (or a dataset ID) -- jobs already in that dataset are skipped, so this run returns only NEW jobs (a delta). Combine both runs' datasets for the full set. Max jobs then counts only the new jobs. For recurring monitoring of the SAME search across scheduled runs instead, use Incremental mode below.

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

Recurring monitoring of the SAME search: schedule this actor and get only what changed (NEW/UPDATED/REAPPEARED/EXPIRED) each run -- the actor remembers its own baseline in a key-value store, no run ID to paste. Off by default so existing runs are unaffected. For continuing ONE interrupted crawl instead, use Resume above.

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

Optional. Name this monitoring campaign so you can run several different filter setups without them sharing a baseline. Leave empty to auto-derive a key from the mode and every filter (region, keyword, category, seniority, employment type, job language, city, technology, remote only, sort, salary range, URLs, fetch details) -- two different filter setups then never collide.

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

Incremental mode only. When off (default), a job identical to the last run is skipped -- not returned, not billed. Turn on to also return (and pay for) every UNCHANGED job alongside NEW/UPDATED/REAPPEARED.

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

Incremental mode only. When on, a job that was tracked before but is no longer found after a COMPLETE scan of this search is returned once with changeType EXPIRED (and billed like any other row). Only fires on a complete, uncapped, non-resumed scan -- a partial run never marks jobs expired. Off by default.

## `proxy` (type: `object`):

NoFluffJobs works on the cheap Apify Datacenter connection (the default, free-tier friendly). Residential is optional and used only if you need geo-diverse IPs.

## `residentialRequestCap` (type: `integer`):

Cost guard. If you chose a Residential connection, switch to the cheaper Datacenter connection after this many residential requests. 0 means no cap.

## `residentialBudgetMb` (type: `integer`):

Cost guard. If you chose a Residential connection, switch to the cheaper Datacenter connection after pulling this many MB over residential. 0 means no budget limit.

## `mcpConnectors` (type: `array`):

Optionally send the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. Authorize a connector once under Apify → Settings → Integrations, then select it here. The connector receives a condensed, human-readable summary per item (title + key fields), not the full JSON — the complete record stays in the dataset. Leave empty to skip. Supported: Notion (https://mcp.notion.com/mcp), Linear (https://mcp.linear.app/sse), Airtable (https://mcp.airtable.com/mcp), Apify (https://mcp.apify.com).

## `notionParentPageUrl` (type: `string`):

URL (or id) of the Notion page under which item pages are created. Required to enable the Notion export; ignored by other connectors.

## `maxNotifyListings` (type: `integer`):

Cap on items written to each connector per run. Does not affect the dataset.

## Actor input object example

```json
{
  "mode": "search",
  "region": "pl",
  "keyword": "java backend",
  "category": "",
  "seniority": "",
  "employmentType": "",
  "jobLanguage": "",
  "city": "warszawa",
  "technology": "react",
  "urls": [
    "https://nofluffjobs.com/pl/backend"
  ],
  "remoteOnly": false,
  "sortBy": "",
  "fetchDetails": true,
  "maxListings": 20,
  "maxPages": 0,
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "proxy": {
    "useApifyProxy": true
  },
  "residentialRequestCap": 0,
  "residentialBudgetMb": 0,
  "maxNotifyListings": 50
}
```

# Actor output Schema

## `overview` (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",
    "region": "pl",
    "urls": [
        "https://nofluffjobs.com/pl/backend"
    ],
    "proxy": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/nofluffjobs-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 = {
    "mode": "search",
    "region": "pl",
    "urls": ["https://nofluffjobs.com/pl/backend"],
    "proxy": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/nofluffjobs-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 '{
  "mode": "search",
  "region": "pl",
  "urls": [
    "https://nofluffjobs.com/pl/backend"
  ],
  "proxy": {
    "useApifyProxy": true
  }
}' |
apify call abotapi/nofluffjobs-scraper --silent --output-dataset

```

## MCP server setup

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