# Brazil Real Estate Multi-Portal Scraper (Delta Mode) (`don.eich/brazil-realestate-multiportal`) Actor

Extract Brazil property listings from OLX Imóveis and QuintoAndar with price/m², GPS coordinates, and incremental delta monitoring to track new listings and price changes.

- **URL**: https://apify.com/don.eich/brazil-realestate-multiportal.md
- **Developed by:** [Erick Bonilla](https://apify.com/don.eich) (community)
- **Categories:** E-commerce, Real estate, Automation
- **Stats:** 2 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $3.00 / 1,000 listing overviews

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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

## Brazil Real Estate Multi-Portal Scraper — OLX + QuintoAndar — Delta Mode

**95%+ success rate · Maintained · Delta mode (NEW/UPDATED/REMOVED) · GPS coordinates**

Extrae propiedades de **OLX Imóveis** y **QuintoAndar** con precio/m² calculado, coordenadas
GPS, y modo delta incremental para detectar nuevas propiedades y cambios de precio entre runs.

***

## Brazil Real Estate Multi-Portal Scraper — OLX + QuintoAndar — Delta Mode

Scrape property listings from **OLX Imóveis** and **QuintoAndar** — Brazil's top real estate portals —
with price-per-m² calculated, GPS coordinates (exact from QuintoAndar, postal-code centroid from OLX),
and incremental delta monitoring to track new listings, price changes and removed properties across runs.

***

### Portais suportados / Supported Portals

| Portal | Motor | GPS | Contato vendedor | Volume |
|---|---|---|---|---|
| **OLX Imóveis** | CheerioCrawler (HTML SSR) | Via Nominatim (CEP) | Nome + UUID público | ~2M anúncios BR |
| **QuintoAndar** | CheerioCrawler (\_\_NEXT\_DATA\_\_) | Nativo (lat/lon exatos) | Marketplace direto | ~60k anúncios SP |
| ImovelWeb | — | — | — | v1.1 (roadmap) |

***

### Modos de operação / Operation Modes

#### FULL (default)

Extrae todos os listings até `maxListings`. O snapshot é salvo no KV Store ao final de cada
run FULL para que o próximo run possa ser DELTA.

#### DELTA — o diferencial

Compara o inventário atual com o run anterior e emite apenas:

- `NEW` — listings que não existiam no snapshot anterior
- `UPDATED` — listings com mudança de preço desde o último run
- `REMOVED` — listings presentes antes e ausentes agora

O estado persiste em um Named Key-Value Store entre runs. Cinco READMEs de competidores prometem
modo delta — nenhum o implementa. Este actor implementa.

**Casos de uso do modo DELTA:**

- Alertas de queda de preço em imóveis monitorados
- Feed de imóveis novos para CRM via webhook do Apify
- Tracking semanal de inventário por bairro e tipo
- Monitoramento de concorrência: quais imobiliárias publicam mais anúncios novos

***

### Campos do output / Output Fields

#### Sempre disponíveis (overview card)

| Campo | Tipo | Descrição |
|---|---|---|
| `id` | string | ID único no portal de origem |
| `portalId` | string | `"olx"` ou `"quintoandar"` |
| `url` | string | URL canônica do anúncio |
| `runId` | string | Actor Run ID (para séries temporais) |
| `snapshotDate` | string | ISO 8601 timestamp da extração |
| `transactionType` | string | `sale` / `rent` / `vacation_rental` |
| `propertyType` | string | `apartment` / `house` / `land` / `commercial` / `other` |
| `propertyTypeRaw` | string | Valor bruto do portal |
| `priceAmount` | number | Preço em BRL |
| `priceCurrency` | string | `"BRL"` |
| `pricePerM2` | number | BRL/m² (calculado) |
| `areaM2` | number | Área útil em m² |
| `bedrooms` | integer | Quartos |
| `parkingSpots` | integer | Vagas de garagem |
| `neighbourhood` | string | Bairro |
| `city` | string | Cidade |
| `state` | string | UF (2 letras) |
| `postalCode` | string | CEP sem traço (só OLX) |
| `latitude` | number | Lat WGS84 |
| `longitude` | number | Lon WGS84 |
| `gpsAccuracy` | string | `"exact"` (QA) / `"postal_code_centroid"` (OLX) |
| `isPrimaryMarket` | boolean | true se lançamento/obra nova |
| `sellerName` | string | Nome do anunciante (só OLX) |
| `photos` | array | URLs das fotos em resolução original |
| `deltaStatus` | string | `NEW` / `UPDATED` / `REMOVED` / `null` |

#### Com `includeDetails: true`

| Campo | Descrição |
|---|---|
| `bathrooms` | Banheiros (disponível no detalhe) |
| `suites` | Suítes (só QuintoAndar) |
| `condoFee` | Condomínio mensal em BRL |
| `iptuAnnual` | IPTU anual em BRL |
| `rentMonthlyTotal` | Custo mensal total em aluguel |
| `hasFurniture` | Mobiliado (só QuintoAndar) |
| `acceptsPets` | Aceita pets (só QuintoAndar) |
| `doorman` | Tipo de portaria (só QuintoAndar) |
| `constructionYear` | Ano de construção (só QuintoAndar) |
| `floor` | Andar da unidade (só QuintoAndar) |
| `propertyFeatures` | Amenidades internas do imóvel |
| `condoFeatures` | Amenidades do condomínio |
| `sellerPublicId` | UUID público do anunciante (só OLX) |
| `isProfessionalAd` | true se anunciante profissional (só OLX) |
| `publicationDate` | Data de primeira publicação |

#### Com `includeDescription: true`

| Campo | Descrição |
|---|---|
| `description` | Descrição textual completa do imóvel |

#### Só em modo DELTA

| Campo | Descrição |
|---|---|
| `deltaStatus` | `NEW` / `UPDATED` / `REMOVED` |
| `previousPrice` | Preço anterior (só em UPDATED) |
| `isDeltaSummary` | true no primeiro record (resumo de counts) |

***

### Preços (Pay per Event)

| Escenário | Custo por 1K listings |
|---|---|
| Overview only (`includeDetails: false`) | $3.00 |
| Overview + detalhe (`includeDetails: true`) | $4.00 |
| vs. incumbente (viralanalyzer) | $5.00 com 51.5% de taxa de sucesso |

***

### Nota sobre campos mascarados

- **Telefone do vendedor (OLX):** aparece somente na UI autenticada. O actor emite `null` em v1.0.
  Não se fabricam dados parciais.
- **IPTU (OLX):** quando mascarado no HTML, emite `null` com nota no log.
- **CEP (QuintoAndar):** não é exposto pelo portal. `postalCode` é sempre `null` para QA.
- **`adDate` (OLX):** o campo retorna epoch interno (ex: `"1970-01-21 12:24:32"`) — não é uma data
  absoluta confiável. `publicationDate` é `null` para OLX em v1.0 até confirmar campo alternativo.

***

### ImovelWeb — v1.1 Roadmap

ImovelWeb é o portal de maior volume do Brasil mas requer bypass de Cloudflare Turnstile + DataDome
com Scrapfly/Zyte + proxy residencial BR. Está fora de v1.0 por decisão deliberada: publicar rápido
e agregar o portal de maior volume em iteração curta.

***

### Configuração de proxy

Para producción, usar Apify Proxy com grupo RESIDENTIAL e país BR:

```json
{
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": ["RESIDENTIAL"],
    "apifyProxyCountry": "BR"
  }
}
```

OLX não requer proxy para volumes baixos (<500 requests/run).
QuintoAndar é mais tolerante — pode rodar sem proxy em testes.

***

### Modo delta — Primeiro run

O primeiro run DELTA não tem snapshot anterior.
Todos os listings saem como `NEW` e o snapshot é salvo como linha de base.
O run seguinte já detecta mudanças.

***

### ViaCEP + Nominatim — GPS para OLX

OLX não expõe coordenadas GPS. O actor usa:

1. **ViaCEP** para validar o CEP (`03044000` → valido)
2. **Nominatim** (OpenStreetMap, gratuito, sem auth) para geocodificar o CEP → centroide
3. Cache em memória por CEP para evitar re-fetch no mesmo run

Precisão: centroide do CEP (~200-500m do endereço real). Documentado no campo `gpsAccuracy`.

Para runs com >500 listings OLX, Nominatim (rate limit: 1 req/seg) pode ser o bottleneck.
Opção: desativar geocoding via input (`geocodeOlxCep: false` — v1.1).

# Actor input Schema

## `sources` (type: `array`):

Portales a scrapear. ImovelWeb disponible en v1.1.

## `state` (type: `string`):

Código de estado de 2 letras. Requerido para OLX. QuintoAndar usa city.

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

Slug de ciudad para QuintoAndar y filtro de ciudad en OLX. Ejemplos: 'sao-paulo', 'rio-de-janeiro', 'belo-horizonte'.

## `transactionType` (type: `string`):

Venta (sale) o alquiler (rent).

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

Filtra por categoría. 'all' extrae todas las categorías disponibles.

## `priceMin` (type: `integer`):

Precio mínimo en Reais. Opcional.

## `priceMax` (type: `integer`):

Precio máximo en Reais. Opcional.

## `bedroomsMin` (type: `integer`):

Número mínimo de dormitorios (quartos). 0 = sin filtro.

## `bathroomsMin` (type: `integer`):

Número mínimo de baños (banheiros). 0 = sin filtro. Solo aplica en portales que soportan este filtro.

## `areaMin` (type: `integer`):

Área mínima en metros cuadrados. Opcional.

## `maxListings` (type: `integer`):

Límite total por run. OLX: 50 por página. QuintoAndar: 12 por página.

## `publicationDays` (type: `integer`):

0 = sin filtro. Filtra listings por fecha de publicación.

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

Si true, fetcha la página de detalle de cada listing para campos adicionales (GPS, baños, descripción, amenidades). Más lento pero más rico.

## `includeDescription` (type: `boolean`):

Si true, incluye el campo 'description' completo. Solo efectivo si includeDetails es true.

## `mode` (type: `string`):

FULL: extrae todos los listings hasta maxListings. DELTA: compara con el run anterior y emite solo NEW, UPDATED y REMOVED.

## `deltaStoreName` (type: `string`):

Named KV Store donde se persiste el estado del run anterior. Mismo nombre en runs sucesivos garantiza continuidad.

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

Usa Apify Proxy (RESIDENTIAL, country=BR) para producción. No requerido para tests ligeros.

## `requestHandlerTimeoutSecs` (type: `integer`):

Tiempo máximo de espera por cada request HTTP antes de reintentar. Aumentar si hay timeouts en conexiones lentas.

## `maxRequestRetries` (type: `integer`):

Número máximo de reintentos por request fallido. Con proxy residencial, 3 reintentos es suficiente.

## Actor input object example

```json
{
  "sources": [
    "olx",
    "quintoandar"
  ],
  "state": "sp",
  "city": "sao-paulo",
  "transactionType": "sale",
  "propertyType": "all",
  "maxListings": 100,
  "includeDetails": true,
  "includeDescription": false,
  "mode": "FULL",
  "deltaStoreName": "brazil-re-delta",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "BR"
  },
  "requestHandlerTimeoutSecs": 60,
  "maxRequestRetries": 3
}
```

# 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 = {
    "sources": [
        "olx",
        "quintoandar"
    ],
    "maxListings": 100
};

// Run the Actor and wait for it to finish
const run = await client.actor("don.eich/brazil-realestate-multiportal").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 = {
    "sources": [
        "olx",
        "quintoandar",
    ],
    "maxListings": 100,
}

# Run the Actor and wait for it to finish
run = client.actor("don.eich/brazil-realestate-multiportal").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "sources": [
    "olx",
    "quintoandar"
  ],
  "maxListings": 100
}' |
apify call don.eich/brazil-realestate-multiportal --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=don.eich/brazil-realestate-multiportal",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/OSFaIxtKyJTRUrjph/builds/zUWr5k8YaLzvZn6MU/openapi.json
