# 4zida.rs Scraper - Serbia Property Listings (`bovi/4zida-scraper`) Actor

Scrape 4zida.rs, Serbia's #1 property portal: sale and rental listings with price, EUR per m², area, rooms, floor, heating, registration, seller type and agency. Optional full adverts add description, coordinates, year built, photos and phones.

- **URL**: https://apify.com/bovi/4zida-scraper.md
- **Developed by:** [Vitalii Bondarev](https://apify.com/bovi) (community)
- **Categories:** Real estate, Lead generation, Business
- **Features:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 4zida.rs scraper - serbia property listings

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## 4zida.rs Scraper - Serbia Property Listings

Scrape **4zida.rs**, Serbia's largest dedicated real-estate portal, into clean rows: sale and rental listings for apartments, houses, commercial premises, lots and garages anywhere in Serbia. No login, no browser, no API key. The actor reads the same public JSON that the 4zida.rs website loads in your browser, so Serbian place names and diacritics (Vračar, Niš, Čukarica) arrive intact.

### What it collects

Every listing row contains:

- **Identity**: 4zida listing ID, listing URL, address/location title, layout (e.g. *Dvosoban stan*), card headline, main photo and photo count
- **Price**: asking price or monthly rent in EUR, previous price when the advertiser cut it, price per m²
- **Property**: deal type (sale/rent), property type, area in m² (lots are converted from ares), rooms, floor and total floors, registration status (uknjiženo), heating, condition, furnishing, elevator, terrace, 3D tour
- **Market timestamps**: published date, last activated date, and when the advertiser last confirmed the ad
- **Seller**: seller type (agency, owner, company, investor), the agency's 4zida page and logo
- **Location**: full location path (neighbourhood, municipality, city) and city

Turn on **Full advert details** (or use the **Detail** operation with advert URLs) and every row also gets: the seller's full description, 4zida's structured summary, advert headline, year built, latitude/longitude, all gallery photos, amenities (air conditioning, internet, intercom, loggia, ...), agency name and e-mail, advertiser name, the phone number plus all published phone numbers, the advertiser's own ad code and the refresh date.

### Input

| Field | Description | Default |
| --- | --- | --- |
| `operation` | `search` (listings by place and filters) or `detail` (full adverts from URLs) | `search` |
| `locations` | Places in Serbian Latin or 4zida slugs: `Beograd`, `Novi Sad`, `Niš`, `novi-beograd-beograd`. Empty = all of Serbia | `["beograd"]` (prefill) |
| `dealType` | `sale` or `rent` | `sale` |
| `propertyTypes` | `apartments`, `houses`, `offices`, `lots`, `vehicle-spots` | `["apartments"]` |
| `sellerType` | `agency`, `owner`, `company`, `investor` or empty for all | all |
| `sort` | `createdAtDesc`, `priceAsc`, `priceDesc`, `m2Asc`, `m2Desc` or empty for 4zida's default | default |
| `minPrice` / `maxPrice` | Price bounds in EUR (0 = none), applied by 4zida's own filter | 0 |
| `minArea` / `maxArea` | Area bounds in m² (0 = none) | 0 |
| `searchUrls` | 4zida search addresses, e.g. `https://www.4zida.rs/izdavanje-stanova/beograd/vlasnik?jeftinije_od=600eur` | - |
| `includeDetails` | Open each advert for description, coordinates, photos, phones | `false` |
| `listingUrls` | Advert URLs for the `detail` operation | - |
| `maxResults` | Maximum rows in total for the run | `50` |
| `proxyConfiguration` | Optional proxy; 4zida.rs works without one | off |

Example input:

```json
{
  "locations": ["Novi Sad"],
  "dealType": "sale",
  "propertyTypes": ["apartments"],
  "sellerType": "owner",
  "maxPrice": 150000,
  "includeDetails": true,
  "maxResults": 100
}
```

### Example output

```json
{
  "listingId": "6a2a4140c07ac1073907fede",
  "url": "https://www.4zida.rs/prodaja-stanova/atelje-212-stari-grad-opstina-beograd/petosoban-stan/6a2a4140c07ac1073907fede",
  "title": "Palmotićeva",
  "structureLabel": "Petosoban stan",
  "dealType": "sale",
  "propertyType": "apartment",
  "price": 465000,
  "priceCurrency": "EUR",
  "pricePerM2Eur": 4305.56,
  "areaM2": 108,
  "rooms": 5,
  "floor": 3,
  "totalFloors": 4,
  "registered": "yes",
  "heatingType": "district",
  "state": "luxury",
  "sellerType": "agency",
  "agencyPage": "https://www.4zida.rs/agencije/beograd/galerija-nekretnine/196",
  "locationPath": "Atelje 212, Stari Grad opština, Beograd",
  "city": "Beograd",
  "createdAt": "2026-06-11T05:01:52+00:00",
  "yearBuilt": 2001,
  "latitude": 44.8148,
  "longitude": 20.4702,
  "agencyName": "Galerija nekretnine",
  "phone": "+381652335260"
}
```

Values such as `registered`, `heatingType`, `state` and `furnished` are 4zida's own codes (`yes`, `in_progress`, `district`, `underfloor`, `luxury`, `needs_repair`, ...). Fields the advert does not publish are `null`.

### Price per result

You pay only for rows delivered to the dataset: **$2.00 per 1,000 listings ($0.002 per result)**, with or without full advert details. There is no start fee and no charge for failed or filtered-out listings.

### Tips

- One search page returns 20 listings; a 1,000-row sweep of Belgrade takes about 50 requests.
- Several places and several property types are searched one after another; duplicates are removed across the run.
- For micro-locations (a blok, a street), build the search on 4zida.rs and paste the address into `searchUrls`.

# Actor input Schema

## `operation` (type: `string`):

Search walks 4zida.rs listing results for the places and filters below (20 listings per page). Detail takes 4zida advert URLs from 'Listing URLs' and returns each full advert.

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

One place per line: a city, town or neighbourhood in Serbian Latin (Beograd, Novi Sad, Niš, Kragujevac) or its 4zida URL slug (beograd, novi-beograd-beograd). Each place is resolved through 4zida's own place index. Leave empty to search all of Serbia.

## `dealType` (type: `string`):

For-sale listings (prodaja) or rentals (izdavanje). Rental prices are monthly rent in EUR, as published.

## `propertyTypes` (type: `array`):

Which property types to search. Empty means apartments. Each place is searched once per selected type.

## `sellerType` (type: `string`):

Keep only listings from one kind of advertiser, using 4zida's own seller filter. Empty means all sellers.

## `sort` (type: `string`):

Result order. Empty keeps 4zida's default order (promoted listings first).

## `minPrice` (type: `integer`):

Lower price bound in EUR (monthly rent for rentals). 0 means no bound. Sent to 4zida's own price filter.

## `maxPrice` (type: `integer`):

Upper price bound in EUR. 0 means no bound.

## `minArea` (type: `integer`):

Minimum area in square metres. 0 means no bound. For lots it is converted to ares, the unit 4zida filters lots by.

## `maxArea` (type: `integer`):

Maximum area in square metres. 0 means no bound.

## `searchUrls` (type: `array`):

Optional: paste 4zida.rs search addresses instead of (or as well as) the form, e.g. https://www.4zida.rs/izdavanje-stanova/beograd/vlasnik?jeftinije_od=600eur. Deal, property type, place, seller type, room structure and price/area bounds are read from the URL.

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

Also open each listing's advert and add the seller's description, coordinates, year built, all photos, amenities, the agency name, e-mail and phone numbers. One extra request per listing.

## `listingUrls` (type: `array`):

4zida advert URLs for the Detail operation, e.g. https://www.4zida.rs/prodaja-stanova/.../6a2a4140c07ac1073907fede.

## `maxResults` (type: `integer`):

Stop after this many listings in total across all places and property types.

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

Optional proxy. 4zida.rs answers without one; turn on Apify Proxy if you run very large jobs.

## Actor input object example

```json
{
  "operation": "search",
  "locations": [
    "beograd"
  ],
  "dealType": "sale",
  "propertyTypes": [
    "apartments"
  ],
  "sellerType": "",
  "sort": "",
  "minPrice": 0,
  "maxPrice": 0,
  "minArea": 0,
  "maxArea": 0,
  "includeDetails": false,
  "maxResults": 50,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

## `results` (type: `string`):

Dataset with every scraped 4zida.rs listing.

# 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 = {
    "operation": "search",
    "locations": [
        "beograd"
    ],
    "dealType": "sale",
    "propertyTypes": [
        "apartments"
    ],
    "sellerType": "",
    "sort": "",
    "maxResults": 50,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("bovi/4zida-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 = {
    "operation": "search",
    "locations": ["beograd"],
    "dealType": "sale",
    "propertyTypes": ["apartments"],
    "sellerType": "",
    "sort": "",
    "maxResults": 50,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("bovi/4zida-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 '{
  "operation": "search",
  "locations": [
    "beograd"
  ],
  "dealType": "sale",
  "propertyTypes": [
    "apartments"
  ],
  "sellerType": "",
  "sort": "",
  "maxResults": 50,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call bovi/4zida-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,bovi/4zida-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/FYYEDhpbkgJUVrdlR/builds/a2ubojc6oFyDasd6L/openapi.json
