# Bazos.cz Classifieds Scraper (`scrapyx/bazos-classifieds-scraper`) Actor

Czech classifieds from Bazos.cz across all 20 categories: title, price in CZK, city, postcode, view count, seller identity and the paid TOP badge. Reports how much of the catalogue is actually reachable instead of implying a full crawl.

- **URL**: https://apify.com/scrapyx/bazos-classifieds-scraper.md
- **Developed by:** [Ibnu Adzim](https://apify.com/scrapyx) (community)
- **Categories:** E-commerce
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.05 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

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

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

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

# README

## Bazos.cz Classifieds Scraper (Czech Republic)

Listings from **Bazos.cz**, the dominant Czech classifieds site, across all 20
categories — cars, property, animals, electronics, jobs, tickets and the rest —
as structured rows: title, price in CZK, city, postcode, **view count**,
**seller identity** and the paid **TOP** badge.

***

### Two things this Actor tells you that the site does not

#### 1. The result count is not the reachable count

`auto.bazos.cz` prints *"Zobrazeno 1-20 inzerátů z 410 334"*. It will hand over
**20 000**, and then answer `404`:

```
/19980/  ->  200, 20 listings      <- last reachable page
/20000/  ->  404
```

A round 20 000 is a cap, not the end of the data. It was pinned by binary
search, and checked to be **global rather than per-category**: `prace` holds
6 402 listings and ends honestly at its own size (`/6400/` → 2 listings,
`/6420/` → 404).

What 20 000 rows actually buys you is narrower still. The feed is
date-descending, and the far end of the window is **yesterday**:

```
offset 0      ->  6.9. 2026
offset 19980  ->  5.9. 2026
```

So this is a **newest-first feed covering roughly two days**, not a catalogue
dump. Every summary row carries `upstreamTotal` (what the site claims),
`reachableCeiling` and `reachableCeilingHit`, so a run never implies a
completeness it did not achieve.

#### 2. "TOP" is paid advertising, and it saturates the top of the feed

```html
<span title="TOP 21x Platí do 17.10. 2026" class="ztop">TOP</span>
```

Bazos sells *topování*, which re-dates an ad to now. Measured on `auto`:

| Offset | TOP rows |
| --- | --- |
| 0 – 9 000 | **20 / 20** |
| 12 500 | 2 / 20 |
| 19 980 | 1 / 20 |

**A shallow scrape of this site returns nothing but advertising.** That is not
a defect to hide — it is the most commercially interesting thing on the page,
so each row carries `isPromoted`, `topPurchases` (how many bumps were bought)
and `topExpiresOn`. Filter with `promotedOnly` to measure dealer ad spend, or
`skipPromoted` to reach organic stock — the latter needs depth to return much.

Because a bump rewrites the date, the date field is named **`bumpedDate`**
rather than `postedDate`. Calling it a posting date would be a quiet lie on
the majority of early rows.

***

### What you get

One `SEARCH_SUMMARY` row per category, then one `LISTING` row per ad.

| Group | Fields |
| --- | --- |
| Identity | `listingId`, `listingUrl`, `title`, `description` |
| Price | `price`, `priceKind`, `priceRaw`, `currency` |
| Location | `city`, `postcode` |
| Engagement | `viewCount` |
| Promotion | `isPromoted`, `topPurchases`, `topExpiresOn`, `bumpedDate` |
| Seller | `sellerId`, `sellerRef`, `sellerName` |
| Media | `imageUrl` |
| Provenance | `_input`, `_source`, `_scrapedAt`, `pageFound`, `offsetFound`, `category`, `subcategory` |

**`viewCount` is unusual.** Most classifieds keep view counts private; Bazos
prints them on the results page. Combined with `bumpedDate` it gives a direct
read on how fast stock moves.

**Seller identity comes free.** Each row's `sellerId` is stable across that
seller's ads — measured on one page: 20 ads, 9 distinct sellers, one dealer
appearing three times. Dealer-versus-private analysis needs no detail fetch.

#### Price is a word more often than a number

Sampled across 140 rows: 96 numeric, and then `V textu` (17), `Dohodou` (16),
`Nabídněte` (10), `Zdarma` (1). Coercing those to `0` would invent 44 free
cars, so `price` stays **null** and `priceKind` says why — `amount`, `inText`,
`negotiable`, `makeOffer` or `free`. Only `Zdarma` yields `0`.

***

### Example input

```json
{
  "categories": [
    "auto",
    "https://reality.bazos.cz/",
    "https://auto.bazos.cz/audi/"
  ],
  "maxPages": 10
}
```

A category can be a plain name (`auto`), a host (`auto.bazos.cz`) or a pasted
category **or subcategory** URL.

Categories: `auto`, `reality`, `zvirata`, `elektro`, `pc`, `mobil`, `sport`,
`deti`, `dum`, `foto`, `hudba`, `knihy`, `motorky`, `nabytek`, `obleceni`,
`ostatni`, `prace`, `sluzby`, `stroje`, `vstupenky`.

***

### Limits and behaviours

**A URL with a query string is refused before any request.** Every Bazos
subdomain's robots.txt disallows the query search surface (`hledat=`,
`order=`, `cenaod=`, `hlokalita=` …), so this Actor navigates by path only and
says so rather than quietly stripping your filter.

**The run stops when a category runs out.** Bazos answers an honest `404`
past the last page rather than re-serving it, so no ceiling has to be guessed
per category. `stoppedReason` distinguishes `exhausted` from
`reachableCeiling`, `maxItems` and `pageBudget`.

**`maxPages` above 1 000 is refused** rather than silently truncated — beyond
that the site returns nothing.

**Every block must yield a listing id.** If a layout change ever produced a
row this parser could not identify, the run stops instead of dropping it
silently.

***

### How it works

HTTP-only. No browser, no login, no API key.

robots.txt is defined **per origin**, and Bazos serves 21 of them — one per
category subdomain. All 21 were fetched and checked individually rather than
assuming the apex file speaks for the subdomains; they are not identical
files. Each allows the listing paths, disallows the query search surface and
the write endpoints, names no AI crawler and sets no crawl delay.

The pages are plain server-rendered HTML in UTF-8 with no JSON-LD, no
`__NEXT_DATA__` and no framework payload — the HTML *is* the data layer here.
Each result is a `<div class="inzeraty inzeratyflex">` whose fields live in
sibling elements, so blocks are cut at the next block rather than at a closing
tag.

Pagination is a **path offset** (`/40/`), verified by set overlap rather than
by comparing first rows: `/20/`, `/40/` and `/2000/` each share zero ids with
page 1.

One detail worth stating because it looks like a trap and is not: each page
carries 20 listing blocks but **21** price cells. The extra one is the
`Cena`/`Lokalita` table header, not a hidden promoted row. Block-based
extraction excludes it by construction, and the Actor asserts that this is
still the reason — so if a real 21st row ever appears, the run fails loudly
instead of quietly emitting a header as a listing.

# Actor input Schema

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

Bazos categories to scrape. Use a plain name (auto, reality, zvirata...), a subdomain host (auto.bazos.cz), or paste a category or SUBCATEGORY URL from the site (https://auto.bazos.cz/audi/). URLs with a query string are refused: every Bazos robots.txt disallows the hledat= / order= / cenaod= search surface, so this Actor navigates by path only.

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

Pages of 20 listings to fetch per category. Bazos serves at most 20000 rows (1000 pages) per category before answering 404, however many it claims to hold.

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

Stop after this many listing rows in total. Leave empty for no limit.

## `promotedOnly` (type: `boolean`):

Keep only ads with a paid TOP bump. Useful for measuring dealer advertising spend.

## `skipPromoted` (type: `boolean`):

Drop ads with a paid TOP bump. Note the first ~9000 rows of a busy category are ALL promoted, so this may return very little unless you also raise Max pages.

## `maxConcurrency` (type: `integer`):

Parallel requests. Kept low by default; Bazos is a small site.

## `minRequestInterval` (type: `number`):

Politeness floor between request starts.

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

Apify Proxy settings. Residential is recommended for large runs.

## Actor input object example

```json
{
  "categories": [
    "auto"
  ],
  "maxPages": 5,
  "promotedOnly": false,
  "skipPromoted": false,
  "maxConcurrency": 2,
  "minRequestInterval": 1,
  "proxy": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `items` (type: `string`):

One row per scraped record. See the dataset's default view for field definitions.

# 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 = {
    "categories": [
        "auto"
    ],
    "maxPages": 5
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapyx/bazos-classifieds-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 = {
    "categories": ["auto"],
    "maxPages": 5,
}

# Run the Actor and wait for it to finish
run = client.actor("scrapyx/bazos-classifieds-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 '{
  "categories": [
    "auto"
  ],
  "maxPages": 5
}' |
apify call scrapyx/bazos-classifieds-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,scrapyx/bazos-classifieds-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/hwR4WzWwdZGsfLWf6/builds/HueTCARciamVetbnB/openapi.json
