# FincaRaíz, Metrocuadrado & Ciencuadras Scraper (`radar-scraper/colombia-real-estate-aggregator`) Actor

Scrape FincaRaíz, Metrocuadrado and Ciencuadras in one run: Colombian rentals and sales by city with price, admin fee, price/m², area, stratum and photos. Cross-portal duplicates merged, below-market signal, and incremental mode for new listings and price drops. $2.50 per 1,000.

- **URL**: https://apify.com/radar-scraper/colombia-real-estate-aggregator.md
- **Developed by:** [Juan Felipe Henao López](https://apify.com/radar-scraper) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.50 / 1,000 property scrapeds

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

## FincaRaíz, Metrocuadrado & Ciencuadras Scraper

Extrae inmuebles en **arriendo y venta en Colombia** de los tres portales principales (**FincaRaíz**, **Metrocuadrado** y **Ciencuadras**) en un solo run. Cada inmueble trae precio, administración, costo mensual total, área, **precio por m²**, estrato, habitaciones, baños, parqueaderos, ubicación y fotos. Además, los anuncios repetidos entre portales se unen en un solo registro con el precio de cada portal.

**Modo incremental:** recibe solo los inmuebles nuevos y las **bajadas de precio** desde tu último run. **Precio: $2,50 USD por cada 1.000 inmuebles.**

*English version below.*

***

### ¿Para quién es?

- **Inversionistas y compradores** que buscan oportunidades por precio/m² en una ciudad.
- **Inmobiliarias y brokers** que siguen la oferta y los precios de su zona.
- **Analistas y proptech** que necesitan datos de mercado de varias fuentes en un solo formato.
- **Quienes buscan arriendo** y quieren comparar el mismo inmueble en varios portales.

### Qué puedes hacer

1. **Un feed diario de inmuebles nuevos y bajadas de precio.** Programa el Actor con `incrementalMode: true` (por ejemplo, apartamentos en arriendo en Medellín) y envía los resultados a Google Sheets, Slack, tu CRM o WhatsApp con **n8n**, **Make** o la API de Apify.
2. **Detectar inmuebles por debajo del mercado.** Filtra `isBelowMarketDeal = true` u ordena por `vsZoneMedianPct` (lo explicamos abajo).
3. **Comparar precios entre portales.** Los anuncios publicados en más de un portal traen `portalPrices`, `cheapestPortal` y `priceSpreadAcrossPortalsCOP`.
4. **Estudios de mercado.** Precio/m² y administración por ciudad, tipo de inmueble y estrato.

### Reparto real por portal

Runs de 500 inmuebles en la nube (2026-10-19). Cada portal aporta por turnos, así que un run grande no se queda en un solo portal:

| Búsqueda | FincaRaíz | Metrocuadrado | Ciencuadras | En 2 o más portales |
|---|---|---|---|---|
| Bogotá · arriendo · apartamento | 242 (48%) | 239 (48%) | 19 (4%) | 32 |
| Medellín · venta · apartamento | 246 (49%) | 237 (47%) | 17 (3%) |  |
| Cali · arriendo · casa | 236 (47%) | 245 (49%) | 19 (4%) | 76 |

Ciencuadras solo aporta unos 20 inmuebles por búsqueda (ver Limitaciones). En los runs pequeños el reparto es parejo: en el prefill de 40 inmuebles fue 14 / 13 / 13.

### Entrada

| Campo | Tipo | Por defecto | Descripción |
|---|---|---|---|
| `city` | lista | `bogota` | Bogotá, Medellín, Cali, Barranquilla, Cartagena, Bucaramanga, Pereira, Santa Marta, Manizales, Villavicencio, Ibagué o Cúcuta (verificadas en los 3 portales). |
| `operationType` | lista | `RENT` | `RENT` (arriendo) o `SALE` (venta). |
| `propertyType` | lista | `APARTMENT` | `APARTMENT`, `HOUSE` (casa) o `COMMERCIAL` (local). |
| `maxItems` | entero | `40` | Máximo de inmuebles que se entregan y se cobran (hasta 500; unos 4 minutos para 500). |
| `portals` | lista | los 3 | Portales a consultar. |
| `deduplicateAcrossPortals` | booleano | `true` | Une en un solo registro el mismo inmueble publicado en varios portales. |
| `includeContactInfo` | booleano | `false` | Contacto del anunciante, **solo de inmobiliarias y constructoras** (ver Privacidad). |
| `incrementalMode` | booleano | `false` | Solo inmuebles nuevos y bajadas de precio desde el run anterior de la misma búsqueda. |
| `stateKey` | texto | — | Nombre opcional del historial incremental. |
| `resetIncrementalState` | booleano | `false` | Borra el historial incremental y empieza de cero. |

### Salida

Un registro por inmueble. Ejemplo real de una inmobiliaria (run `lS6sE2niX1fZnRHdg` del 2026-10-19). En este README abreviamos la dirección y las fotos; el Actor entrega la dirección tal como la publica el portal:

```json
{
  "id": "fincaraiz-194321116",
  "portal": "FINCARAIZ",
  "portalId": "194321116",
  "title": "Apartamento en Arriendo en Las nieves, Bogotá",
  "operationType": "RENT",
  "propertyType": "APARTMENT",
  "city": "Bogotá",
  "neighborhood": "Las nieves",
  "address": "CALLE 23 # 5 - 70",
  "coordinates": {
    "latitude": 4.608446934544,
    "longitude": -74.069255373473
  },
  "priceCOP": 1620000,
  "adminFeeCOP": 480000,
  "totalMonthlyCostCOP": 2100000,
  "areaM2": 40,
  "pricePerM2COP": 40500,
  "rooms": 1,
  "bathrooms": 1,
  "garages": 1,
  "stratum": 4,
  "images": [
    "https://www-img-cc.s3.amazonaws.com/inmuebles/images/3916671/adfda_179243497929_plana.png"
  ],
  "url": "https://www.fincaraiz.com.co/apartamento-en-arriendo-en-las-nieves-bogota/194321116",
  "advertiserType": "AGENCY",
  "listedOnPortals": [
    "CIENCUADRAS",
    "FINCARAIZ"
  ],
  "portalUrls": {
    "CIENCUADRAS": "https://www.ciencuadras.com/inmueble/apartamento-en-arriendo-en-las-nieves-bogota-3916671",
    "FINCARAIZ": "https://www.fincaraiz.com.co/apartamento-en-arriendo-en-las-nieves-bogota/194321116"
  },
  "portalPrices": {
    "CIENCUADRAS": 1850000,
    "FINCARAIZ": 1620000
  },
  "cheapestPortal": "FINCARAIZ",
  "priceSpreadAcrossPortalsCOP": 230000,
  "scrapedAt": "2026-10-19T22:26:28.164Z",
  "zoneMedianPricePerM2COP": 50385,
  "vsZoneMedianPct": -19.6,
  "isBelowMarketDeal": false,
  "comparablesCount": 140,
  "comparisonGroup": "Bogotá · RENT · APARTMENT · estrato 4"
}
```

#### Cómo se calculan los campos

- **Fidelidad del filtro:** cada inmueble se compara con lo que **el propio portal** dice de su ciudad, operación y tipo. Si no coincide con tu búsqueda, se descarta y no se cobra. Los anuncios "Arriendo o venta" valen para las dos operaciones.
- `adminFeeCOP`: la administración que publica el portal. Es `null` cuando el portal no la publica y `0` solo cuando el portal indica que está **incluida** en el arriendo (FincaRaíz lo marca; en 84 anuncios de FincaRaíz, el 29% traía importe, el 46% la tenía incluida y el 25% no la publicaba).
- `totalMonthlyCostCOP` = arriendo + administración, solo en **arriendos** con administración conocida; si no, `null`. `pricePerM2COP` = precio ÷ área.
- **Duplicados entre portales:** se consideran el mismo inmueble si coinciden la operación, el tipo, las habitaciones y el área (±2 m²), están a menos de 250 m (o en el mismo barrio) y sus precios no difieren más de un 25%. El registro conserva el portal más barato y añade `listedOnPortals`, `portalUrls`, `portalPrices`, `cheapestPortal` y `priceSpreadAcrossPortalsCOP`. Es una heurística: puede unir dos unidades casi idénticas del mismo edificio.
- `zoneMedianPricePerM2COP`, `vsZoneMedianPct` e `isBelowMarketDeal` comparan el precio/m² con la **mediana de los comparables del mismo run**. Ojo: aquí la **"zona" no es el barrio**. El grupo de comparación es **ciudad + operación + tipo**, más el **estrato** cuando hay al menos 10 de ese estrato en el run. `comparisonGroup` lo dice en claro (por ejemplo, `"Bogotá · RENT · APARTMENT · estrato 6"`) y `comparablesCount` dice cuántos se usaron.
  - Por eso son normales las diferencias positivas grandes (+100% o más) en barrios premium: se comparan con la mediana de toda la ciudad (o del estrato), no con la de su barrio.
  - `isBelowMarketDeal = true` cuando el precio/m² está **al menos un 35% por debajo** de esa mediana.
  - Con **menos de 10 comparables**, esos tres campos y `comparisonGroup` son `null`.
  - Con un **área implausible para el tipo** (apartamento fuera de 20–600 m², casa fuera de 40–1.500 m², local fuera de 6–2.000 m²), el inmueble se entrega igual, pero `isBelowMarketDeal` y `vsZoneMedianPct` son `null` y no cuenta para la mediana. Suele ser el área del lote de una casa campestre o un error de digitación, que haría parecer una ganga el precio/m².
  - En nuestros runs de 500 marcó entre el 8% y el 18% de los inmuebles, según la búsqueda.
  - **Es una señal orientativa, no un avalúo:** un precio muy bajo también puede ser un error del anuncio, un área mal digitada o un inmueble para remodelar.
- `advertiserType`: `AGENCY` (inmobiliaria), `BUILDER` (constructora), `INDIVIDUAL` (particular) o `UNKNOWN`, según el indicador del propio portal.

### Modo incremental

Con `incrementalMode: true`, cada run entrega solo lo que los runs anteriores de la **misma búsqueda** no entregaron:

- Los inmuebles nuevos llegan con `changeType: "NEW"`.
- Las **bajadas de precio** se vuelven a entregar (y se cobran) con `changeType: "PRICE_DROP"` y `previousPriceCOP`.
- Lo que ya recibiste **no se entrega ni se cobra**.
- El historial se guarda en un key-value store con nombre (`incremental-…`) **en tu cuenta de Apify** y se conserva entre runs. Si cambias la búsqueda, empieza un historial nuevo; si cambias `maxItems` o el proxy, no.

### Privacidad

- Los datos de contacto vienen **apagados por defecto**. Con `includeContactInfo: true`, se entregan el nombre, el teléfono y el WhatsApp **solo de inmobiliarias y constructoras**. Los anuncios de particulares **nunca** traen datos de contacto.
- En los anuncios de particulares y de anunciantes sin identificar, las **coordenadas se redondean a unos 110 m** (3 decimales). Solo las de inmobiliarias y constructoras van exactas.
- La **dirección** (`address`) solo se entrega en anuncios de inmobiliarias y constructoras. En los anuncios de particulares falta **a propósito**.
- Los títulos se limpian de teléfonos y correos.

### Precio

**$2,50 USD por cada 1.000 inmuebles** ($0,0025 por inmueble), más la pequeña tarifa estándar de Apify por cada run. Solo pagas los inmuebles entregados en tu dataset: los duplicados, los que no coinciden con tu búsqueda y, en modo incremental, los que ya recibiste **no se cobran**.

| Ejemplo | Costo |
|---|---|
| 100 inmuebles | ≈ $0,25 |
| 500 inmuebles | ≈ $1,25 |
| Feed diario con ~50 novedades | ≈ $0,13 al día |

Puedes poner un tope de gasto por run con el cargo máximo de Apify.

### Limitaciones (léelas)

- **Máximo por búsqueda y portal:**
  - **Ciencuadras:** unos **20 inmuebles por búsqueda**, porque su sitio solo publica la primera página sin JavaScript.
  - **FincaRaíz:** hasta unas 20 páginas (~400 inmuebles) por run.
  - **Metrocuadrado:** pagina hasta ~10.000 por búsqueda (verificado hasta la posición 9.800).
  - `maxItems` admite hasta 500 por run.
- La **mediana** y la señal `isBelowMarketDeal` se calculan sobre los inmuebles del run, no sobre todo el mercado. Con búsquedas pequeñas hay menos comparables (y más `null`).
- La **deduplicación** es heurística (ver arriba) y solo une anuncios **de portales distintos**. Dentro de un mismo portal, el mismo inmueble puede aparecer dos veces cuando lo publican dos inmobiliarias: no los unimos porque son anuncios reales, con su propio precio y contacto.
- Algunos datos dependen de lo que publica el anunciante: la administración falta en parte de los anuncios (`null`) y el estrato no siempre está.
- **Direcciones:** se entregan tal como las escribe el anunciante en el portal. En Ciencuadras a menudo son parciales (por ejemplo, solo "Calle 92"); no las recortamos.
- Si un portal falla o bloquea, el run sigue con los demás e informa cuál falló. Si fallan todos, el run termina en **FAILED** con un mensaje claro, no en un "éxito" vacío.

### Úsalo desde código, n8n o Make

```bash
curl -X POST "https://api.apify.com/v2/acts/radar-scraper~colombia-real-estate-aggregator/run-sync-get-dataset-items?token=<TU_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"city": "medellin", "operationType": "RENT", "propertyType": "APARTMENT", "maxItems": 200, "incrementalMode": true}'
```

En **n8n** o **Make**, usa el módulo "Run Actor" de Apify con el mismo input y lleva los ítems del dataset a tu hoja, CRM o canal de mensajería.

### Preguntas frecuentes

**¿Inicia sesión en los portales?** No. Solo lee lo que ve cualquier visitante anónimo.
**¿Qué tan frescos son los datos?** Se leen en vivo en cada run.
**¿Puedo buscar por barrio o por rango de precio?** Todavía no: la búsqueda es por ciudad, operación y tipo. Puedes filtrar el dataset después. Si lo necesitas, abre un issue.

### Opiniones y soporte

Si este Actor te ahorra tiempo, una reseña corta en su página de la Store ayuda a que otros lo encuentren; queremos tu opinión honesta, buena o mala. ¿Algo no funciona? Abre un issue en la página del Actor y te respondemos en menos de 48 horas.

### ⚖️ Aviso legal y de datos

Este Actor extrae información pública, visible para cualquier visitante anónimo de los sitios de origen. No inicia sesión, no evade muros de pago y no accede a áreas privadas. Los datos personales de particulares están excluidos: los campos opcionales de contacto vienen desactivados y, aun activándolos, solo se entregan los de inmobiliarias y constructoras. Eres responsable de que el uso que hagas de los datos cumpla la ley aplicable (incluida la Ley 1581 de 2012 de Colombia) y los términos de los sitios de origen. Si tienes dudas, consulta con un abogado.

***

### 🇬🇧 English

**Scrape Colombian real estate from FincaRaíz, Metrocuadrado and Ciencuadras in one run.** Rentals and sales by city (12 main cities), with price, administration fee, total monthly cost, area, **price per m²**, stratum, rooms, bathrooms, parking, location and photos. Listings published on several portals are merged into one record with each portal's price.

- **Real portal mix:** in 500-listing runs, about 48% FincaRaíz, 48% Metrocuadrado and 4% Ciencuadras (Ciencuadras only exposes about 20 listings per search).
- **Filter fidelity:** every listing is checked against the portal's own city, operation and type. Mismatches are dropped and not charged.
- **Below-market signal:** `isBelowMarketDeal` is `true` when the price per m² is at least **35% below the median** of at least 10 comparable listings in the same run. The comparison group is **city + operation + type (+ stratum when there are 10 or more)**, not the neighbourhood; `comparisonGroup` names it. Large positive differences (+100%) are normal in premium neighbourhoods. It flagged 8–18% of listings in our runs. It is an indicative signal, not an appraisal.
- **Incremental mode:** only new listings and **price drops** (`changeType: "PRICE_DROP"`, `previousPriceCOP`) since your last run of the same search. Already-delivered listings are not charged.
- **Administration fee:** `adminFeeCOP` is `null` when the portal does not publish it and `0` only when the portal says it is included; `totalMonthlyCostCOP` is only set for rentals with a known fee. Addresses are delivered as the advertiser wrote them (often partial on Ciencuadras) and are intentionally absent for private listings. The same property can appear twice within one portal when two agencies list it (real, separate listings). The below-market signal is not set when the area is implausible for the type (e.g. apartments outside 20–600 m²).
- **Privacy:** contact data is off by default and, when enabled, returned **only for agencies and builders**. Coordinates of private listings are rounded to about 110 m and their street address is not delivered.
- **Price:** $2.50 per 1,000 listings, plus Apify's standard small fee per run start.
- **Limits:** up to 500 listings per run; Ciencuadras about 20 per search; FincaRaíz about 400 per run; Metrocuadrado up to about 10,000 per search.

**Feedback:** if the Actor saves you time, a short review helps other buyers find it. Something not working? Open an issue and we will answer within 48 hours.

**Legal notice:** this Actor extracts publicly available information visible to any anonymous visitor. It does not log in, bypass paywalls or access private areas. Personal data of private individuals is excluded; optional contact fields are off by default and, even when enabled, only returned for real-estate agencies and builders. You are responsible for complying with applicable laws (including GDPR and Colombia's Law 1581 of 2012) and the source websites' terms.

# Actor input Schema

## `city` (type: `string`):

Colombian city. The list contains the cities verified on all three portals; only listings that the portal itself places in this city are returned.

## `operationType` (type: `string`):

Rental (arriendo) or Sale (venta).

## `propertyType` (type: `string`):

Type of real estate property.

## `portals` (type: `array`):

Select which portals to aggregate (FINCARAIZ, METROCUADRADO, CIENCUADRAS).

## `deduplicateAcrossPortals` (type: `boolean`):

Identify identical properties listed across multiple portals and merge them with arbitrage insights.

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

Maximum total properties to scrape and output.

## `includeContactInfo` (type: `boolean`):

Off by default. When enabled, contact data (company name, phone, WhatsApp, as published by the portal) is returned only for listings published by real-estate agencies or builders. Listings by private individuals never include contact data. Every listing always carries advertiserType (AGENCY, BUILDER, INDIVIDUAL or UNKNOWN). Advertiser contacts may be personal data under Colombia's Law 1581 of 2012: enable it only if you have a lawful basis to process it. You are responsible for how you use it.

## `incrementalMode` (type: `boolean`):

When enabled, properties delivered by a previous run of the same search are skipped: not returned and not charged, unless their price dropped. Price drops are delivered (and charged) again with changeType PRICE_DROP and previousPriceCOP; new properties carry changeType NEW. The history is kept in a named key-value store in your Apify account. The search is identified by City, Operation Type and Property Type; changing Max Items or the proxy keeps the history. Two runs of the same search at the same time may re-deliver a few items on the next run.

## `stateKey` (type: `string`):

Optional name for the incremental history (letters, digits and ! - \_ . ' ( ) only). Leave empty to derive it from the search. Set it to keep one history across searches.

## `resetIncrementalState` (type: `boolean`):

Deletes the incremental history of this search before running, so every item is delivered as new.

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

Apify Proxy configuration. By default uses standard Datacenter proxies to safeguard PPE margins.

## Actor input object example

```json
{
  "city": "bogota",
  "operationType": "RENT",
  "propertyType": "APARTMENT",
  "portals": [
    "FINCARAIZ",
    "METROCUADRADO",
    "CIENCUADRAS"
  ],
  "deduplicateAcrossPortals": true,
  "maxItems": 40,
  "includeContactInfo": false,
  "incrementalMode": false,
  "resetIncrementalState": false
}
```

# Actor output Schema

## `properties` (type: `string`):

No description

## `outputStats` (type: `string`):

No description

## `runReport` (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 = {
    "city": "bogota",
    "maxItems": 40
};

// Run the Actor and wait for it to finish
const run = await client.actor("radar-scraper/colombia-real-estate-aggregator").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 = {
    "city": "bogota",
    "maxItems": 40,
}

# Run the Actor and wait for it to finish
run = client.actor("radar-scraper/colombia-real-estate-aggregator").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 '{
  "city": "bogota",
  "maxItems": 40
}' |
apify call radar-scraper/colombia-real-estate-aggregator --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,radar-scraper/colombia-real-estate-aggregator"
        }
    }
}
```

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/CODO0KpZO47sl8VyD/builds/yZgXERBQpngHt2nw7/openapi.json
