# Fajn-brigady.cz & INwork.cz Scraper ($1.75/1K): Czech Jobs (`tsutseisoftware/fajn-brigady-scraper`) Actor

Scrape temporary jobs (brigády) and job offers from Fajn-brigady.cz and INwork.cz: wage per hour and month, company, town, region, start date, positions, contract type, suitable for students or pensioners. Monitoring of new and removed jobs, market summary. Unofficial.

- **URL**: https://apify.com/tsutseisoftware/fajn-brigady-scraper.md
- **Developed by:** [Tomáš Buchta](https://apify.com/tsutseisoftware) (community)
- **Categories:** Jobs
- **Features:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.59 / 1,000 jobs

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

## Fajn-brigady.cz & INwork.cz Scraper: Czech Temporary Jobs

Get temporary jobs (**brigády**) and job offers from **Fajn-brigady.cz** and **INwork.cz**, two big Czech job boards run on one platform, as clean structured data. Say **which towns, regions, categories or search words** you want, or paste any job list of either website (all jobs, a category, a town or a region) or individual job URLs. For each job you get the title, company, town with its **region (kraj) and district (okres)**, the **wage as numbers with hourly and monthly equivalents**, start date or term, number of open positions, contract type, minimum education, the groups the job suits (students and graduates, pensioners, parents on leave, people with disabilities, self-employed, refugees from Ukraine), whether the advertiser is a company or a **staffing agency**, and the posting date. Optionally the full **description** (with e-mails, phone numbers and the contact person's name removed).

Built for **watching the market of temporary jobs**: schedule it daily in monitoring mode and you get, and pay for, only jobs that are new or changed (and a small fee per list page checked), with an optional alert to Slack, Discord, Telegram or a webhook, plus, if you want, the jobs that **disappeared**. Every run also saves a free **market summary** (wage median and quartiles per hour and per month, by region and category, top employers, share of staffing agencies).

An ad published on both websites is returned once. The output uses the same field names and values as our **Jobs.cz** and **Prace.cz** scrapers wherever the data is the same (`salaryMin`, `salaryPeriod`, `region`, `changeType`…).

> **Česky:** Scraper brigád a nabídek práce z **Fajn-brigady.cz a INwork.cz**: hodinová nebo měsíční mzda, město, kraj, termín nástupu, počet míst a typ smlouvy. Monitoring nových a odstraněných nabídek a přehled mezd zdarma. Výstup do Excelu, CSV nebo JSON. Podporu poskytujeme i česky.

### Why use this Fajn-brigady.cz scraper?

- 💰 **Wages you can compare.** "180 - 180 Kč/hod.", "až 285 Kč/h" in the title, "35-40.000 Kč" or "134,4 Kč/hod" become `salaryMin`/`salaryMax` numbers with `salaryPeriod` (`hour`, `day`, `week`, `month`) and **monthly and hourly equivalents** (160 hours a month). When the wage field says *Dle domluvy* (negotiable), the amount from the title is used (`salarySource: "title"`). Sign-on bonuses are not taken for the wage.
- 🗺️ **Region and district for every job,** from the workplace ("Jevišovka (Ok. Břeclav)") or the town, using the official list of Czech municipalities.
- 🧑‍🎓 **The facts job seekers filter by:** start (`startsImmediately`, `termFrom`/`termTo`), number of positions, full-time / part-time (INwork.cz), minimum education, language, suitable for students and graduates, pensioners, parents on leave, people with disabilities, self-employed, refugees from Ukraine.
- 🏢 **Agencies vs. employers:** `advertiserType` `company`, `agency` (the website's own label, or a well-known staffing agency such as Randstad, Manpower, Grafton, Manuvia) or `private`.
- 🧭 **No URLs needed:** type towns (`Brno`, `Kuřim`), pick regions, name categories (`Skladník`, `Uklízečka`) or search words (`řidič`, `Lidl`); the actor finds the lists on both websites and tells you by name when a place has none.
- 🔎 **Filters** (filtered-out jobs are not charged as jobs): title or description must (not) contain, companies, job type (brigáda / regular job), only with a wage, minimum hourly or monthly wage, posted in the last N days, contract type, suitable for, position, exclude staffing agencies.
- 📬 **Monitoring mode:** a daily feed of new and changed jobs (`changeType`, `changedFields`, `previousValues`), optionally with **removed jobs** (`removed`, `reappeared`), and a crashed run never makes the next run return the same jobs again.
- ⚡ **Light on the websites and cheap:** one request gives 40 jobs; the actor downloads only the part of each page it needs. No browser, 256 MB of memory.
- 🤖 **Ready for AI agents and spreadsheets:** the description as plain text, **HTML or Markdown** with a length limit, **compact rows** with the core fields only, rows without empty fields.
- 🔒 **Privacy first.** No names of the people who posted the ads, no phone numbers or e-mails; ads of **private persons** (sole traders under their own name) come without the advertiser's name, logo, profile or workplace address. See the FAQ.

### What data can you get?

| Field | Example |
|---|---|
| Title, website, job type, category | Brigáda - balení - až 180 Kč/h, fajn-brigady.cz, brigada, manualni |
| Company, company ID, profile, logo | Randstad HR Solutions s.r.o., 2643 |
| Advertiser type | `agency` (also `company`, `private`) |
| Town, workplace, region, district | Brno; Kuřim (Ok. Brno - venkov); Jihomoravský kraj; Brno-venkov |
| Wage | 180 CZK per hour → 28 800 CZK per month; "180 - 180 Kč/ hod." |
| Start / term | Ihned (`startsImmediately: true`), or 25. 12. 2026 – 31. 3. 2027 |
| Positions | 10 |
| Contract type (INwork.cz), education, language | `fullTime`; Středoškolské; Čeština - Pokročilý |
| Position | Operátor/ka výroby |
| Suitable for | `graduates`, `pensioners`, `parents`, `disabled`, `selfEmployed`, `ukrainianRefugees` |
| Labels | Nově přidáno, recommended ad |
| Posted at | 2026-11-05 |
| With **Include description** | the ad's text with sections (Informace o pracovním místě, Požadujeme, Nabízíme) |
| With **Monitoring** | changeType `new`, `updated`, `removed`, `reappeared`; firstSeenAt, lastSeenAt, removedAt, changedFields, previousValues |

See the **Output** tab for all fields. The dataset has views for **Overview**, **Wages** and **Monitoring**.

### How to scrape Fajn-brigady.cz and INwork.cz

1. Fill in **Locations** (towns, e.g. `Brno`), **Regions**, **Categories** (e.g. `Skladník`) or **Search words** (e.g. `řidič`). Or open [Fajn-brigady.cz](https://www.fajn-brigady.cz/brigady/) or [INwork.cz](https://www.inwork.cz/prace/), pick a category, town or region and paste its URL into **Listing or job URLs**, e.g. `https://www.fajn-brigady.cz/brigady/brno/`, `https://www.fajn-brigady.cz/brigady/manualni/` or `https://www.inwork.cz/brigady/jihomoravsky-kraj/`.
2. You can combine them: several towns, lists from both websites and individual job URLs in one run.
3. Optionally set filters (minimum hourly wage, posted in the last N days, suitable for students or pensioners, exclude agencies…).
4. Set **Max jobs** and click **Start**. Download the results as JSON, CSV or Excel, or connect them to Google Sheets, Slack or Make.

#### Towns, regions, categories and search words

| Input | What is read |
|---|---|
| **Locations** `["Brno"]` | the town's list on Fajn-brigady.cz (temporary jobs) and on INwork.cz (regular jobs) |
| **Regions** | the region's list on both websites |
| **Categories** `["Skladník"]` | the category of Fajn-brigady.cz, for the whole country or for each location and region. INwork.cz has other categories and is not read with Categories. |
| **Search words** `["řidič"]` alone | the websites' own search over the whole country (Fajn-brigady.cz: position or company, INwork.cz: title) |
| **Search words** with a location, region or category | the lists of the place, kept when the title or the company contains one of the words (the websites cannot combine a search with a place) |
| **Job type** | picks the website: temporary jobs only = Fajn-brigady.cz, regular jobs only = INwork.cz |

A town or category the website has no list for is reported by its name in the status message, and the run fails when nothing else could be read. The actor never returns the jobs of the whole country instead. A town name used in several districts (e.g. Lhota) is read on Fajn-brigady.cz only; paste its INwork.cz URL if you need it.

"New jobs" pages and the "suitable for" pages of the websites are not supported: use a category, town or region list together with the actor's filters.

### Use cases

#### New temporary jobs in Brno every morning

Schedule a daily run in monitoring mode; each run returns only jobs that are new or changed since the previous run (and those that disappeared).

```json
{
  "startUrls": [
    { "url": "https://www.fajn-brigady.cz/brigady/brno/" },
    { "url": "https://www.inwork.cz/brigady/jihomoravsky-kraj/" }
  ],
  "maxItems": 0,
  "onlyNewOrChanged": true,
  "includeRemovedJobs": true,
  "monitoringKey": "brno"
}
```

#### Hourly wages of warehouse and production jobs

```json
{
  "startUrls": [{ "url": "https://www.fajn-brigady.cz/brigady/manualni/" }],
  "maxItems": 500,
  "onlyWithSalary": true
}
```

Use the **Wages** view, or the `SUMMARY` record for the median and quartiles per hour and per month by region.

#### Jobs for students and graduates paying at least 170 CZK per hour, posted this week

```json
{
  "startUrls": [{ "url": "https://www.fajn-brigady.cz/brigady/" }],
  "maxItems": 100,
  "minHourlySalary": 170,
  "suitableFor": ["graduates"],
  "postedWithinDays": 7
}
```

#### Direct employers only (no staffing agencies) in a region

```json
{
  "startUrls": [{ "url": "https://www.inwork.cz/prace/jihomoravsky-kraj/" }],
  "maxItems": 200,
  "excludeAgencies": true,
  "jobTypes": ["job"]
}
```

#### The whole list quickly, without job pages

```json
{
  "startUrls": [{ "url": "https://www.fajn-brigady.cz/brigady/" }],
  "maxItems": 0,
  "maxPages": 0,
  "includeDetails": false
}
```

About 1 800 jobs from Fajn-brigady.cz in under a minute (title, company, town, region, labels and the wage when it is in the title).

#### Filters (applied by the actor, filtered-out jobs are not charged as jobs)

Title must contain, Title must not contain, Only these companies, Exclude companies and Job type work on the list, before any job page is read. Only jobs with a wage, Min hourly wage and Min monthly wage use the wage from the job page (without job pages the wage from the title). Posted within (days), Contract type, Suitable for, Position must contain and Exclude staffing agencies need **Include job details** (on by default). **Title or description must contain** and **must not contain** look for your words in the title and in the whole description (they read the description even when **Include description** is off). Jobs rejected after their page don't count towards **Max jobs**: the actor reads more jobs until Max jobs is reached. The websites cannot apply these filters themselves, so the actor reads the pages and filters; pages read beyond the saved jobs are charged as **extra pages** (see the prices below), e.g. a title filter that keeps 1 job out of 20 list pages pays 1 job and 19 extra pages.

#### Region and district

`region` (kraj) and `district` (okres) come from the district written next to the workplace on the job page ("Kuřim (Ok. Brno - venkov)"), otherwise from the town, otherwise from the region in the INwork.cz URL, matched against the official list of Czech municipalities. "Celá ČR" (whole country) and jobs abroad have no region.

#### Monitoring mode explained

With **Monitoring: only new or changed jobs** the actor remembers the jobs of the previous run with the same **Monitoring key** and saves only jobs that are new (`changeType: new`) or whose title, company, town or wage in the list changed (`updated`, with `changedFields` and `previousValues`). Job pages are read only for those, so daily runs take seconds. Every list page read in monitoring mode is charged as a **monitoring page** (the list has to be read to find the changes). If a run is interrupted (crash, out of memory), the next run does not return the jobs that were already saved again. Unchanged jobs are not charged, unless you turn on **Monitoring: include unchanged jobs**: then they are returned too (`changeType: unchanged`, charged like any job), so one run gives the whole list with every job marked. With **Monitoring: include removed jobs**, jobs of the previous run that are no longer in the lists are returned as `removed` with their last known values, `lastSeenAt` and `removedAt`; a removed job that comes back is returned as `reappeared`. Removed jobs are checked only when the run read every list completely (the status message says when it did not), so keep the same start URLs and filters for one monitoring key.

#### Market summary (SUMMARY)

Every run saves a free JSON record `SUMMARY` in the key-value store: number of jobs, wage median and quartiles **per hour** and **per month** (overall, by region and by category), top 20 employers, the share of staffing agencies, contract types and regions. Unchanged jobs in monitoring mode count too.

#### Description formats and compact output

With **Include description** you choose the **Description format**: plain text (default), clean HTML (headings, paragraphs, lists, bold, links) or Markdown, and a **Description length limit** (whole paragraphs, then "…"). The same protection applies in every format: contacts, the name of the person who posted the ad and the name of a private advertiser are removed, also when the name is split by formatting or sits in a link address; if a name could remain in the formatted text, the description is built from the cleaned plain text instead.

**Compact output** keeps only the core fields of every row (id, title, company, town, region, district, job type, category, position, wage, start, positions, contract type, suitable for, posting date, link and the monitoring fields; the description when you ask for it). **Leave out empty fields** drops fields that are null or an empty list. Both make rows smaller for AI agents and MCP clients; the price per job is the same.

#### Notifications (job alerts, free)

Fill in a Slack or Discord webhook, a Telegram bot token with a chat ID, or any webhook URL (Zapier, Make, n8n, your server), and after each run the actor sends the saved jobs there: title, company, town, wage and link, at most **Notifications: max jobs per message** (default 10) and "…and N more". With monitoring you get only the new, changed and removed jobs, and nothing on a day without changes. Descriptions and contacts are never sent. The generic webhook gets JSON `{title, summary, totalSaved, runUrl, datasetUrl, items}`. A channel that fails is written to the log and never fails the run.

#### Fast mode (optional, small extra charge)

Up to 120 requests per minute and 5 at a time instead of 60 and 3, which helps most with job pages. When a website answers "too many requests", shows a challenge or slows down, the run switches back to the normal speed for the rest of the run.

### How much does it cost to scrape Fajn-brigady.cz?

Pay per result, see the **Pricing** tab for the current prices and discounts for higher Apify plans:

| What | Price |
|---|---|
| Job (every saved row, with or without job page details, including removed jobs in monitoring mode) | **$1.75 per 1 000 jobs** ($1.69 Bronze, $1.65 Silver, $1.59 Gold and higher Apify plans) |
| **+ Fast mode** (only jobs saved while the optional fast mode was on) | **+$0.01 per 1 000 jobs** |
| **Monitoring: listing page checked** (only in monitoring mode: every list page read, up to 40 jobs each) | **$0.70 per 1 000 pages** ($0.0007 per page) |
| **Extra page** (a page read beyond what the saved jobs explain, see below) | **$0.70 per 1 000 pages** ($0.0007 per page) |
| Run start | $0.004 per run |
| Unchanged jobs in monitoring mode, the `SUMMARY` record, notifications | free |

Job page details are included in the price. **A plain run pays only for its jobs and the start**: every saved job covers one list page and one job page. Extra pages are charged at the end of the run only for what was read beyond that: list pages of a filter that keeps few jobs, job pages of jobs that a filter rejected (or that were removed during the run), and lists or job URLs that don't exist. List pages in monitoring mode are monitoring pages, never extra pages too. A second attempt and a page that a website rejected are never charged. Your maximum cost per run covers all of it: the actor stops reading before the pages would exceed it. The status message says how many pages were charged.

Examples: 20 jobs with details $0.039; **Locations** with a town that one of the websites has no list for: 1 extra page ($0.0007); **Search words** that find nothing: 1 extra page per website; a daily monitoring run of one town (3 list pages, 2 new jobs) $0.0096; a title filter that reads 20 list pages and keeps 1 job $0.019; a run that finds nothing on 1 page $0.0047.

Why these charges: both websites accept only residential IP addresses, so every page read costs real money whether it holds a job for you or not. Platform usage and the residential proxy are included. The actor makes at most 3 requests at a time and 60 per minute, to keep the load on the websites low. Typical runs (measured 2026-11-06):

| Scenario | Requests | Typical time |
|---|---|---|
| 20 jobs with details (the example input) | 21 | ~30 seconds |
| 100 jobs with details | ~103 | ~2 minutes |
| 1 000 jobs without job pages | ~25 | ~30 seconds |
| 1 000 jobs with details | ~1 025 | ~17 minutes (~9 in fast mode) |
| Daily monitoring run with 10 new jobs | list pages + 10 | under a minute |

### Input example

```json
{
  "startUrls": [{ "url": "https://www.fajn-brigady.cz/brigady/brno/" }],
  "maxItems": 100,
  "minHourlySalary": 180,
  "suitableFor": ["graduates"]
}
```

Without URLs, for an AI agent (warehouse jobs in two towns, Markdown descriptions, compact rows):

```json
{
  "locations": ["Brno", "Olomouc"],
  "searchQueries": ["sklad", "skladník"],
  "maxItems": 50,
  "includeDescription": true,
  "descriptionFormat": "markdown",
  "maxDescriptionLength": 1500,
  "compact": true,
  "excludeEmptyFields": true
}
```

### Output example

```json
{
  "id": "fajn:6586740",
  "adId": "6586740",
  "source": "inwork.cz",
  "url": "https://www.inwork.cz/brigady/jihomoravsky-kraj/brno-venkov/kurim/6586740-vikendova-brigada-ve-vyrobe-kabelu-moznost-volby-smen/",
  "title": "Víkendová brigáda ve výrobě kabelů | možnost volby směn!",
  "jobType": "brigada",
  "category": null,
  "position": "Operátor/ka výroby",
  "companyName": "Manuvia, a. s., organizační složka",
  "companyId": "2353",
  "companyProfileUrl": "https://www.inwork.cz/profil-zamestnavatele/2353-manuvia-a-s-organizacni-slozka/",
  "companyLogoUrl": "https://media.fajnsprava.cz/firmy/loga/thumbs/1_2353.jpg",
  "advertiserType": "agency",
  "city": "Kuřim",
  "workplace": "Kuřim (Ok. Brno - venkov)",
  "region": "Jihomoravský kraj",
  "district": "Brno-venkov",
  "regionSlug": "jihomoravsky-kraj",
  "salaryText": "185 - 200 Kč/ hod.",
  "salaryMin": 185,
  "salaryMax": 200,
  "salaryCurrency": "CZK",
  "salaryPeriod": "hour",
  "salaryMonthlyMin": 29600,
  "salaryMonthlyMax": 32000,
  "salaryHourlyMin": 185,
  "salaryHourlyMax": 200,
  "salarySource": "structured",
  "startText": "Dle domluvy",
  "startsImmediately": false,
  "termFrom": null,
  "termTo": null,
  "positionsCount": 20,
  "employmentTypes": [],
  "suitableFor": ["graduates"],
  "minEducation": null,
  "languageRequirement": null,
  "labels": [],
  "isRecommended": true,
  "postedAt": "2026-10-27",
  "description": "Informace o pracovním místě\nVýroba kabelových svazků a konektorů\n\nObsluha jednoduchých výrobních zařízení\n…\n\nNabízíme\n\nZákladní mzda: 185 Kč/hod\n…",
  "detailScraped": true,
  "changeType": null,
  "sourceUrl": "https://www.inwork.cz/brigady/jihomoravsky-kraj/",
  "scrapedAt": "2026-11-06T13:19:29Z"
}
```

### Limitations

- The list has the title, company, town and labels only; the wage (unless it is in the title), start, positions, contract type and posting date are on the job page, so **Include job details** is on by default (one more request per job).
- Fill rates measured on 160 jobs with details (2026-11-06): title, town, start, positions, posting date 100 %; region 97–100 %; wage 92 % on Fajn-brigady.cz and 82 % on INwork.cz (the rest is *Dle domluvy* without an amount in the title); company 88–97 % (the rest are private persons, hidden; 12 % on the whole Fajn-brigady.cz list); contract type and minimum education 100 % on INwork.cz regular jobs, not shown for brigády; suitable for 33 %; description 100 % (with **Include description**).
- Wages per lesson or per task, and wages whose unit doesn't fit the amount (e.g. "150 Kč per month"), are kept in `salaryMin`/`salaryMax` without a period and without conversion.
- Fajn-brigady.cz shows the number of jobs only on its main list; other lists are read page by page until their last page.
- Search, "new jobs" and "suitable for" pages of the websites are not supported (use the filters). A list that doesn't exist is reported as not found (INwork.cz would otherwise show jobs from the whole country).
- Both websites are behind Cloudflare and reject requests from data-center IP addresses (checked 2026-11-10), so the actor uses Apify Proxy with residential IPs in the Czech Republic by default. If a website rejects the requests anyway, the run stops with a clear message. The actor never solves challenges or changes IP addresses to get around a block, and it doesn't read archived (expired) ads.

### For AI agents and API users

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_API_TOKEN>")
run = client.actor("tsutseisoftware/fajn-brigady-scraper").call(run_input={
    "startUrls": [{"url": "https://www.fajn-brigady.cz/brigady/brno/"}], "maxItems": 100,
})
for job in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(job["title"], job["companyName"], job["salaryHourlyMax"], job["region"])
```

### Troubleshooting

| Status message | What to do |
|---|---|
| `Skipping unsupported URL` / `No supported URL` | Use a list of a category, town or region (`/brigady/...`, `/prace/...`) or a job URL, not a search or "new jobs" page. |
| `Job list not found` | The town or region in the URL doesn't exist on the website; pick it on the website and copy the URL again. |
| `No job list for …` / `No job list found for: …` | The website has no list for that town, region or category. Check the spelling (towns as on the websites; categories as in the menu of Fajn-brigady.cz, e.g. `skladnik`, `uklizecka`), or paste the list URL. |
| `INwork.cz is not read with "Categories"` | Categories are lists of Fajn-brigady.cz; paste an INwork.cz list URL into **Listing or job URLs** if you need its jobs too. |
| `bot challenge or rejects requests` | Try again later, or set **Proxy configuration** to Apify Proxy, group RESIDENTIAL, country CZ. |
| `stopped at "Max listing pages"` | Raise **Max listing pages** (0 = no limit). |
| `removed jobs were not checked` | The run did not read every list completely (Max jobs, Max listing pages, unread pages); raise the limits for monitoring runs. |
| Fewer jobs than expected | Check "filtered out: …" in the status message. |
| `extra pages read beyond the saved jobs` | The run read more pages than its saved jobs explain (a narrow filter, a list or job URL that doesn't exist). Use a narrower list (a category or town URL) instead of a filter over a long list, or lower **Max listing pages**. |

### More Czech job scrapers

The same output format for the other Czech job sources (many jobs are published on several of them):

- [Czech Jobs Aggregator](https://apify.com/tsutseisoftware/czech-jobs-aggregator): all these Czech job boards in one run: duplicates merged, one output format, monitoring
- [Jobs.cz Scraper](https://apify.com/tsutseisoftware/jobs-cz-scraper): Czech jobs with salary, region, requirements, monitoring and optional ARES company data
- [Prace.cz Scraper](https://apify.com/tsutseisoftware/prace-cz-scraper): Czech jobs with salary, region, monitoring and optional ARES company data
- [Úřad práce Scraper](https://apify.com/tsutseisoftware/urad-prace-scraper): all Czech Labour Office vacancies (MPSV open data) with salary, profession, shifts and jobs for foreigners
- [Pracezarohem.cz Scraper](https://apify.com/tsutseisoftware/pracezarohem-scraper): jobs near a town (GPS distance search) from all big Czech job sources, with the portal of every ad
- [JenPráce.cz Scraper](https://apify.com/tsutseisoftware/jenprace-scraper): Czech jobs and side jobs with salary, own ads told apart from Labour Office vacancies
- [StartupJobs.cz Scraper](https://apify.com/tsutseisoftware/startupjobs-scraper): IT and startup jobs with salary, technologies, seniority, remote work, company profiles and an IT market overview
- [Profesia.sk & .cz Scraper](https://apify.com/tsutseisoftware/profesia-scraper): Slovak and Czech jobs from Profesia with salary, region and monitoring
- [GastroJobs.cz Scraper](https://apify.com/tsutseisoftware/gastrojobs-scraper): Czech hotel, restaurant and catering jobs: salary, type of venue, employer company ID, monitoring

### Custom scrapers and changes

Need a custom version of this actor, another field or filter, or a scraper for a different website? Write to **info@tsutsei-software.com** with the website, the data you need and how often, and we will reply with a fixed quote. We work mainly with clients from the Czech Republic and from outside the EU.

More about us and all our actors: [tsutsei-software.com](https://tsutsei-software.com).

### FAQ

#### Is it legal to scrape Fajn-brigady.cz and INwork.cz?

The actor reads only job ads that the websites publish for everyone without logging in, at a moderate speed. It returns no contact persons, removes e-mails and phone numbers from the texts and hides the names of private advertisers (see below). The websites' terms of use restrict automated collection and commercial use of their content; you are responsible for how you use the data (GDPR, database rights and the websites' terms). If in doubt, consult a lawyer. Don't use the data to republish the job database.

#### Why is the company empty for some jobs?

Some ads are posted by private persons or sole traders under their own name ("Jan Novák"). A person's name and address are personal data, so for them `companyName`, `companyId`, the profile, the logo and the workplace address are null, `advertiserType` is `private`, and the name is replaced with `[employer]` in the title and the description. The town, region and district stay. An advertiser counts as a person when the website says so ("Soukromá osoba"), when the name contains the name of the person who posted the ad, or when it has the shape of a personal name (2–4 words, a Czech, Slovak or other common first name, a female surname in -ová, a title such as Ing. or Mgr., or an e-mail) without a legal form (s.r.o., a.s., z.ú.…) or a business word. It is a heuristic and privacy wins in doubtful cases: on all 726 advertiser names of the whole Fajn-brigady.cz list (1 792 jobs, 2026-11-06) it hid 164, all of them personal names, and no company. That is 12 % of the jobs on Fajn-brigady.cz (couriers, flyer distribution, small shops and stalls are often advertised by persons) and 11 % in an INwork.cz region.

#### What does the actor do with the contact person?

The job page shows the name of the person who posted the ad and a phone button. The actor never reads the phone, and the name is only used to recognise private advertisers and is replaced with `[contact person]` wherever it appears in the title or the description. E-mail addresses and phone numbers inside the texts become `[e-mail removed]` and `[phone removed]`.

#### How are wages converted?

Hourly amounts are multiplied by 160 hours for the monthly equivalent, monthly amounts divided by 160 for the hourly one (a day = 8 hours, a week = 40). "Až 45 000 Kč" (up to) gives only `salaryMax`; a single amount is both `salaryMin` and `salaryMax`.

#### Do I need a proxy?

Yes, and it's already set: the default is Apify Proxy with the RESIDENTIAL group and country CZ, and its cost is included in the price. Both websites answer requests from data-center IP addresses (Apify's own and the data-center proxy) with a Cloudflare challenge, which the actor never solves. Keep the default, or use your own Czech residential proxies.

#### Something doesn't work

Open an issue in the **Issues** tab and include the input you used.

This is an unofficial tool, not affiliated with or endorsed by Fajn skupina pracovních portálů s.r.o., the operator of Fajn-brigady.cz and INwork.cz.

***

Also known as a Fajn-brigady.cz API, INwork.cz scraper, "brigády export do Excelu" or "nabídky brigád Brno".

# Actor input Schema

## `startUrls` (type: `array`):

Fajn-brigady.cz or INwork.cz job lists, e.g. https://www.fajn-brigady.cz/brigady/ (all temporary jobs), https://www.fajn-brigady.cz/brigady/brno/, https://www.fajn-brigady.cz/brigady/manualni/ or https://www.inwork.cz/prace/jihomoravsky-kraj/, or individual job URLs. Open the site, pick a category, town or region and copy the URL. An ad listed on both sites is returned once. You can leave this empty and use Locations, Regions, Categories or Search words below instead (or together with it). Pages of "new jobs" and the websites' filter pages are not supported; use the actor's filters.

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

Towns to read, e.g. Brno, Ostrava, Kuřim. Each town is its list on Fajn-brigady.cz (temporary jobs) and on INwork.cz (regular jobs); "Job type" picks one of the websites. A town the website has no list for is reported by name (the run never returns the whole country instead), and the page read for it is charged as an extra page. A town name used in several districts is read on Fajn-brigady.cz only.

## `regions` (type: `array`):

Whole regions (kraje): the list of the region on Fajn-brigady.cz and on INwork.cz.

## `categories` (type: `array`):

Categories of temporary jobs as in the menu of Fajn-brigady.cz, by name or as in the URL: e.g. Skladník, Uklízečka, Prodavač, Číšník servírka, Administrativní pracovník, or a field such as manualni. Alone they are read for the whole country, with Locations or Regions for each of them. INwork.cz has other categories, so it is not read with Categories (paste its list URL if you need it).

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

Words to search for, e.g. skladník, řidič, Lidl. Alone they use the websites' own search over the whole country (Fajn-brigady.cz: position or company, INwork.cz: title; "Job type" picks one website). With Locations, Regions or Categories they keep the jobs of those lists whose title or company contains one of the words (the websites cannot combine a search with a place), and list pages read beyond the saved jobs are charged as extra pages.

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

Maximum number of jobs saved. You pay per saved job. 0 = no limit.

## `includeDetails` (type: `boolean`):

Open each job page to add the wage, start date, number of positions, contract type, posting date and more. Without it you get only the title, company and city from the listing (and a wage if it's in the title).

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

Add the full job description (contacts removed). Descriptions are written by employers and may be protected by copyright; use them for analysis, not for republishing.

## `descriptionFormat` (type: `string`):

With "Include description": plain text (default), clean HTML (headings, paragraphs, lists, bold, links) or Markdown. Contacts, the name of the person who posted the ad and the name of a private advertiser are removed in every format.

## `maxDescriptionLength` (type: `integer`):

Cut the description at about this many characters (whole paragraphs, then "…"). 0 = the whole description.

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

Safety limit for list pages per start URL (40 jobs per page). 0 = no limit. The whole Fajn-brigady.cz list has ~45 pages, a big INwork.cz region ~95.

## `keywords` (type: `array`):

Keep only jobs whose title contains at least one of these words (case and diacritics insensitive), e.g. 'sklad'.

## `excludeKeywords` (type: `array`):

Skip jobs whose title contains any of these words.

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

INwork.cz lists both regular jobs and temporary jobs; Fajn-brigady.cz only temporary jobs.

## `onlyWithSalary` (type: `boolean`):

Skip jobs with a negotiable wage ('Dle domluvy') and no amount in the title.

## `minHourlySalary` (type: `integer`):

Keep only jobs whose wage (upper bound, converted to hourly at 160 h/month) is at least this. Without job details only wages from the title are known.

## `minMonthlySalary` (type: `integer`):

Keep only jobs whose wage (upper bound, converted to monthly) is at least this.

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

Keep only jobs posted in the last N days. Needs job details.

## `employmentTypes` (type: `array`):

Keep only jobs with one of these types (INwork.cz). Needs job details.

## `suitableFor` (type: `array`):

Keep only jobs marked as suitable for one of these groups. Needs job details.

## `jobPositions` (type: `array`):

Keep only jobs whose position (e.g. 'Skladník', 'Číšník, servírka') contains one of these words. Needs job details.

## `descriptionKeywords` (type: `array`):

Keep only jobs whose title or description contains one of these words (e.g. "VZV", "víkend", "home office"); letter case and diacritics don't matter. The description is read for the filter even when "Include description" is off. Needs job details.

## `excludeDescriptionKeywords` (type: `array`):

Skip jobs whose title or description contains any of these words (e.g. "noční", "živnostenský list"); letter case and diacritics don't matter. The description is read for the filter even when "Include description" is off. Needs job details.

## `companies` (type: `array`):

Keep only jobs whose company name contains one of these words.

## `excludeCompanies` (type: `array`):

Skip jobs whose company name contains any of these words.

## `excludeAgencies` (type: `boolean`):

Skip jobs posted by staffing agencies: where the site says so (INwork.cz) or where the company is a well-known staffing agency (Manpower, Randstad, Adecco, Grafton…). Needs job details.

## `onlyNewOrChanged` (type: `boolean`):

Return (and pay for) only jobs that are new since the previous run with the same Monitoring key, or whose title, company, city or wage in the list changed. Job pages are downloaded only for those. Unchanged jobs are not charged; every list page read is charged as "Monitoring: listing page checked" ($0.0007 per page of up to 40 jobs). Ideal for a daily schedule.

## `includeRemovedJobs` (type: `boolean`):

With "Monitoring: only new or changed jobs": also return jobs of the previous run that are no longer in the lists, as rows with changeType "removed" (their last known values, lastSeenAt and removedAt). Checked only when the run reads every list completely (Max jobs, Max listing pages, unread pages or rejected requests skip the check, and the status message says why). Use the same start URLs in every run. Removed rows are charged like any other job.

## `includeUnchangedJobs` (type: `boolean`):

With "Monitoring: only new or changed jobs": also return the jobs that did not change, as rows with changeType "unchanged" (charged like any other job, their job pages are read again). One run then gives the whole list with every job marked new, updated or unchanged. Off by default: only the changes are returned and paid.

## `monitoringKey` (type: `string`):

Name of the saved state. Use a different key for each set of lists you monitor (e.g. "brno-sklad"). Empty = "default". The value is stored encrypted.

## `compact` (type: `boolean`):

Keep only the core fields of every row: id, title, company, town, region, district, job type, category, position, wage (text, numbers, hourly and monthly), start, positions, contract type, suitable for, posting date, link and the monitoring fields (and the description when you ask for it).

## `excludeEmptyFields` (type: `boolean`):

Drop fields that are null or an empty list from every row (id, source, url, title and scrapedAt always stay).

## `slackWebhookUrl` (type: `string`):

Incoming webhook of your Slack channel (https://hooks.slack.com/services/...). Stored encrypted.

## `discordWebhookUrl` (type: `string`):

Webhook of your Discord channel (https://discord.com/api/webhooks/...). Stored encrypted.

## `telegramBotToken` (type: `string`):

Token of your Telegram bot (from @BotFather). Used with "Telegram chat ID". Stored encrypted.

## `telegramChatId` (type: `string`):

Chat, group or channel ID the bot writes to (e.g. -1001234567890).

## `webhookUrl` (type: `string`):

Any HTTPS URL (Zapier, Make, n8n, your server): gets a POST with JSON {title, summary, totalSaved, runUrl, datasetUrl, items}.

## `notificationLimit` (type: `integer`):

How many jobs are listed in one notification (the rest as "…and N more").

## `notifyOnlyChanges` (type: `boolean`):

Send nothing when the run saved no job (e.g. a monitoring run without new jobs).

## `fastMode` (type: `boolean`):

Reads with up to 5 requests at a time and 120 per minute instead of 3 and 60, which helps most with job pages ("Include job details"). It stays polite: the moment a website answers "too many requests", shows a challenge or slows down, the run switches back to the normal speed for the rest of the run, and it never changes IP address to get around a limit. Extra charge "Fast mode" (see the Pricing tab) only for jobs saved while the fast speed was on.

## `proxyConfiguration` (type: `object`):

Both websites are behind Cloudflare and reject requests from data-center IP addresses, so the default is Apify Proxy with residential IPs in the Czech Republic (included in the price). You can also use your own proxies.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://www.fajn-brigady.cz/brigady/brno/"
    }
  ],
  "maxItems": 20,
  "includeDetails": true,
  "includeDescription": false,
  "descriptionFormat": "text",
  "maxDescriptionLength": 0,
  "maxPages": 20,
  "onlyWithSalary": false,
  "excludeAgencies": false,
  "onlyNewOrChanged": false,
  "includeRemovedJobs": false,
  "includeUnchangedJobs": false,
  "compact": false,
  "excludeEmptyFields": false,
  "notificationLimit": 10,
  "notifyOnlyChanges": true,
  "fastMode": false,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "CZ"
  }
}
```

# Actor output Schema

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

No description

## `summary` (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 = {
    "startUrls": [
        {
            "url": "https://www.fajn-brigady.cz/brigady/brno/"
        }
    ],
    "maxItems": 20,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "CZ"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("tsutseisoftware/fajn-brigady-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 = {
    "startUrls": [{ "url": "https://www.fajn-brigady.cz/brigady/brno/" }],
    "maxItems": 20,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "CZ",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("tsutseisoftware/fajn-brigady-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 '{
  "startUrls": [
    {
      "url": "https://www.fajn-brigady.cz/brigady/brno/"
    }
  ],
  "maxItems": 20,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "CZ"
  }
}' |
apify call tsutseisoftware/fajn-brigady-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,tsutseisoftware/fajn-brigady-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/8Pa3h4LOIEX42QzIu/builds/pW36qggUeYQSIWntj/openapi.json
