# Jobs.cz Scraper: Czech Jobs, Salaries & Companies (`tsutseisoftware/jobs-cz-scraper`) Actor

Scrape Jobs.cz jobs with salary, region, district, seniority and the ad's requirements and benefits, using the website's own filters. Track new, removed and re-posted jobs, get a free salary summary and optional ARES company data. Recruiter contacts only if you turn them on. Unofficial.

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

## Pricing

from $0.43 / 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

## Jobs.cz Scraper: Czech Jobs, Salaries & Companies

Get job listings from **Jobs.cz**, the largest Czech job board, as clean structured data. Search by **keywords**, **location** with an optional **radius in km**, **regions**, **categories** and the **website's own filters** (employment type, contract type, education, salary, remote work, language skills, language of the ad, employer type, posting date), or paste any Jobs.cz search URL. For each job you get the title, company, place of work with its **region (kraj) and district (okres)**, **salary when the employer shows it** (with monthly and hourly equivalents), employment type, remote/hybrid flag, posting and expiry dates, categories and Atmoskop employer reviews. Every job also gets its **seniority** from the title, the salary as one export-friendly text and how many days ago it was posted. Optionally the actor opens each job page and adds the **description** (plain text or Markdown, e-mails and phone numbers removed) split into **responsibilities, requirements, nice to have and what the employer offers**, benefits, required education and languages, contract type, start date, the workplace address with GPS and whether the advertiser is a recruitment agency.

Built for **watching the job market**: schedule it daily in monitoring mode and you get, and pay for, only jobs that are new or changed, plus, if you want, the jobs that **disappeared** since the last run and how many days they were online, and **re-posted** ads flagged or skipped. Every run also saves a free **market summary** (salary median and quartiles by region and category, top employers, remote share). Optional **company data from ARES**, the public Czech business register (company ID, legal form, size, main activity), for a small extra charge.

### Why this Jobs.cz scraper?

- 🔎 **The website's filters, applied by the website.** Employment type (full-time, part-time, brigáda, internship), contract type (HPP, IČO, DPP/DPČ), education, minimum salary, work from home, language skills, language of the ad (e.g. jobs in English), "suitable for" groups, direct employers vs. agencies, posting date (24 h / 3 days / 7 days) and **categories (obor)** are sent to Jobs.cz, so the results match what you see on the website and filtered-out jobs cost nothing.
- 🗺️ **Region and district for every job**, worked out from the place of work with the official list of Czech municipalities (ČSÚ / RÚIAN), without extra requests. Filter by **regions** or **districts**.
- 📍 **Radius search like on the website:** `brno` + 20 km returns "Brno-město a okolí" (Jobs.cz's own radius search, any whole number of km up to 100), for each location of the run.
- 🧩 **Searches over the 9 990-job page limit are split by region.** Jobs.cz shows at most 9 990 jobs per search. For a bigger search without a location, the actor reads it region by region and abroad (15 searches) and tells you if these searches didn't cover all its jobs (e.g. jobs placed only in "Česko").
- 💰 **Salaries you can compute with.** `salaryMin`, `salaryMax`, currency and period as numbers, plus monthly and hourly equivalents. Only salaries shown on the website are returned; when an employer hides the salary, the actor returns `null`.
- 📊 **Free market summary** after every run (`SUMMARY` record): median and quartiles of monthly salary overall, by region and by category, top 20 employers, share of remote/hybrid/onsite jobs, agencies and employment types.
- 📬 **Monitoring mode:** a daily feed of new and changed jobs (`changeType`), optionally with **removed jobs** (`removedAt`, `daysOnline`) to see how fast positions are filled, and **re-posted ads** (`isRepost`) flagged or skipped.
- 🧾 **The ad, structured:** responsibilities, requirements, nice-to-have and employer offers as lists, seniority, start date, company profile and logo, plain text or Markdown description for LLMs.
- 🏢 **Company data from ARES** (optional, extra charge): IČO, legal form, registered office, employee count range, main activity (CZ-NACE), founding date.
- 🔒 **Privacy first.** Recruiter contacts only if you turn them on (off by default, see [Recruiter contacts and privacy](#recruiter-contacts-and-privacy-optional-personal-data)); e-mails and phone numbers inside descriptions are replaced with `[e-mail removed]` / `[phone removed]`. Employers that are private persons are never named.
- 🚀 **Optional fast mode:** up to 120 requests per minute instead of 30, with automatic slow-down when the website asks (small extra charge).
- ⚡ **Fast and light:** one request gives 30 jobs. No browser, no proxy needed.

How it compares with typical alternatives in Apify Store (checked 2026-10-13):

| | This actor | Typical alternatives |
|---|---|---|
| Price per 1 000 jobs | **$0.49**, description and details included | $0.50–$1.10 for the popular ones, often $2–9; details sometimes extra |
| Website filters (employment and contract type, education, salary, remote, language of the ad, employer type, suitable for, category) | all of them, applied by Jobs.cz | keyword and location, a few filters, or "paste a URL" |
| Several keywords, locations, regions and categories in one run | yes, each job saved and charged once | often one keyword and one location |
| Radius around a location (km) | yes, Jobs.cz's own radius search, for each location | one alternative (0–50 km), two only via a pasted URL, most none |
| Region and district for every job | yes | rarely |
| Salary | numbers, period, currency, monthly and hourly equivalents and a text | usually text or min/max |
| Description sections, seniority, start date | yes | rarely |
| Monitoring | new, changed, removed, reappeared and re-posted jobs | mostly none |
| Company register data (ARES) | optional | no |
| Max jobs per company, free market summary | yes | no |
| Searches above the website's per-search limit | split by region automatically | stop at the limit |
| Proxy | not needed | some need a residential proxy |
| Recruiters' names and phones | only if you turn them on, exactly as the job page shows them, never for private employers; no extra charge | some return company phones and e-mails, some sell e-mail look-ups |
| Speed | 30 requests per minute, optional fast mode up to 120 with automatic slow-down | varies |

Looking for Prace.cz? Use [Prace.cz Scraper](https://apify.com/tsutseisoftware/prace-cz-scraper), the same core data, filters and monitoring for Prace.cz. Many jobs are published on both websites.

### What data can you get?

| Field | Example |
|---|---|
| Title, seniority, company | Datový analytik ZÚ, `senior` (when the title says so), Oborová zdravotní pojišťovna zaměstnanců bank, pojišťoven a stavebnictví |
| Location, region, district | Praha, Hlavní město Praha, Praha |
| Workplace (with **Add description and details**) | street, house number 1225/1, city part Braník, postal code, GPS 50.04035, 14.429325 |
| Salary | 55 000 – 75 000 CZK per month (monthly 55 000 – 75 000, hourly 343.75 – 468.75), `salaryText` "55 000–75 000 CZK/month" |
| Employment type, work arrangement | fullTime, onsite |
| Posted (time, date, days ago), valid through | 2026-10-13T13:40:07Z, 2026-10-13, 0, 2026-11-13 |
| Categories, professions | IS/IT: Vývoj aplikací a systémů, Datový analytik |
| Suitable for | noExperience |
| Company logo, company profile, career site, Atmoskop | companyLogoUrl, companyProfileUrl, careerPageUrl, atmoskopUrl, atmoskopReviewCount |
| Agency (with details or the Employer type filter) | false |
| With **Add description and details** | description (plain text or Markdown) and its length, **responsibilities, requirements, nice to have, what the employer offers** as lists, benefits, education, languages, contract types, start date, workplace address and GPS |
| With **Add company data from ARES** | IČO 17774713, Akciová společnost, 500-999 employees, CZ-NACE activity, founding date |
| With **Monitoring: include removed jobs** | changeType `removed` (removedAt, firstSeenAt, lastSeenAt, daysOnline) and `reappeared` |

See the **Output** tab for all fields. The dataset has views for **Overview**, **Salaries**, **Companies**, **Work arrangement**, **Details**, **Monitoring** and **Removed jobs**.

### How to scrape Jobs.cz

1. Enter **Search queries** (e.g. `python`, `účetní`) and a **Location** (e.g. `praha`, `brno`, `jihomoravsky-kraj`) or several **Locations**, **Regions** or **Categories**, or paste Jobs.cz URLs into **Jobs.cz URLs**: set your search and filters on the website and copy the URL, e.g. `https://www.jobs.cz/prace/brno/?q[]=python`.
2. Optionally choose **Website filters** and **Filters**, set **Max jobs**, and turn on **Add description and details** or **Add company data from ARES** if you need them.
3. Click **Start** and download the results as JSON, CSV or Excel, or use the API. The market summary is in the **Storage → Key-value store** tab as `SUMMARY` (and in the **Output** tab).

### Use cases

#### New jobs by e-mail, Slack or Google Sheets every morning

Save this input as a task, add a **Schedule** (e.g. daily at 7:00) and connect an **integration** in Apify Console (e-mail, Slack, Google Sheets, Make, Zapier or a webhook) that sends the dataset after each run. Thanks to monitoring mode, each run returns only jobs that are new or changed, so the message contains just the news.

```json
{
  "searchQueries": ["python", "django"],
  "location": "praha",
  "maxItems": 0,
  "onlyNewOrChanged": true,
  "monitoringKey": "python-praha"
}
```

#### Salary research for a region and category

Read jobs with a salary in two regions for one category and open the free `SUMMARY` record: median and quartiles of the monthly salary by region and by category, and the share of jobs that show a salary. The **Salaries** view shows the individual jobs.

```json
{
  "categories": ["Ekonomika a podnikové finance"],
  "regions": ["jihomoravsky-kraj", "zlinsky-kraj"],
  "onlyWithSalary": true,
  "maxItems": 500
}
```

#### Jobs in English for expats

```json
{
  "adLanguages": ["en"],
  "location": "brno",
  "workArrangement": ["hybrid", "remote"],
  "maxItems": 200
}
```

#### Junior jobs with structured requirements for an LLM or a spreadsheet

Requirements, responsibilities and offers as lists, the description in Markdown, a few jobs per employer and no empty fields:

```json
{
  "searchQueries": ["junior"],
  "categories": ["IS/IT: Vývoj aplikací a systémů"],
  "maxItems": 50,
  "maxItemsPerCompany": 2,
  "includeDetails": true,
  "descriptionFormat": "markdown",
  "omitEmptyFields": true
}
```

#### Employer insights: who is hiring, how big they are and what they do

Job market analysis with company context: add **company data from ARES** (extra charge, see below) to see the hiring employers' legal form, size (employee count range) and main activity, e.g. to compare which industries and company sizes hire in a region. Direct employers only, so agencies are left out.

```json
{
  "location": "brno",
  "jobTypes": ["fullTime"],
  "employerType": "directEmployer",
  "includeCompanyInfo": true,
  "maxItems": 200
}
```

#### Removed jobs and time to fill

Run the same search daily with removed jobs turned on. Jobs that are no longer on the website come back as rows with `changeType: "removed"`, `removedAt` and `daysOnline` (days from posting to the last run that still saw the job), a simple measure of how fast positions are filled. Keep **Max jobs** at 0 (no limit) so the whole search is read; see *Monitoring mode explained*.

```json
{
  "searchQueries": ["svářeč"],
  "regions": ["moravskoslezsky-kraj"],
  "maxItems": 0,
  "onlyNewOrChanged": true,
  "includeRemovedJobs": true,
  "monitoringKey": "svarec-msk"
}
```

#### IT development jobs across the country, daily feed of new ones

A broad search can have more jobs than the website shows for one search (9 990). With **Max jobs** and **Max pages per search** at 0, such a search without a location is read region by region and abroad. In monitoring mode you get only the jobs that are new or changed since the last run.

```json
{
  "categories": ["IS/IT: Vývoj aplikací a systémů"],
  "maxItems": 0,
  "maxPages": 0,
  "onlyNewOrChanged": true,
  "monitoringKey": "it-dev-cz"
}
```

#### Side jobs (brigády) on a work agreement in South Moravia posted in the last 3 days

```json
{
  "location": "jihomoravsky-kraj",
  "jobTypes": ["temporary"],
  "contractTypes": ["workAgreement"],
  "postedWithinDays": 3,
  "maxItems": 100
}
```

#### Website filters (applied by Jobs.cz)

| Input | What it does |
|---|---|
| **Categories** | Category (obor), e.g. `IS/IT: Vývoj aplikací a systémů`. Without Jobs.cz URLs each category is one search on the website (`/prace/<location>/<category>/`); with URLs the actor checks the categories of each job (job URLs: the job page doesn't list categories, so these jobs are kept and the status message says so). |
| **Employment type** | Full-time, part-time, temporary / side job (brigáda), internship. |
| **Contract type** | Employment contract, self-employed (IČO), work agreement (DPP/DPČ). |
| **Education** | Elementary, apprenticeship, high school with diploma, university. |
| **Salary from (CZK per month)** | Minimum monthly salary, e.g. 40000. The website also matches jobs whose salary is hidden on the page; these come without a salary. |
| **Work from home / remote** | On site, hybrid, remote. |
| **Language skills** | Czech, English, German, French, Russian, Polish, Italian, Spanish. |
| **Language of the ad** | Czech, English, Slovak, German, e.g. English for jobs in English. |
| **Suitable for** | No experience, graduates, seniors, parents on leave, people with disabilities, refugees. |
| **Employer type** | Direct employers only (exclude agencies) or agencies only. |
| **Posted in the last N days** | 1, 3 and 7 days are applied by the website; other values are checked by the actor on the posting date. |
| **Posted in the last N hours** | Checked by the actor on the exact posting time (e.g. 6 for a run every 6 hours); the website pre-filters the matching day window, so older jobs cost nothing. |
| **Regions** | Without a Location and without Jobs.cz URLs, each search runs once per region on the website (`/prace/<region>/`). |
| **Locations** | Several places, one search each (e.g. `praha`, `brno`, `ostrava`), at most 20; can be combined with **Location**. |
| **Radius (km)** | Also jobs up to N km around each Location, like "+ okolí 20 km" on the website (e.g. `brno` + 20 = "Brno-město a okolí"). See [Radius search](#radius-search). |

The website filters are also added to your **Jobs.cz URLs**, unless the URL already sets the same filter. **Location** and **Locations** apply to **Search queries**, **Categories** and website filters, not to pasted URLs. One run makes at most 120 searches (search queries × locations or regions × categories).

#### Radius search

**Radius (km)** adds the jobs up to N km around each **Location**, the same search as "+ okolí N km" on Jobs.cz. The actor first reads the location's page once to get the website's ID of the place (one extra request per location, e.g. `brno` = district Brno-město), then runs every search of that location with the radius. The results and their number are the website's own: Location `brno` gave 1 844 jobs, with 20 km 2 204 and with 50 km 2 636 (2026-10-13).

```json
{
  "searchQueries": ["skladník"],
  "locations": ["brno", "olomouc"],
  "radiusKm": 20,
  "maxItems": 500
}
```

- **0** (the default) = only the place itself, exactly as before. The website offers 10, 20, 30, 40 and 50 km; any whole number up to 100 works.
- Works for towns, districts and parts of Prague (`praha-5`). **Regions** (`jihomoravsky-kraj`) and abroad (`zahranici`) have no radius and are searched as they are; the log and the status message say so.
- Without a Location the run stops with a clear message (a radius needs a place). **Regions** and **Districts** together with a radius filter the jobs found around the location.
- A radius search is **never split region by region**; above 9 990 jobs (e.g. Praha + 100 km) use a smaller radius, more filters or keywords.
- **Jobs.cz URLs** keep their own radius: a www.jobs.cz URL with "+ okolí" (`locality[radius]`) is read with that radius. A www.jobs.cz URL without it (e.g. `https://www.jobs.cz/prace/brno/`, which the website shows with 50 km by default) is read for the place itself, as before.
- Jobs in several places are found when any of their places is in the radius; `location` shows the first place.
- The numbers are those of the Jobs.cz search the actor reads (beta.jobs.cz, the `sourceUrl` of each job). The classic www.jobs.cz page can show a somewhat different count for the same place, because it searches differently (e.g. Brno + 20 km: 2 194 there, 2 204 here; the same `python` search gave the same 54 jobs on both).

#### Filters (applied by the actor, you don't pay for filtered-out jobs)

| Input | Example |
|---|---|
| **Regions** (with a Location, Locations or Jobs.cz URLs) | the region of each job's place of work must be one of the chosen regions |
| **Districts** | `Brno-venkov`, `Praha-východ` (district of the place of work; Prague is `Praha`) |
| **Only jobs with a salary** | skip jobs without a visible salary |
| **Title must contain**, **Title must not contain** | `junior`, not `senior` |
| **Company**, **Exclude companies** | `Škoda`, not `Manpower` |
| **Max jobs per company** | at most N jobs from one employer, so a few big employers or agencies don't fill the results |

Safety limits in **Advanced**: **Max pages per search** (30 jobs per page). **Max jobs per search** limits each query, region, category or URL separately.

A job found by several searches of one run (e.g. `python` and `django`, or overlapping locations) is saved and charged **once**. Results come in the website's order: newest first, with the website's promoted jobs at the top of each page. Jobs.cz doesn't sort by salary; sort the exported data instead.

#### Description sections, seniority and start date

With **Add description and details**, the description is also split by its headings into `responsibilities`, `requirements`, `niceToHave` and `employerOffers` (lists of the ad's own bullet points). The actor recognises the usual Czech, Slovak and English headings ("Náplň práce", "Co vás čeká", "Požadujeme", "Výhodou bude", "Nabízíme", "Responsibilities", "What we offer"…); an ad without such headings has empty lists and the full text in `description`. `startDate` and `startImmediately` are filled when the description states them ("nástup od 1. 11. 2026", "nástup ihned"). **Description format** `markdown` keeps headings, bold text and lists, which is handy for LLMs. `descriptionLength` is the length of the full text. `seniority` (intern, junior, mid, senior, lead, executive) is read from the title of every job, also without details; titles without a level give `null`.

#### Region and district

`region` (kraj) and `district` (okres) come from the place of work shown in the list (`location`), matched against the official list of Czech municipalities (ČSÚ code lists and RÚIAN links, embedded in the actor, no extra requests). Places like "Praha-Smíchov" or "Brno-Černovice" are parts of a municipality and count as Prague (`district: "Praha"`) or Brno (`Brno-město`). When a name belongs to several municipalities and the actor can't tell which one is meant (e.g. "Staré Město"), or the place is "Česko" or abroad, both fields are `null`. For jobs with several places, `region` and `district` describe the first place with a known region and `regions` lists the regions of all places. When the website itself limited the search to a region (**Regions**, or a region as **Location**), a place the list can't decide (e.g. "Ostrov") gets that region. With **Add description and details**, region and district come from the job page itself when it gives them, together with the workplace's house number, city part (`cityPart`) and GPS (`gpsLat`, `gpsLon`) of the first workplace; these are never returned for a private employer. In our Jobs.cz samples (about 2 000 jobs) almost every job got a region.

#### Monitoring mode explained

With **Monitoring: only new or changed jobs** each run compares jobs with the previous run that used the same **Monitoring: key**. A job is returned as `changeType: "new"` when it wasn't seen before, or `"updated"` when its title, company, salary, place or type of work changed (`changedFields` says which of title, salary and work arrangement). Jobs not seen for 90 days are forgotten. Use one key per watched search, and keep the same search and filters for that key.

**Monitoring: include removed jobs** adds jobs from the previous run that the current run no longer finds, with their last known title, company, place, region, salary and URL, `changeType: "removed"`, `firstSeenAt`, `lastSeenAt`, `removedAt` and `daysOnline`. Each removed job is reported once; if it comes back to the website later, it is returned once as `changeType: "reappeared"` (with `removedAt` of the earlier report).

**Re-posted ads:** in monitoring mode a new job with the same title, employer and place as a job seen before under another ID (within 90 days) gets `isRepost: true` and `repostOf` (the ID of the earlier job). Re-posts often mean a position that is hard to fill. **Monitoring: skip reposted jobs** leaves them out of the results instead (they are not charged). Only a short hash of title, employer and place is kept for this, next to the monitoring state. A job counts as removed when it is no longer in your search on the website: usually it expired or was taken down, but it can also mean the employer changed it so that the **website filters** no longer match it (e.g. a lower salary with **Salary from**). The actor's own **Filters** never make a job "removed": a job that is still on the website but filtered out by them is not reported. To avoid false alarms, removed jobs are checked **only when the run read the whole search**: if it stopped at **Max jobs**, **Max jobs per search** or **Max pages per search**, at the 9 990-job limit of the website, at your maximum cost per run, if some pages could not be read or the site rejected requests, or if the region searches of a split search showed fewer jobs than the search has, nothing is marked as removed and the status message says why. With **Posted in the last N days**, jobs that simply got older than N days are not reported as removed. **Removed rows are normal dataset rows and are charged like any other job.**

#### Market summary (SUMMARY)

Every successful run writes a free JSON record `SUMMARY` to the run's default key-value store (link in the **Output** tab, or `GET https://api.apify.com/v2/key-value-stores/<storeId>/records/SUMMARY`). It covers all jobs the run found that match your search and filters, each job once; in monitoring mode also the unchanged jobs that were not saved again.

| Key | Content |
|---|---|
| `counts` | jobs matched and saved; in monitoring mode new, updated, unchanged, removed and reappeared; jobs with company data |
| `salary` | number and share of jobs with a salary; monthly CZK median and quartiles (`p25`, `median`, `p75`) overall, `byRegion` and `byCategory` (groups with at least 5 jobs) |
| `topCompanies` | top 20 employers by number of jobs |
| `workArrangement`, `employmentTypes`, `regions` | number and share of jobs per value |
| `agencies` | share of agencies among jobs where it is known (with details or the Employer type filter), otherwise `null` |

The monthly figure of a job is the middle of its salary range; hourly and daily salaries are converted at 160 hours per month. "Yearly" salaries under 120 000 CZK are monthly amounts with a wrong period on the website and are left out of the statistics (the job itself still has them). The summary contains no personal data: companies only, no recruiter data and no descriptions.

#### Company data from ARES (optional, extra charge)

With **Add company data from ARES (extra charge)** the actor looks up each employer in **ARES** (Administrativní registr ekonomických subjektů), the public register of economic subjects run by the **Ministry of Finance of the Czech Republic** (ares.gov.cz), and adds:

| Field | Example | Note |
|---|---|---|
| `companyIco` | `14205823` | company ID (IČO) |
| `companyLegalForm` | `Společnost s ručením omezeným` | converted from the ARES code to its name |
| `companyAddress` | `28. října 2020/231, Mariánské Hory, 70900 Ostrava` | registered office, not the workplace |
| `companyEmployeeRange` | `1-5` | ČSÚ employee count category converted to a range (`0` = no employees, `10000+`); `null` when not stated |
| `companyNace` | `73110 Činnosti reklamních agentur` | main activity, CZ-NACE code and name (ČSÚ) |
| `companyFoundedAt` | `2022-01-31` | date of establishment |
| `companyInfoSource`, `companyInfoMatch`, `companyInfoRetrievedAt` | `ARES`, `exact`, `2026-10-13T10:00:00Z` | where and when the data come from |

How it works and what to expect:

- **Off by default.** Without this option the actor never contacts ARES.
- **Matching by the company name.** ARES is searched by the employer name from the job, and data are returned only for an **unambiguous match**: the same name, including the legal form (case, diacritics and punctuation don't matter, "spol. s r.o." = "s.r.o."), and exactly one active company. Brand names without a legal form ("IKEA Česká republika", "DATART") or several companies of the same name get no data. In a live sample 25 of 30 full-time jobs of direct employers in Brno got company data; the company IDs we checked by hand were all correct.
- **The advertiser, not its client.** The data describe the company in `company`. Jobs known to be posted by a recruitment agency (`isAgency: true`, known with details or the Employer type filter) are not looked up, because the agency is not the employer. When `isAgency` is unknown, an agency's job gets the agency's data.
- **No private persons.** Employers whose name looks like a person are never sent to ARES. When ARES says the employer is a natural person (a sole trader, legal forms 101–108, 424, 425), nothing from ARES is returned and the job's company name and address are removed as well.
- **ARES data are informative only**, not an official record, and can be up to about two weeks behind the source registers. Each name is looked up once per run, one request at a time (at most about 60 per minute), always directly. Results are kept for 30 days in a key-value store `jobs-cz-ares-cache` in your account, so repeated runs don't ask again (sole traders are never stored). If ARES is unavailable or limits requests, jobs are saved without company data and the status message says so.
- **Price:** ARES is a free public service; the extra charge pays for the matching and integration. You pay the **Company info** event only for saved jobs that got company data. Jobs without a match, agencies, private persons and failed lookups cost nothing extra. Your **maximum cost per run** is respected: when it couldn't cover the next job with company data, the actor stops adding company data and keeps saving jobs without it while the budget allows.
- About 1–2 seconds per new company name, so a run with company data takes longer.

This actor is not affiliated with or endorsed by the Ministry of Finance of the Czech Republic.

#### Recruiter contacts and privacy (optional, personal data)

**Include recruiter contacts (personal data)** adds `recruiterName`, `recruiterPhone` and `recruiterEmail` to each job. Off by default; it needs **Add description and details**.

- **What and from where.** Only what the job page on Jobs.cz shows in its "Kontaktní údaje" (contact details) block: the contact person's name and the phone number of its phone link (as `+420…`). An e-mail only when the block shows one, which the site almost never does, so `recruiterEmail` is nearly always `null`. Not the block's address or company. Nothing is guessed, derived or looked up elsewhere (no e-mail finding, no other websites), and e-mails and phone numbers inside descriptions stay removed.
- **When not.** Never for employers that look like a private person (also when ARES says so), for jobs on an employer's career site, for jobs whose page couldn't be read, or for people who asked to be left out. There is no list of recruiters, no count per recruiter and no contact in the `SUMMARY` record.
- **Nothing is kept by the actor.** Contacts go only into your run's dataset: never into the monitoring state, the ARES cache, the `SUMMARY` record or the log. A change of the contact person doesn't count as a change in monitoring mode. No extra charge for contacts.
- **You are the data controller (GDPR).** You need your own legal basis, typically a legitimate interest, and only for **contacting the person about that specific job ad** (e.g. as an applicant or on an applicant's behalf, or to clarify the offer).
- **No marketing.** In Czechia, commercial e-mails and SMS without prior consent are prohibited (Act No. 480/2004 Coll., § 7), also to business addresses, and marketing calls need consent (Act No. 127/2005 Coll., § 96), also to business numbers. Don't use the contacts for marketing or commercial offers, don't resell them and don't build contact databases from them.
- **Inform and delete.** Tell the person who you are, why you contact them, where you got the contact (Jobs.cz job ad) and that they may object (Art. 14 GDPR), at the latest when you first contact them, otherwise within one month. Respect objections. Delete a dataset with contacts as soon as you no longer need it: named Apify storages are never deleted automatically.
- **To be left out of future results** (recruiters): open an issue in the **Issues** tab of this actor in Apify Store, or write to Apify's privacy contact privacy@apify.com. We remove the person from all future runs; data already in a user's dataset are that user's responsibility, so please contact them or the employer too.

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

**Fast mode** reads Jobs.cz up to 4× faster: at most **120 requests per minute and 4 at a time** instead of 30 per minute and 2 at a time. It stays polite: the moment the website answers "too many requests", "service unavailable" or slows down (or shows a challenge page), the run switches back to the normal speed for the rest of the run, it waits when the website sends `Retry-After` (and stops with what it has saved when asked to wait more than 2 minutes), and it never changes IP address to get around a limit; with a proxy, the whole run uses one proxy session. The status message says when and why the run slowed down.

- **Price:** the **Fast mode** event, **$0.01 per 1 000 jobs** ($0.00001 per job), only for jobs saved while the fast speed was on. Jobs saved after the switch back to the normal speed have no fast-mode charge. It respects your maximum cost per run: when the budget doesn't cover the charge, the run continues at the normal speed.
- **Limits stay the same:** Max jobs, Max pages per search and the website's per-search limit. A listing page has 30 jobs, so the speed matters most with **Add description and details** (one request per job).
- **Measured** (2026-10-14, 256 MB, no proxy, 100 jobs with description and details from Praha): 68 requests in 35 seconds in fast mode, about 4× faster than at the normal speed (about 2 minutes 20 seconds).
- Please don't run several fast-mode runs at the same time.

### How much does it cost to scrape Jobs.cz?

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

| What | Price |
|---|---|
| Job (every saved row, including removed jobs in monitoring mode; description and details included) | **$0.49 per 1 000 jobs** ($0.47 Bronze, $0.45 Silver, $0.43 Gold and higher Apify plans) |
| **+ Company info** (only jobs that got company data from ARES) | **+$1.00 per 1 000 jobs** |
| **+ Fast mode** (only jobs saved while the optional fast mode was on) | **+$0.01 per 1 000 jobs** |
| Recruiter contacts (optional) | no extra charge |
| Filtered-out jobs, unchanged jobs in monitoring mode, the `SUMMARY` record | free |

Platform usage is included; Jobs.cz works without a proxy. The actor makes at most 2 requests at a time and 30 per minute, to keep the load on the website low (in the optional fast mode at most 4 at a time and 120 per minute, slowing down automatically when the website asks). Typical runs:

| Scenario | Requests to Jobs.cz | Typical time |
|---|---|---|
| 20 jobs from a search | 1 | a few seconds |
| 1 000 jobs from searches | ~34 | ~1–2 minutes |
| 100 jobs with description and details | up to ~60 (career-site jobs have no job page) | ~2–3 minutes |
| 30 jobs with company data | 1 (+ ~40 to ARES) | ~1 minute |
| 20 jobs around a location (Radius) | 2 (1 more per location) | a few seconds |

#### Performance

Measured on our test runs (2026-10-13, 256 MB, no proxy): the default input (20 jobs) takes **1 request and about 3 seconds**; 90 jobs took 4 requests and 9 seconds; 300 jobs on the Apify platform about 30 seconds; 20 jobs with description and details 16 requests and 32 seconds. Listing pages give 30 jobs each, so about 1 000 jobs a minute; with **Add description and details** about 30 jobs a minute, because each job page is one more request and the actor deliberately stays at 30 requests per minute. Memory use stays around 100–120 MB.

### Input example

```json
{
  "searchQueries": ["python"],
  "location": "praha",
  "maxItems": 100,
  "jobTypes": ["fullTime"],
  "includeDetails": false,
  "includeCompanyInfo": false
}
```

### Output example

A job read from its job URL, so with the job page data (lists shortened here; jobs from searches also have `categories`, `professions`, `workArrangement` and Atmoskop data):

```json
{
  "id": "jobs:3807d9a5-02bf-48da-8851-29f0e9d54785",
  "source": "jobs.cz",
  "url": "https://www.jobs.cz/rpd/2001478070/",
  "jobId": "3807d9a5-02bf-48da-8851-29f0e9d54785",
  "recruitmentId": "2001478070",
  "title": "Datový analytik ZÚ",
  "seniority": null,
  "company": "Oborová zdravotní pojišťovna zaměstnanců bank, pojišťoven a stavebnictví",
  "companyId": "t4gtc9",
  "companyLogoUrl": null,
  "companyProfileUrl": null,
  "isAgency": false,
  "location": "Praha",
  "locations": [
    "Praha"
  ],
  "region": "Hlavní město Praha",
  "district": "Praha",
  "regions": [
    "Hlavní město Praha"
  ],
  "streetAddress": "Roškotova",
  "postalCode": "14000",
  "houseNumber": "1225/1",
  "cityPart": "Braník",
  "gpsLat": 50.04035,
  "gpsLon": 14.429325,
  "workArrangement": null,
  "remote": null,
  "salaryMin": 55000,
  "salaryMax": 75000,
  "salaryCurrency": "CZK",
  "salaryPeriod": "month",
  "salaryMonthlyMin": 55000,
  "salaryMonthlyMax": 75000,
  "salaryHourlyMin": 343.75,
  "salaryHourlyMax": 468.75,
  "salaryText": "55 000–75 000 CZK/month",
  "employmentType": [
    "fullTime"
  ],
  "contractTypes": [
    "employee"
  ],
  "education": "středoškolské nebo odborné vyučení s maturitou",
  "languagesRequired": [
    "čeština (výborná)"
  ],
  "language": "cs",
  "suitableFor": [
    "noExperience"
  ],
  "categories": [],
  "professions": [],
  "benefits": [
    "Bonusy/prémie",
    "Příspěvek na penzijní/životní připojištění",
    "Flexibilní začátek/konec pracovní doby",
    "Stravenky/příspěvek na stravování"
  ],
  "responsibilities": [
    "analýzy a modelace a s tím související vytěžování datového skladu, produkční databáze a dalších zdrojů informací",
    "spolupráce na přípravě úhradových dodatků, finančního vyrovnání, výročních zpráv, zdravotně pojistných plánů, dohadných položek"
  ],
  "requirements": [
    "vysokoškolské nebo středoškolské vzdělání",
    "velmi dobrou znalost Excelu"
  ],
  "niceToHave": [
    "praxe s datovou analýzou, práce s velkými objemy dat",
    "znalost SQL, Power BI"
  ],
  "employerOffers": [
    "zajímavou práci v oblasti datové analýzy",
    "pružnou pracovní dobu 37,5 hodin týdně"
  ],
  "postedAt": "2026-10-13T13:40:07Z",
  "postedDate": "2026-10-13",
  "postedDaysAgo": 0,
  "updatedAt": null,
  "validThrough": "2026-11-13T22:59:59Z",
  "startDate": null,
  "startImmediately": null,
  "isPromoted": false,
  "hasCareerPage": false,
  "careerPageUrl": null,
  "atmoskopUrl": null,
  "atmoskopReviewCount": null,
  "companyIco": null,
  "companyLegalForm": null,
  "companyAddress": null,
  "companyEmployeeRange": null,
  "companyNace": null,
  "companyFoundedAt": null,
  "companyInfoSource": null,
  "companyInfoMatch": null,
  "companyInfoRetrievedAt": null,
  "description": "Datový analytik zdravotního úseku\n\nCo bude Tvým úkolem?\n\n- analýzy a modelace a s tím související vytěžování datového…",
  "descriptionLength": 1663,
  "detailScraped": true,
  "changeType": null,
  "firstSeenAt": null,
  "changedFields": null,
  "lastSeenAt": null,
  "removedAt": null,
  "daysOnline": null,
  "sourceUrl": "https://beta.jobs.cz/nabidka/3807d9a5-02bf-48da-8851-29f0e9d54785/",
  "scrapedAt": "2026-10-13T17:00:00Z"
}
```

### Limitations

- **Salary:** shown only when the employer publishes it on the website. In our samples that was about 5 % of IT jobs in Prague and about a third of all jobs in Prague. Hidden salaries are never returned.
- **Jobs on employer career sites:** roughly a third to a half of the jobs are published on the employer's own career site (`hasCareerPage: true`, `<company>.jobs.cz`). For them you get the listing data (title, company, place, salary if shown, dates, categories) but no description, benefits or education; the actor doesn't read career sites. Career-site URLs given as input are skipped with a warning.
- **At most 9 990 jobs per search:** Jobs.cz shows at most 333 pages of 30 jobs for one search. Searches the actor builds without a location are split by region and abroad automatically when you want more (Max jobs and Max pages per search high enough or 0); for pasted URLs and searches with a location, split the search yourself (location, regions, categories or keywords).
- **One category per website search:** each chosen category is a separate search; jobs in several chosen categories are returned once.
- **Radius only around a town, district or part of Prague:** regions and abroad have no radius on Jobs.cz, and radius searches are not split by region above 9 990 jobs.
- **Location** must exist on the website (as in its URLs, e.g. `praha`, `brno`, `jihomoravsky-kraj`, `zahranici`); a misspelled location stops the run with a clear message.
- **Region and district** are `null` for places the actor can't place (names shared by several municipalities, "Česko", abroad). With **Regions** and a Location or Jobs.cz URLs, such jobs are skipped.
- **Company data** need an exact name match, so brand names without a legal form stay without them; agencies' jobs get the agency's data unless `isAgency` is known. See *Company data from ARES*.
- **Removed jobs** are checked only after a run that read the whole search; see *Monitoring mode explained*.
- **Descriptions** are written by employers and may mention a person's name (e.g. "contact Jana"); e-mails and phone numbers are removed, names in free text are not. **Description length limit** shortens the text if you only need a snippet. Descriptions can be protected by copyright; use them for analysis, not for republishing.
- **Employer names that look like a private person** (e.g. a self-employed courier) are returned as `null`, together with their address, GPS and profile. A name counts as a person when it has the shape of a personal name and one of its words is a common given name (Czech, Slovak, Ukrainian or Vietnamese, from Wikidata), or it has an academic title; an Atmoskop profile of a company with a legal form and the same name marks the employer as a company. In our sample of 1 258 jobs (737 employer names) from Jobs.cz and Prace.cz, no company was hidden (before this check 1.7 % of jobs, 10 company names, e.g. "Honeywell Aerospace", "Ingersoll Rand"). A brand name made of a given name (e.g. "Hugo Boss") is still hidden when there is no such Atmoskop profile for it.
- **Job URLs given as input** have a few listing-only fields empty (`workArrangement`, `updatedAt`, Atmoskop, company profile, `categories`, `professions`): the job page doesn't contain them; `language` is then detected from the description. **Categories** are therefore not checked for such jobs (they are kept, and the status message says so).
- If the website rejects a request, a rejected page is asked once more after 30 seconds from the same IP address and session (never from another IP), which recovers short-lived rejections. If it rejects a few requests in a row even so, the run stops and says so. Jobs whose page couldn't be read are returned with the listing data (`detailScraped: false`).
- This is not a copy of the Jobs.cz database: it returns the jobs matching your search and filters, up to **Max jobs**.

### For AI agents and API users

- **Minimal input:** `{"searchQueries": ["python"], "location": "praha", "maxItems": 20}`.
- One row = one job = one charge. `includeDetails: true` adds 1 request per job (except career-site jobs), same price per job. `includeCompanyInfo: true` adds the **Company info** charge only for rows with `companyInfoSource: "ARES"`.
- 30 jobs per listing page. `maxItems` counts saved jobs, `maxItemsPerStartUrl` limits each query, region, category or URL.
- Enum values: `employmentType` ⊂ `fullTime`, `partTime`, `temporary`, `internship`; `workArrangement` ∈ `onsite`, `hybrid`, `remote`; `salaryPeriod` ∈ `hour`, `day`, `week`, `month`, `year`; `seniority` ∈ `intern`, `junior`, `mid`, `senior`, `lead`, `executive`, `null`; `changeType` ∈ `new`, `updated`, `removed`, `reappeared`; `descriptionFormat` input ∈ `text`, `markdown`; `adLanguages` input ⊂ `cs`, `en`, `sk`, `de`; `regions` input ⊂ `praha`, `stredocesky-kraj`, `jihocesky-kraj`, `plzensky-kraj`, `karlovarsky-kraj`, `ustecky-kraj`, `liberecky-kraj`, `kralovehradecky-kraj`, `pardubicky-kraj`, `kraj-vysocina`, `jihomoravsky-kraj`, `olomoucky-kraj`, `zlinsky-kraj`, `moravskoslezsky-kraj`.
- `omitEmptyFields: true` drops null values and empty lists from each row (fewer tokens); `descriptionFormat: "markdown"` keeps the ad's headings and lists.
- Monitoring state is kept per `monitoringKey` between runs of the same user. The market summary is the `SUMMARY` record of the run's default key-value store.

#### Python

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_API_TOKEN>")
run = client.actor("tsutseisoftware/jobs-cz-scraper").call(run_input={
    "searchQueries": ["python"],
    "location": "praha",
    "maxItems": 100,
})
for job in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(job["title"], job["company"], job["region"], job["salaryMin"], job["url"])
summary = client.key_value_store(run["defaultKeyValueStoreId"]).get_record("SUMMARY")["value"]
print(summary["salary"]["monthlyCzk"])
```

#### JavaScript

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: '<YOUR_API_TOKEN>' });
const run = await client.actor('tsutseisoftware/jobs-cz-scraper').call({
    searchQueries: ['python'], location: 'praha', maxItems: 20,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.map((j) => `${j.title} (${j.company}, ${j.region})`));
```

You can also connect the actor to Make, Zapier, n8n, Google Sheets, Slack, e-mail or webhooks and schedule it in Apify Console.

### Troubleshooting

| Status message | What to do |
|---|---|
| `Skipping unsupported URL` | Use a Jobs.cz search URL (`https://www.jobs.cz/prace/...`) or a job URL (`https://www.jobs.cz/rpd/<id>/`, `https://beta.jobs.cz/nabidka/<id>/`). |
| `… is a Prace.cz URL` | This actor reads Jobs.cz only; use [Prace.cz Scraper](https://apify.com/tsutseisoftware/prace-cz-scraper). |
| `… a job on an employer's own career site` | Career sites (`<company>.jobs.cz`) are not read; search on Jobs.cz instead, the job is in the results with its listing data. |
| `This input makes N searches` | Use fewer locations, regions, categories or search queries (at most 120 searches per run). |
| `The search page was not found (HTTP 404)` | The location doesn't exist on the website. Use the form from the website URL, e.g. `praha`, `brno`, `jihomoravsky-kraj`. |
| `At most 20 locations per run` | Split the locations into several runs or use **Regions**. |
| `"Radius (km)" needs a Location` | Add a **Location** (e.g. `brno`) or set the radius to 0. For a Jobs.cz URL, choose "+ okolí" on www.jobs.cz and paste the URL. |
| `no radius for ... (a region or abroad)` | The location is a region or abroad on Jobs.cz; use a town or district (e.g. `brno`, `kurim`) for a radius. |
| `… start URL could not be read` | The job has expired, it is on the employer's career site, or the URL is wrong. Open it in your browser. |
| `Jobs.cz is rejecting requests` | Try again later. |
| `… listing pages could not be read, so the results may be incomplete` | The website didn't answer some pages; run it again later for complete results. |
| `stopped reading N searches after 3 listing pages in a row could not be read` | The website showed something other than job lists for that search; the rest of it was skipped. Try again later. |
| `"Categories" not checked for N job URLs` | Job pages don't list categories, so job URLs are returned without that check. Use a search with **Categories** instead. |
| `the site shows at most 9990 jobs per search` | Split the search by location, regions, categories or keywords, or leave out the Location so that the actor splits it by region. |
| `Saved 0 jobs (new or changed since last run…)` | Nothing changed since the previous monitoring run; that's expected. |
| `removed jobs were not checked because the run did not read the whole search (…)` | Set **Max jobs** to 0 (or high enough), remove **Max jobs per search**, raise **Max pages per search**, or try again if pages could not be read. With `a search read region by region did not cover all its jobs`, narrow the search (keywords, categories, regions) so that it fits into one search of 9 990 jobs. |
| `"Monitoring: include removed jobs" works only together with …` | Turn on **Monitoring: only new or changed jobs**. |
| `"Monitoring: skip reposted jobs" works only together with …` | Turn on **Monitoring: only new or changed jobs**. |
| `skipped N jobs over "Max jobs per company"` | Expected: these employers already had the maximum number of jobs in the results. Raise **Max jobs per company** or set it to 0. |
| `company info unavailable for the rest of the run: ARES rate limit` | ARES limited requests; the jobs were saved without company data and without the extra charge. Try again later. |
| `stopped adding company info at your maximum cost per run` | Raise the maximum cost per run, or lower **Max jobs**. |

### More Czech job scrapers

The same output format for the other big Czech job board (many jobs are published on both):

- [Prace.cz Scraper](https://apify.com/tsutseisoftware/prace-cz-scraper): Czech jobs with salary, region, monitoring and optional ARES company data

### FAQ

#### Is it legal to scrape Jobs.cz?

The actor reads only publicly available job listings, without logging in. It reads recruiters' contact details only if you turn on **Include recruiter contacts (personal data)** (off by default, and then you are responsible for using them lawfully, see [Recruiter contacts and privacy](#recruiter-contacts-and-privacy-optional-personal-data)), and it removes e-mails and phone numbers from descriptions; employer names that look like a private person are not returned. It makes 30 requests per minute by default, at most 120 in fast mode, and it slows down when the website asks; it never changes IP address to get around a limit and never solves captchas or other challenges. Note that the terms of use of Alma Career, the operator of Jobs.cz, prohibit automated reading and copying of the website's content and further automated processing of its data. You are responsible for making sure your use of the data complies with those terms and with the law (including data protection and database rights). If unsure, consult a lawyer.

#### Where do the company data come from, and can I rely on them?

From ARES, the public register of economic subjects of the Ministry of Finance of the Czech Republic, which publishes them as open data. They have an informative character only (not an official record) and can be up to about two weeks behind the source registers. Sole traders and other natural persons are deliberately left out.

#### How are region and district determined?

From the place of work in the job list, using the official list of Czech municipalities (ČSÚ / RÚIAN open data) built into the actor. No extra requests. See *Region and district*.

#### Do I need a proxy?

No. Jobs.cz works without a proxy, including from Apify's servers (tested from Apify without a proxy and through Apify datacenter proxy). You can still set Apify Proxy or your own proxies in **Proxy configuration** if you prefer. ARES is always contacted directly, never through a proxy.

#### Does it scrape Prace.cz too?

No, only Jobs.cz. For Prace.cz use [Prace.cz Scraper](https://apify.com/tsutseisoftware/prace-cz-scraper), with the same core data, filters and monitoring. Many jobs are published on both websites.

#### Can I get recruiters' e-mails or phone numbers?

Only what the job page shows, and only if you turn on **Include recruiter contacts (personal data)** together with **Add description and details**: the contact person's name and phone number (about half of the job pages show a phone). Jobs.cz almost never shows a recruiter's e-mail, so `recruiterEmail` is nearly always empty, and the actor never guesses or looks up e-mails. These are personal data: use them only to contact the person about that specific job, never for marketing (see [Recruiter contacts and privacy](#recruiter-contacts-and-privacy-optional-personal-data)). For company-level data use the optional company data from ARES.

#### Can it search within N km of a town?

Not directly: the Jobs.cz search the actor reads has no radius option. Use **Locations**, **Regions** or **Districts**, or turn on **Add description and details** and compute distances from `gpsLat` and `gpsLon` of each workplace.

#### Does it give the Atmoskop rating of the employer?

It gives what Jobs.cz shows: the link to the employer's Atmoskop profile (`atmoskopUrl`) and the number of reviews (`atmoskopReviewCount`). The rating itself is only on Atmoskop.cz, a different website, which the actor doesn't read.

#### Is the salary gross or net? How many positions are open? Since when?

Jobs.cz doesn't say whether an amount is gross or net, so the actor returns it as published (`salaryMin`, `salaryMax`, `salaryText`). The number of open positions is not shown on Jobs.cz. The start date (`startDate`, `startImmediately`) is filled when the description states it.

#### Can I sort the results?

Results come in the website's order, newest first (promoted jobs at the top of each page), which suits daily feeds. Jobs.cz doesn't sort by salary; sort the exported data instead, e.g. by `salaryMonthlyMax`.

#### Why are some jobs without a description?

Jobs published on an employer's own career site (`hasCareerPage: true`) are separate websites of those employers. The actor doesn't read them and returns the listing data that Jobs.cz shows for these jobs.

#### Can I use it through the API?

Yes. See the Python and JavaScript examples above, or the **API** tab.

#### Something doesn't work

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

Unofficial, not affiliated with or endorsed by Alma Career Czechia s.r.o., the operator of Jobs.cz, or by the Ministry of Finance of the Czech Republic. It is also known as a Jobs.cz API, Jobs.cz job scraper, Jobs.cz job monitor, "nabídky práce Jobs.cz", "hlídání nových nabídek práce" or "mzdy podle krajů".

# Actor input Schema

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

Search or listing URLs from Jobs.cz, e.g. https://www.jobs.cz/prace/brno/?q\[]=python (set the search and filters on the website and paste the URL; www.jobs.cz and beta.jobs.cz both work), or job URLs, e.g. https://www.jobs.cz/rpd/<id>/ or https://beta.jobs.cz/nabidka/<id>/. Website filters below are added to these URLs too, unless the URL already sets them. Prace.cz URLs are not accepted (use Prace.cz Scraper).

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

Keywords to search for, e.g. 'python', 'skladník', 'účetní'. Each query is a separate search (combined with Location and the website filters). At most 20.

## `location` (type: `string`):

One city or region as in the website URL, e.g. 'praha', 'brno', 'ostrava', 'jihomoravsky-kraj', 'stredocesky-kraj', 'zahranici' (abroad). Czech names work too ('Jihomoravský kraj'). Used for Search queries, Categories and website filters, not for Jobs.cz URLs. For several places use Locations.

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

Several cities or regions, one search each (e.g. 'praha', 'brno', 'ostrava'), at most 20. Same format as Location; both can be used together. Not used for Jobs.cz URLs.

## `radiusKm` (type: `integer`):

Also jobs up to this many km around each Location, like '+ okolí 20 km' on the website. Example: Location 'brno' with Radius 20 = 'Brno-město a okolí' (about 2 200 jobs instead of 1 800). The website offers 10, 20, 30, 40 and 50 km; any whole number up to 100 works. 0 = only the place itself (as before). Needs a Location (city, district or part of Prague); regions and 'zahranici' have no radius and are searched as they are. Costs one extra page request per location. A radius search is never split region by region, so above 9 990 jobs use a smaller radius or more filters. Jobs.cz URLs keep their own radius.

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

Only jobs in these regions (kraj). Without a Location and without Jobs.cz URLs, the actor runs one search per region on the website, so you pay and wait only for those jobs. Otherwise it checks the region of each job's place of work and skips jobs it can't place (e.g. "Česko" for the whole country).

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

Only jobs in these categories (obor), as listed on the website. Without Jobs.cz URLs the actor runs one search per category on the website (combined with Location or Regions and Search queries), so you pay only for those jobs. With Jobs.cz URLs it checks the categories of each job.

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

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

## `maxItemsPerStartUrl` (type: `integer`):

Limit for each search query or URL separately, so one large search doesn't use up the whole Max jobs. 0 = no limit.

## `maxItemsPerCompany` (type: `integer`):

At most this many jobs from one employer (by name), so a few big employers or agencies don't fill the results. Jobs without an employer name are not limited. 0 = no limit.

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

Open each job page to add the description (e-mails and phone numbers removed) and its sections (responsibilities, requirements, nice to have, what the employer offers), benefits, required education and languages, contract type, start date, workplace address with GPS and whether the advertiser is an agency. Slower: one extra request per job (2 seconds each). Jobs published on an employer's own career site keep the listing data only.

## `descriptionMaxLength` (type: `integer`):

With "Add description and details": shorten the description to about this many characters (e.g. 300 for a snippet). 0 = full text.

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

With "Add description and details": plain text, or Markdown that keeps the ad's headings, bold text and bullet lists (handy for LLMs and notes). E-mails and phone numbers are removed in both.

## `omitEmptyFields` (type: `boolean`):

Save each job without its empty fields (null values and empty lists), e.g. without the company data and monitoring fields you didn't ask for. Smaller rows and fewer tokens for AI agents and APIs. Keep it off for CSV or Excel, so that every row has the same columns.

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

Only these employment types, as in the website's filter. Several values = any of them.

## `contractTypes` (type: `array`):

Only these contract types. Several values = any of them.

## `education` (type: `array`):

Only jobs for these education levels, as in the website's filter.

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

Only jobs offering at least this monthly salary, e.g. 40000. The website also matches jobs whose employer hid the salary on the page; such jobs are returned without a salary (combine with "Only jobs with a salary" to skip them).

## `workArrangement` (type: `array`):

Only jobs with this place of work.

## `languages` (type: `array`):

Only jobs that ask for one of these languages.

## `adLanguages` (type: `array`):

Only ads written in these languages, e.g. English for jobs in English (the website's "advert language" filter).

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

Only jobs the employer marked as suitable for these groups.

## `employerType` (type: `string`):

Direct employers only (no recruitment agencies), or agencies only. Sets isAgency in the results.

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

Only jobs posted in the last N days. 1, 3 and 7 days are applied by the website; other values are checked by the actor on the posting date. 0 = any time.

## `postedWithinHours` (type: `integer`):

Only jobs posted in the last N hours, checked on the exact posting time (e.g. 6 for a run every 6 hours). The website pre-filters the matching day window (24 hours, 3 or 7 days), so older jobs cost nothing. With Posted in the last N days, the shorter window applies. 0 or empty = any time.

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

Skip jobs that don't show a salary on the website.

## `districts` (type: `array`):

Only jobs in these districts (okres), e.g. 'Brno-venkov', 'Praha-východ', 'Olomouc'. Checked by the actor on the district of each job's place of work (Prague is 'Praha'); combine it with a Location or Regions to save time.

## `titleKeywords` (type: `array`):

Keep only jobs whose title contains at least one of these words (case and diacritics don't matter).

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

Skip jobs whose title contains any of these words, e.g. 'senior', 'brigáda'.

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

Keep only jobs of companies whose name contains one of these texts, e.g. 'Škoda', 'O2'.

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

Skip jobs of companies whose name contains any of these texts.

## `includeCompanyInfo` (type: `boolean`):

Add data about the employer from ARES, the public register of economic subjects of the Czech Ministry of Finance: IČO (company ID), legal form, registered office, employee count range, main activity (CZ-NACE) and founding date. Off by default: when off, the actor never contacts ARES. Extra charge "Company info" (see the Pricing tab) only for saved jobs whose company data were found and returned; jobs without a match cost nothing extra. Matching is by the exact company name including its legal form, so some jobs stay without company data. Recruitment agencies (when known) and private persons are never looked up or returned. About 1 second per new company name.

## `includeRecruiterContacts` (type: `boolean`):

Off by default. Adds recruiterName, recruiterPhone and recruiterEmail exactly as the job page shows them in its "Kontaktní údaje" block (the site almost never shows an e-mail, so recruiterEmail is nearly always empty). Needs "Add description and details". Never for private employers or jobs on career sites; nothing is guessed or looked up elsewhere, and e-mails and phones in descriptions stay removed. These are personal data and YOU are the data controller (GDPR): use them only to contact the person about that specific job ad, inform them (Art. 14 GDPR) at the latest when you first contact them, respect objections and delete the data when no longer needed. Commercial e-mails/SMS without prior consent (Act 480/2004 Coll., § 7) and marketing calls without consent (Act 127/2005 Coll., § 96) are illegal in Czechia, also to business addresses and numbers. Don't use the contacts for marketing, don't resell them and don't build contact databases. No extra charge.

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

Return only jobs that are new or whose title, company, salary, place or type of work changed since the previous run with the same Monitoring key. Unchanged jobs are free.

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

Name of the saved state. Use a different key for each watched search (e.g. 'python-praha').

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

With "Monitoring: only new or changed jobs": also return jobs from the previous run with the same Monitoring key that are no longer in the search, as rows with changeType "removed", removedAt, firstSeenAt, daysOnline and the last known title, company, place, salary and URL. Checked only when the run reads the whole search (Max jobs, Max jobs per search, Max pages per search, unread pages or rejected requests skip the check, and the status message says why). Removed rows are normal dataset rows and are charged like any other job.

## `skipReposts` (type: `boolean`):

With monitoring: don't save a new job that has the same title, employer and place as a job seen before under another ID (a re-posted ad). Without this option such jobs are saved with isRepost: true and repostOf.

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

Reads Jobs.cz up to 4x faster: at most 120 requests per minute and 4 at a time instead of 30 per minute and 2 at a time. It stays polite: the moment the website answers "too many requests", "service unavailable" or slows down, the run switches back to the normal speed for the rest of the run, it waits when the website asks it to wait, and it never changes IP address to get around a limit. Extra charge "Fast mode" (see the Pricing tab, $0.01 per 1 000 jobs) only for jobs saved while the fast speed was on. Limits (Max jobs, Max pages) stay the same. Please don't run several fast-mode runs at the same time.

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

Safety limit for each search or URL (30 jobs per page). The website itself shows at most 333 pages (9 990 jobs) per search; when you want more from a search the actor built itself (no Location, no URLs), it reads it region by region. 0 = no limit.

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

Not needed: Jobs.cz works without a proxy, including from Apify servers. You can use Apify Proxy or your own proxies if you prefer.

## Actor input object example

```json
{
  "searchQueries": [
    "python"
  ],
  "location": "praha",
  "radiusKm": 0,
  "maxItems": 20,
  "maxItemsPerStartUrl": 0,
  "maxItemsPerCompany": 0,
  "includeDetails": false,
  "descriptionMaxLength": 0,
  "descriptionFormat": "text",
  "omitEmptyFields": false,
  "employerType": "any",
  "onlyWithSalary": false,
  "includeCompanyInfo": false,
  "includeRecruiterContacts": false,
  "onlyNewOrChanged": false,
  "monitoringKey": "default",
  "includeRemovedJobs": false,
  "skipReposts": false,
  "fastMode": false,
  "maxPages": 50,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# 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 = {
    "searchQueries": [
        "python"
    ],
    "location": "praha",
    "maxItems": 20,
    "includeCompanyInfo": false
};

// Run the Actor and wait for it to finish
const run = await client.actor("tsutseisoftware/jobs-cz-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 = {
    "searchQueries": ["python"],
    "location": "praha",
    "maxItems": 20,
    "includeCompanyInfo": False,
}

# Run the Actor and wait for it to finish
run = client.actor("tsutseisoftware/jobs-cz-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 '{
  "searchQueries": [
    "python"
  ],
  "location": "praha",
  "maxItems": 20,
  "includeCompanyInfo": false
}' |
apify call tsutseisoftware/jobs-cz-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,tsutseisoftware/jobs-cz-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/zDhcsP7nKUD1COrKN/builds/ZFe32hmVbFRvRHTrB/openapi.json
