# Real Estate Price Index (Roll-up) (`zinin/re-price-index`) Actor

Build one live asking-price index per city from eight in-process real-estate source adapters. Returns median/mean price and price per m² with honest sample, partial, and error metadata.

- **URL**: https://apify.com/zinin/re-price-index.md
- **Developed by:** [Tim Zinin](https://apify.com/zinin) (community)
- **Categories:** Real estate
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $8.50 / 1,000 city price index rows

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/platform/actors/running/actors-in-store#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

## Real Estate Price Index

Build one asking-price snapshot per city from live real-estate listings. The Actor fetches each source directly through a bounded in-process adapter, then returns sample size, median price, mean price, and median price per square meter in the city's native currency.

### Supported cities

| City key | Source | Currency | Default market slice |
|---|---|---:|---|
| `almaty` | Krisha.kz | KZT | apartments for sale |
| `tbilisi` | SS.ge | GEL | all property/deal types |
| `vienna` | Willhaben | EUR | apartments for sale |
| `london` | Rightmove | GBP | properties for sale |
| `istanbul` | Emlakjet | TRY | apartments for sale |
| `lisbon` | Imovirtual | EUR | apartments for sale |
| `dubai` | Property Finder | AED | properties for sale |
| `podgorica` | Realitica | EUR | apartments for sale |

`baku` is not currently supported. Tap.az requires an Azerbaijan residential proxy plus strict redirect-target enforcement. The earlier `baku` key depended on a separate run and has been retired until an in-process proxy-aware adapter passes that security and reliability acceptance. Requests that still contain `baku` receive a free `found:false, reason:"unknown_city"` row; migrate by removing that key for now.

### What you get

- One paid price-index row per city only when at least five native-currency prices come from a complete source scan.
- Median and mean only when at least five priced listings are available. Smaller samples remain visible, but statistics stay `null` instead of presenting noise as a market signal.
- A separate price-per-m² sample, because some priced listings do not publish area.
- Free error/empty rows with explicit reasons; source failures are never sold as valid data.
- A free summary row listing delivered, missing, and partial cities.
- Bounded parallelism: at most five city sources are fetched at once.

### Pricing

Pay per event starts at **$0.005 per run + $0.01 per delivered city index**. The six account tiers use exact start/result pairs: FREE `.005/.010`, BRONZE `.00475/.0095`, SILVER `.0045/.009`, GOLD `.00425/.0085`, PLATINUM `.0041/.0082`, DIAMOND `.004/.008`. Runtime accepts only one complete pair and no extra event. Unknown cities, source errors, partial source scans, samples below five native-currency prices, and the summary row are not charged. Partial scans are returned as explicit free evidence and never sold as an index. A paid row is pushed and charged atomically; if the remaining-budget read fails, paid delivery stops and the run fails honestly.

### Input

| Field | Required | Description |
|---|---|---|
| `cities` | no | City keys from the supported table. Default: `almaty`, `tbilisi`. Duplicate/case variants are normalized and deduplicated. |
| `max_items_per_city` | no | Maximum listing rows per source sample. Default 30, range 1–200. |
| `max_pages_per_city` | no | Maximum pages per source. Default 2, range 1–10. |
| `max_concurrency` | no | City sources fetched simultaneously. Default 2, range 1–5. |

```json
{
    "cities": ["almaty", "tbilisi", "vienna"],
    "max_items_per_city": 30,
    "max_pages_per_city": 2,
    "max_concurrency": 2
}
```

### Output

A successful city row keeps the established price-index fields:

```json
{
    "city": "almaty",
    "found": true,
    "source_portal": "krisha-kz",
    "currency": "KZT",
    "deal_type": "sale",
    "property_type": "apartment",
    "sample_size": 10,
    "median_price": 41500000,
    "mean_price": 43750000,
    "sample_size_price_per_sqm": 8,
    "median_price_per_sqm": 630000,
    "partial": false,
    "partial_reason": null,
    "currency_filtered_rows": 0,
    "currency_missing_rows": 0,
    "note": null,
    "scan_duration_ms": 842,
    "child_run_duration_ms": 842,
    "scraped_at": "2026-08-08T00:00:00.000Z"
}
```

`child_run_duration_ms` remains only as a backward-compatible field name for existing consumers; its value now equals `scan_duration_ms`, the in-process source scan duration.

A listing is aggregated only when it declares the city's exact native `currency`. Explicit mismatches and missing currency are excluded rather than guessed; `currency_filtered_rows` and `currency_missing_rows` report those removals. At least five remaining native-currency prices and a complete source scan are required for a paid index; a smaller sample returns a free `insufficient_sample` row, while a sufficiently large but partial scan returns a free `source_partial` evidence row.

An unavailable source is explicit and free:

```json
{
    "city": "almaty",
    "found": false,
    "reason": "source_failed",
    "error": "source-specific failure",
    "source_portal": "krisha-kz",
    "scraped_at": "2026-08-08T00:00:00.000Z"
}
```

Every run ends with a summary row containing `requested_cities`, `succeeded_cities`, `missing_cities`, `partial_cities`, `partial`, and `supported_cities`.

### Limitations

- These are asking prices from current listings, not completed-sale valuations or appraisals.
- Currencies are not converted. Compare cities only after applying an exchange-rate policy appropriate to your use case.
- A source can rate-limit, change markup, or return only a partial page set. Such cases are marked `partial` or `source_failed` rather than silently accepted.

Built by [zinin](https://apify.com/zinin).

# Actor input Schema

## `cities` (type: `array`):

Which cities to price: almaty, tbilisi, vienna, london, istanbul, lisbon, dubai, or podgorica. Unknown and retired keys (including baku) produce a free unknown\_city row instead of failing the run.

## `max_items_per_city` (type: `integer`):

Maximum listing rows each in-process source adapter may use for that city's price sample.

## `max_pages_per_city` (type: `integer`):

Maximum result pages each source adapter may walk before stopping.

## `max_concurrency` (type: `integer`):

How many city sources may be fetched at once. Runtime clamps this to 1–5 to keep source load bounded.

## Actor input object example

```json
{
  "cities": [
    "almaty",
    "tbilisi"
  ],
  "max_items_per_city": 10,
  "max_pages_per_city": 1,
  "max_concurrency": 2
}
```

# Actor output Schema

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

API URL for the default dataset items produced by this run.

# 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 = {
    "cities": [
        "almaty",
        "tbilisi"
    ],
    "max_items_per_city": 10,
    "max_pages_per_city": 1,
    "max_concurrency": 2
};

// Run the Actor and wait for it to finish
const run = await client.actor("zinin/re-price-index").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 = {
    "cities": [
        "almaty",
        "tbilisi",
    ],
    "max_items_per_city": 10,
    "max_pages_per_city": 1,
    "max_concurrency": 2,
}

# Run the Actor and wait for it to finish
run = client.actor("zinin/re-price-index").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 '{
  "cities": [
    "almaty",
    "tbilisi"
  ],
  "max_items_per_city": 10,
  "max_pages_per_city": 1,
  "max_concurrency": 2
}' |
apify call zinin/re-price-index --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,zinin/re-price-index"
        }
    }
}

```

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/2k0lpY3fhrblAqCs0/builds/fnqaxC4zVnvbPqqtc/openapi.json
